root/src/vfs/ftpfs/ftpfs.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. ftpfs_set_blksize
  2. ftpfs_default_stat
  3. ftpfs_translate_path
  4. ftpfs_correct_url_parameters
  5. ftpfs_get_reply
  6. ftpfs_reconnect
  7. G_GNUC_PRINTF
  8. ftpfs_new_archive
  9. ftpfs_free_archive
  10. ftpfs_changetype
  11. ftpfs_login_server
  12. ftpfs_load_no_proxy_list
  13. ftpfs_check_proxy
  14. ftpfs_get_proxy_host_and_port
  15. ftpfs_open_socket
  16. ftpfs_open_archive_int
  17. ftpfs_open_archive
  18. ftpfs_archive_same
  19. ftpfs_get_current_directory
  20. ftpfs_setup_passive_pasv
  21. ftpfs_setup_passive_epsv
  22. ftpfs_setup_passive
  23. ftpfs_setup_active
  24. ftpfs_init_data_socket
  25. ftpfs_initconn
  26. ftpfs_open_data_connection
  27. ftpfs_linear_abort
  28. resolve_symlink_without_ls_options
  29. resolve_symlink_with_ls_options
  30. resolve_symlink
  31. ftpfs_dir_load
  32. ftpfs_file_store
  33. ftpfs_linear_start
  34. ftpfs_linear_read
  35. ftpfs_linear_close
  36. ftpfs_ctl
  37. ftpfs_send_command
  38. ftpfs_stat
  39. ftpfs_lstat
  40. ftpfs_fstat
  41. ftpfs_chmod
  42. ftpfs_chown
  43. ftpfs_unlink
  44. ftpfs_is_same_dir
  45. ftpfs_chdir_internal
  46. ftpfs_rename
  47. ftpfs_mkdir
  48. ftpfs_rmdir
  49. ftpfs_fh_new
  50. ftpfs_fh_open
  51. ftpfs_fh_close
  52. ftpfs_done
  53. ftpfs_fill_names
  54. ftpfs_netrc_next
  55. ftpfs_netrc_bad_mode
  56. ftpfs_find_machine
  57. ftpfs_netrc_lookup
  58. ftpfs_init_passwd
  59. vfs_init_ftpfs

   1 /*
   2    Virtual File System: FTP file system.
   3 
   4    Copyright (C) 1995-2024
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Ching Hui, 1995
   9    Jakub Jelinek, 1995
  10    Miguel de Icaza, 1995, 1996, 1997
  11    Norbert Warmuth, 1997
  12    Pavel Machek, 1998
  13    Yury V. Zaytsev, 2010
  14    Slava Zanko <slavazanko@gmail.com>, 2010, 2013
  15    Andrew Borodin <aborodin@vmail.ru>, 2010-2022
  16 
  17    This file is part of the Midnight Commander.
  18 
  19    The Midnight Commander is free software: you can redistribute it
  20    and/or modify it under the terms of the GNU General Public License as
  21    published by the Free Software Foundation, either version 3 of the License,
  22    or (at your option) any later version.
  23 
  24    The Midnight Commander is distributed in the hope that it will be useful,
  25    but WITHOUT ANY WARRANTY; without even the implied warranty of
  26    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  27    GNU General Public License for more details.
  28 
  29    You should have received a copy of the GNU General Public License
  30    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  31  */
  32 
  33 /**
  34  * \file
  35  * \brief Source: Virtual File System: FTP file system
  36  * \author Ching Hui
  37  * \author Jakub Jelinek
  38  * \author Miguel de Icaza
  39  * \author Norbert Warmuth
  40  * \author Pavel Machek
  41  * \date 1995, 1997, 1998
  42  *
  43  * \todo
  44 - make it more robust - all the connects etc. should handle EADDRINUSE and
  45   ERETRY (have I spelled these names correctly?)
  46 - make the user able to flush a connection - all the caches will get empty
  47   etc., (tarfs as well), we should give there a user selectable timeout
  48   and assign a key sequence.
  49 - use hash table instead of linklist to cache ftpfs directory.
  50 
  51 What to do with this?
  52 
  53 
  54      * NOTE: Usage of tildes is deprecated, consider:
  55      * \verbatim
  56          cd ftp//:pavel@hobit
  57          cd ~
  58        \endverbatim
  59      * And now: what do I want to do? Do I want to go to /home/pavel or to
  60      * ftp://hobit/home/pavel? I think first has better sense...
  61      *
  62     \verbatim
  63     {
  64         int f = !strcmp( remote_path, "/~" );
  65         if (f || !strncmp( remote_path, "/~/", 3 )) {
  66             char *s;
  67             s = mc_build_filename ( qhome (*bucket), remote_path +3-f, (char *) NULL );
  68             g_free (remote_path);
  69             remote_path = s;
  70         }
  71     }
  72     \endverbatim
  73  */
  74 
  75 /* \todo Fix: Namespace pollution: horrible */
  76 
  77 #include <config.h>
  78 #include <stdio.h>              /* sscanf() */
  79 #include <stdlib.h>             /* atoi() */
  80 #include <sys/types.h>          /* POSIX-required by sys/socket.h and netdb.h */
  81 #include <netdb.h>              /* struct hostent */
  82 #include <sys/socket.h>         /* AF_INET */
  83 #include <netinet/in.h>         /* struct in_addr */
  84 #ifdef HAVE_ARPA_INET_H
  85 #include <arpa/inet.h>
  86 #endif
  87 #include <arpa/ftp.h>
  88 #include <arpa/telnet.h>
  89 #ifdef HAVE_SYS_PARAM_H
  90 #include <sys/param.h>
  91 #endif
  92 #include <errno.h>
  93 #include <ctype.h>
  94 #include <fcntl.h>
  95 #include <inttypes.h>           /* uintmax_t */
  96 
  97 #include "lib/global.h"
  98 #include "lib/file-entry.h"
  99 #include "lib/util.h"
 100 #include "lib/strutil.h"        /* str_move() */
 101 #include "lib/mcconfig.h"
 102 
 103 #include "lib/tty/tty.h"        /* enable/disable interrupt key */
 104 #include "lib/widget.h"         /* message() */
 105 
 106 #include "src/history.h"
 107 #include "src/setup.h"          /* for load_anon_passwd */
 108 
 109 #include "lib/vfs/vfs.h"
 110 #include "lib/vfs/utilvfs.h"
 111 #include "lib/vfs/netutil.h"
 112 #include "lib/vfs/xdirentry.h"
 113 #include "lib/vfs/gc.h"         /* vfs_stamp_create */
 114 
 115 #include "ftpfs.h"
 116 
 117 /*** global variables ****************************************************************************/
 118 
 119 /* Delay to retry a connection */
 120 int ftpfs_retry_seconds = 30;
 121 
 122 /* Method to use to connect to ftp sites */
 123 gboolean ftpfs_use_passive_connections = TRUE;
 124 gboolean ftpfs_use_passive_connections_over_proxy = FALSE;
 125 
 126 /* Method used to get directory listings:
 127  * 1: try 'LIST -la <path>', if it fails
 128  *    fall back to CWD <path>; LIST
 129  * 0: always use CWD <path>; LIST
 130  */
 131 gboolean ftpfs_use_unix_list_options = TRUE;
 132 
 133 /* First "CWD <path>", then "LIST -la ." */
 134 gboolean ftpfs_first_cd_then_ls = TRUE;
 135 
 136 /* Use the ~/.netrc */
 137 gboolean ftpfs_use_netrc = TRUE;
 138 
 139 /* Anonymous setup */
 140 char *ftpfs_anonymous_passwd = NULL;
 141 int ftpfs_directory_timeout = 900;
 142 
 143 /* Proxy host */
 144 char *ftpfs_proxy_host = NULL;
 145 
 146 /* whether we have to use proxy by default? */
 147 gboolean ftpfs_always_use_proxy = FALSE;
 148 
 149 gboolean ftpfs_ignore_chattr_errors = TRUE;
 150 
 151 /*** file scope macro definitions ****************************************************************/
 152 
 153 #ifndef MAXHOSTNAMELEN
 154 #define MAXHOSTNAMELEN 64
 155 #endif
 156 
 157 #define FTP_SUPER(super) ((ftp_super_t *) (super))
 158 #define FTP_FILE_HANDLER(fh) ((ftp_file_handler_t *) (fh))
 159 #define FH_SOCK FTP_FILE_HANDLER(fh)->sock
 160 
 161 #ifndef INADDR_NONE
 162 #define INADDR_NONE 0xffffffff
 163 #endif
 164 
 165 #define RFC_AUTODETECT 0
 166 #define RFC_DARING 1
 167 #define RFC_STRICT 2
 168 
 169 /* ftpfs_command wait_flag: */
 170 #define NONE        0x00
 171 #define WAIT_REPLY  0x01
 172 #define WANT_STRING 0x02
 173 
 174 #define FTP_COMMAND_PORT   21
 175 
 176 /* some defines only used by ftpfs_changetype */
 177 /* These two are valid values for the second parameter */
 178 #define TYPE_ASCII    0
 179 #define TYPE_BINARY   1
 180 
 181 /* This one is only used to initialize bucket->isbinary, don't use it as
 182    second parameter to ftpfs_changetype. */
 183 #define TYPE_UNKNOWN -1
 184 
 185 #define ABORT_TIMEOUT (5 * G_USEC_PER_SEC)
 186 /*** file scope type declarations ****************************************************************/
 187 
 188 #ifndef HAVE_SOCKLEN_T
 189 typedef int socklen_t;
 190 #endif
 191 
 192 /* This should match the keywords[] array below */
 193 typedef enum
 194 {
 195     NETRC_NONE = 0,
 196     NETRC_DEFAULT,
 197     NETRC_MACHINE,
 198     NETRC_LOGIN,
 199     NETRC_PASSWORD,
 200     NETRC_PASSWD,
 201     NETRC_ACCOUNT,
 202     NETRC_MACDEF,
 203     NETRC_UNKNOWN
 204 } keyword_t;
 205 
 206 typedef struct
 207 {
 208     struct vfs_s_super base;    /* base class */
 209 
 210     int sock;
 211 
 212     char *proxy;                /* proxy server, NULL if no proxy */
 213     gboolean failed_on_login;   /* used to pass the failure reason to upper levels */
 214     gboolean use_passive_connection;
 215     gboolean remote_is_amiga;   /* No leading slash allowed for AmiTCP (Amiga) */
 216     int isbinary;
 217     gboolean cwd_deferred;      /* current_directory was changed but CWD command hasn't
 218                                    been sent yet */
 219     int strict;                 /* ftp server doesn't understand
 220                                  * "LIST -la <path>"; use "CWD <path>"/
 221                                  * "LIST" instead
 222                                  */
 223     gboolean ctl_connection_busy;
 224     char *current_dir;
 225 } ftp_super_t;
 226 
 227 typedef struct
 228 {
 229     vfs_file_handler_t base;    /* base class */
 230 
 231     int sock;
 232     gboolean append;
 233 } ftp_file_handler_t;
 234 
 235 /*** forward declarations (file scope functions) *************************************************/
 236 
 237 static char *ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super);
 238 static int ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super,
 239                                  const char *remote_path);
 240 static int ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super);
 241 static gboolean ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super,
 242                                     const char *netrcpass);
 243 static gboolean ftpfs_netrc_lookup (const char *host, char **login, char **pass);
 244 
 245 /*** file scope variables ************************************************************************/
 246 
 247 static int code;
 248 
 249 static char reply_str[80];
 250 
 251 static struct vfs_s_subclass ftpfs_subclass;
 252 static struct vfs_class *vfs_ftpfs_ops = VFS_CLASS (&ftpfs_subclass);
 253 
 254 static GSList *no_proxy = NULL;
 255 
 256 static char buffer[BUF_MEDIUM];
 257 static char *netrc = NULL;
 258 static const char *netrcp;
 259 
 260 /* --------------------------------------------------------------------------------------------- */
 261 /*** file scope functions ************************************************************************/
 262 /* --------------------------------------------------------------------------------------------- */
 263 
 264 static void
 265 ftpfs_set_blksize (struct stat *s)
     /* [previous][next][first][last][top][bottom][index][help]  */
 266 {
 267 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
 268     /* redefine block size */
 269     s->st_blksize = 64 * 1024;  /* FIXME */
 270 #endif
 271 }
 272 
 273 /* --------------------------------------------------------------------------------------------- */
 274 
 275 static struct stat *
 276 ftpfs_default_stat (struct vfs_class *me)
     /* [previous][next][first][last][top][bottom][index][help]  */
 277 {
 278     struct stat *s;
 279 
 280     s = vfs_s_default_stat (me, S_IFDIR | 0755);
 281     ftpfs_set_blksize (s);
 282     vfs_adjust_stat (s);
 283 
 284     return s;
 285 }
 286 
 287 /* --------------------------------------------------------------------------------------------- */
 288 
 289 /* Translate a Unix path, i.e. MC's internal path representation (e.g.
 290    /somedir/somefile) to a path valid for the remote server. Every path
 291    transferred to the remote server has to be mangled by this function
 292    right prior to sending it.
 293    Currently only Amiga ftp servers are handled in a special manner.
 294 
 295    When the remote server is an amiga:
 296    a) strip leading slash if necessary
 297    b) replace first occurrence of ":/" with ":"
 298    c) strip trailing "/."
 299  */
 300 static char *
 301 ftpfs_translate_path (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
     /* [previous][next][first][last][top][bottom][index][help]  */
 302 {
 303     char *ret, *p;
 304 
 305     if (!FTP_SUPER (super)->remote_is_amiga)
 306         return g_strdup (remote_path);
 307 
 308     if (me->logfile != NULL)
 309     {
 310         fprintf (me->logfile, "MC -- ftpfs_translate_path: %s\n", remote_path);
 311         fflush (me->logfile);
 312     }
 313 
 314     /* strip leading slash(es) */
 315     while (IS_PATH_SEP (*remote_path))
 316         remote_path++;
 317 
 318     /* Don't change "/" into "", e.g. "CWD " would be invalid. */
 319     if (*remote_path == '\0')
 320         return g_strdup (".");
 321 
 322     ret = g_strdup (remote_path);
 323 
 324     /* replace first occurrence of ":/" with ":" */
 325     p = strchr (ret, ':');
 326     if (p != NULL && IS_PATH_SEP (p[1]))
 327         str_move (p + 1, p + 2);
 328 
 329     /* strip trailing "/." */
 330     p = strrchr (ret, PATH_SEP);
 331     if ((p != NULL) && (*(p + 1) == '.') && (*(p + 2) == '\0'))
 332         *p = '\0';
 333 
 334     return ret;
 335 }
 336 
 337 /* --------------------------------------------------------------------------------------------- */
 338 /** Extract the hostname and username from the path */
 339 /*
 340  * path is in the form: [user@]hostname:port/remote-dir, e.g.:
 341  * ftp://sunsite.unc.edu/pub/linux
 342  * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
 343  * ftp://tsx-11.mit.edu:8192/
 344  * ftp://joe@foo.edu:11321/private
 345  * If the user is empty, e.g. ftp://@roxanne/private, then your login name
 346  * is supplied.
 347  */
 348 
 349 static vfs_path_element_t *
 350 ftpfs_correct_url_parameters (const vfs_path_element_t *velement)
     /* [previous][next][first][last][top][bottom][index][help]  */
 351 {
 352     vfs_path_element_t *path_element = vfs_path_element_clone (velement);
 353 
 354     if (path_element->port == 0)
 355         path_element->port = FTP_COMMAND_PORT;
 356 
 357     if (path_element->user == NULL)
 358     {
 359         /* Look up user and password in netrc */
 360         if (ftpfs_use_netrc)
 361             ftpfs_netrc_lookup (path_element->host, &path_element->user, &path_element->password);
 362     }
 363     if (path_element->user == NULL)
 364         path_element->user = g_strdup ("anonymous");
 365 
 366     /* Look up password in netrc for known user */
 367     if (ftpfs_use_netrc && path_element->password == NULL)
 368     {
 369         char *new_user = NULL;
 370         char *new_passwd = NULL;
 371 
 372         ftpfs_netrc_lookup (path_element->host, &new_user, &new_passwd);
 373 
 374         /* If user is different, remove password */
 375         if (new_user != NULL && strcmp (path_element->user, new_user) != 0)
 376             MC_PTR_FREE (path_element->password);
 377 
 378         g_free (new_user);
 379         g_free (new_passwd);
 380     }
 381 
 382     return path_element;
 383 }
 384 
 385 /* --------------------------------------------------------------------------------------------- */
 386 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
 387 
 388 static int
 389 ftpfs_get_reply (struct vfs_class *me, int sock, char *string_buf, int string_len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 390 {
 391     while (TRUE)
 392     {
 393         char answer[BUF_1K];
 394 
 395         if (vfs_s_get_line (me, sock, answer, sizeof (answer), '\n') == 0)
 396         {
 397             if (string_buf != NULL)
 398                 *string_buf = '\0';
 399             code = 421;
 400             return 4;
 401         }
 402 
 403         /* cppcheck-suppress invalidscanf */
 404         switch (sscanf (answer, "%d", &code))
 405         {
 406         case 0:
 407             if (string_buf != NULL)
 408                 g_strlcpy (string_buf, answer, string_len);
 409             code = 500;
 410             return 5;
 411         case 1:
 412             if (answer[3] == '-')
 413             {
 414                 while (TRUE)
 415                 {
 416                     int i;
 417 
 418                     if (vfs_s_get_line (me, sock, answer, sizeof (answer), '\n') == 0)
 419                     {
 420                         if (string_buf != NULL)
 421                             *string_buf = '\0';
 422                         code = 421;
 423                         return 4;
 424                     }
 425                     /* cppcheck-suppress invalidscanf */
 426                     if ((sscanf (answer, "%d", &i) > 0) && (code == i) && (answer[3] == ' '))
 427                         break;
 428                 }
 429             }
 430             if (string_buf != NULL)
 431                 g_strlcpy (string_buf, answer, string_len);
 432             return code / 100;
 433         default:
 434             break;
 435         }
 436     }
 437 }
 438 
 439 /* --------------------------------------------------------------------------------------------- */
 440 
 441 static gboolean
 442 ftpfs_reconnect (struct vfs_class *me, struct vfs_s_super *super)
     /* [previous][next][first][last][top][bottom][index][help]  */
 443 {
 444     ftp_super_t *ftp_super = FTP_SUPER (super);
 445     int sock;
 446 
 447     sock = ftpfs_open_socket (me, super);
 448     if (sock != -1)
 449     {
 450         char *cwdir = ftp_super->current_dir;
 451 
 452         close (ftp_super->sock);
 453         ftp_super->sock = sock;
 454         ftp_super->current_dir = NULL;
 455 
 456         if (ftpfs_login_server (me, super, super->path_element->password))
 457         {
 458             if (cwdir == NULL)
 459                 return TRUE;
 460 
 461             sock = ftpfs_chdir_internal (me, super, cwdir);
 462             g_free (cwdir);
 463             return (sock == COMPLETE);
 464         }
 465 
 466         ftp_super->current_dir = cwdir;
 467     }
 468 
 469     return FALSE;
 470 }
 471 
 472 /* --------------------------------------------------------------------------------------------- */
 473 
 474 static int
 475 G_GNUC_PRINTF (4, 5)
     /* [previous][next][first][last][top][bottom][index][help]  */
 476 ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *fmt,
 477                ...)
 478 {
 479     ftp_super_t *ftp_super = FTP_SUPER (super);
 480     va_list ap;
 481     GString *cmdstr;
 482     int status;
 483     static gboolean retry = FALSE;
 484     static int level = 0;       /* ftpfs_login_server() use ftpfs_command() */
 485 
 486     cmdstr = g_string_sized_new (32);
 487     va_start (ap, fmt);
 488     g_string_vprintf (cmdstr, fmt, ap);
 489     va_end (ap);
 490     g_string_append (cmdstr, "\r\n");
 491 
 492     if (me->logfile != NULL)
 493     {
 494         if (strncmp (cmdstr->str, "PASS ", 5) == 0)
 495             fputs ("PASS <Password not logged>\r\n", me->logfile);
 496         else
 497         {
 498             size_t ret;
 499 
 500             ret = fwrite (cmdstr->str, cmdstr->len, 1, me->logfile);
 501             (void) ret;
 502         }
 503 
 504         fflush (me->logfile);
 505     }
 506 
 507     got_sigpipe = 0;
 508     tty_enable_interrupt_key ();
 509     status = write (ftp_super->sock, cmdstr->str, cmdstr->len);
 510 
 511     if (status < 0)
 512     {
 513         code = 421;
 514 
 515         if (errno == EPIPE)
 516         {                       /* Remote server has closed connection */
 517             if (level == 0)
 518             {
 519                 level = 1;
 520                 status = ftpfs_reconnect (me, super) ? 1 : 0;
 521                 level = 0;
 522                 if (status != 0 && (write (ftp_super->sock, cmdstr->str, cmdstr->len) > 0))
 523                     goto ok;
 524 
 525             }
 526             got_sigpipe = 1;
 527         }
 528         g_string_free (cmdstr, TRUE);
 529         tty_disable_interrupt_key ();
 530         return TRANSIENT;
 531     }
 532 
 533     retry = FALSE;
 534 
 535   ok:
 536     tty_disable_interrupt_key ();
 537 
 538     if (wait_reply != NONE)
 539     {
 540         status = ftpfs_get_reply (me, ftp_super->sock,
 541                                   (wait_reply & WANT_STRING) != 0 ? reply_str : NULL,
 542                                   sizeof (reply_str) - 1);
 543         if ((wait_reply & WANT_STRING) != 0 && !retry && level == 0 && code == 421)
 544         {
 545             retry = TRUE;
 546             level = 1;
 547             status = ftpfs_reconnect (me, super) ? 1 : 0;
 548             level = 0;
 549             if (status != 0 && (write (ftp_super->sock, cmdstr->str, cmdstr->len) > 0))
 550                 goto ok;
 551         }
 552         retry = FALSE;
 553         g_string_free (cmdstr, TRUE);
 554         return status;
 555     }
 556 
 557     g_string_free (cmdstr, TRUE);
 558     return COMPLETE;
 559 }
 560 
 561 /* --------------------------------------------------------------------------------------------- */
 562 
 563 static struct vfs_s_super *
 564 ftpfs_new_archive (struct vfs_class *me)
     /* [previous][next][first][last][top][bottom][index][help]  */
 565 {
 566     ftp_super_t *arch;
 567 
 568     arch = g_new0 (ftp_super_t, 1);
 569     arch->base.me = me;
 570     arch->base.name = g_strdup (PATH_SEP_STR);
 571     arch->sock = -1;
 572     arch->use_passive_connection = ftpfs_use_passive_connections;
 573     arch->strict = ftpfs_use_unix_list_options ? RFC_AUTODETECT : RFC_STRICT;
 574     arch->isbinary = TYPE_UNKNOWN;
 575 
 576     return VFS_SUPER (arch);
 577 }
 578 
 579 /* --------------------------------------------------------------------------------------------- */
 580 
 581 static void
 582 ftpfs_free_archive (struct vfs_class *me, struct vfs_s_super *super)
     /* [previous][next][first][last][top][bottom][index][help]  */
 583 {
 584     ftp_super_t *ftp_super = FTP_SUPER (super);
 585 
 586     if (ftp_super->sock != -1)
 587     {
 588         vfs_print_message (_("ftpfs: Disconnecting from %s"), super->path_element->host);
 589         ftpfs_command (me, super, NONE, "%s", "QUIT");
 590         close (ftp_super->sock);
 591     }
 592     g_free (ftp_super->current_dir);
 593 }
 594 
 595 /* --------------------------------------------------------------------------------------------- */
 596 
 597 static int
 598 ftpfs_changetype (struct vfs_class *me, struct vfs_s_super *super, int binary)
     /* [previous][next][first][last][top][bottom][index][help]  */
 599 {
 600     if (binary != FTP_SUPER (super)->isbinary)
 601     {
 602         if (ftpfs_command (me, super, WAIT_REPLY, "TYPE %c", binary ? 'I' : 'A') != COMPLETE)
 603             ERRNOR (EIO, -1);
 604         FTP_SUPER (super)->isbinary = binary;
 605     }
 606     return binary;
 607 }
 608 
 609 /* --------------------------------------------------------------------------------------------- */
 610 /* This routine logs the user in */
 611 
 612 static int
 613 ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super, const char *netrcpass)
     /* [previous][next][first][last][top][bottom][index][help]  */
 614 {
 615     ftp_super_t *ftp_super = FTP_SUPER (super);
 616     char *pass;
 617     char *op;
 618     char *name;                 /* login user name */
 619     gboolean anon = FALSE;
 620     char reply_string[BUF_MEDIUM];
 621 
 622     ftp_super->isbinary = TYPE_UNKNOWN;
 623 
 624     if (super->path_element->password != NULL)  /* explicit password */
 625         op = g_strdup (super->path_element->password);
 626     else if (netrcpass != NULL) /* password from netrc */
 627         op = g_strdup (netrcpass);
 628     else if (strcmp (super->path_element->user, "anonymous") == 0
 629              || strcmp (super->path_element->user, "ftp") == 0)
 630     {
 631         if (ftpfs_anonymous_passwd == NULL)     /* default anonymous password */
 632             ftpfs_init_passwd ();
 633         op = g_strdup (ftpfs_anonymous_passwd);
 634         anon = TRUE;
 635     }
 636     else
 637     {                           /* ask user */
 638         char *p;
 639 
 640         p = g_strdup_printf (_("FTP: Password required for %s"), super->path_element->user);
 641         op = vfs_get_password (p);
 642         g_free (p);
 643         if (op == NULL)
 644             ERRNOR (EPERM, 0);
 645         super->path_element->password = g_strdup (op);
 646     }
 647 
 648     if (!anon || me->logfile != NULL)
 649         pass = op;
 650     else
 651     {
 652         pass = g_strconcat ("-", op, (char *) NULL);
 653         wipe_password (op);
 654     }
 655 
 656     /* Proxy server accepts: username@host-we-want-to-connect */
 657     if (ftp_super->proxy != NULL)
 658         name =
 659             g_strconcat (super->path_element->user, "@",
 660                          super->path_element->host[0] ==
 661                          '!' ? super->path_element->host + 1 : super->path_element->host,
 662                          (char *) NULL);
 663     else
 664         name = g_strdup (super->path_element->user);
 665 
 666     if (ftpfs_get_reply (me, ftp_super->sock, reply_string, sizeof (reply_string) - 1) == COMPLETE)
 667     {
 668         char *reply_up;
 669 
 670         reply_up = g_ascii_strup (reply_string, -1);
 671         ftp_super->remote_is_amiga = strstr (reply_up, "AMIGA") != NULL;
 672         if (strstr (reply_up, " SPFTP/1.0.0000 SERVER ") != NULL)       /* handles `LIST -la` in a weird way */
 673             ftp_super->strict = RFC_STRICT;
 674         g_free (reply_up);
 675 
 676         if (me->logfile != NULL)
 677         {
 678             fprintf (me->logfile, "MC -- remote_is_amiga = %s\n",
 679                      ftp_super->remote_is_amiga ? "yes" : "no");
 680             fflush (me->logfile);
 681         }
 682 
 683         vfs_print_message ("%s", _("ftpfs: sending login name"));
 684 
 685         switch (ftpfs_command (me, super, WAIT_REPLY, "USER %s", name))
 686         {
 687         case CONTINUE:
 688             vfs_print_message ("%s", _("ftpfs: sending user password"));
 689             code = ftpfs_command (me, super, WAIT_REPLY, "PASS %s", pass);
 690             if (code == CONTINUE)
 691             {
 692                 char *p;
 693 
 694                 p = g_strdup_printf (_("FTP: Account required for user %s"),
 695                                      super->path_element->user);
 696                 op = input_dialog (p, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT, "",
 697                                    INPUT_COMPLETE_USERNAMES);
 698                 g_free (p);
 699                 if (op == NULL)
 700                     ERRNOR (EPERM, 0);
 701                 vfs_print_message ("%s", _("ftpfs: sending user account"));
 702                 code = ftpfs_command (me, super, WAIT_REPLY, "ACCT %s", op);
 703                 g_free (op);
 704             }
 705             if (code != COMPLETE)
 706                 break;
 707 
 708             MC_FALLTHROUGH;
 709 
 710         case COMPLETE:
 711             vfs_print_message ("%s", _("ftpfs: logged in"));
 712             wipe_password (pass);
 713             g_free (name);
 714             return TRUE;
 715 
 716         default:
 717             ftp_super->failed_on_login = TRUE;
 718             wipe_password (super->path_element->password);
 719             super->path_element->password = NULL;
 720             goto login_fail;
 721         }
 722     }
 723 
 724     message (D_ERROR, MSG_ERROR, _("ftpfs: Login incorrect for user %s "),
 725              super->path_element->user);
 726 
 727   login_fail:
 728     wipe_password (pass);
 729     g_free (name);
 730     ERRNOR (EPERM, FALSE);
 731 }
 732 
 733 /* --------------------------------------------------------------------------------------------- */
 734 
 735 static void
 736 ftpfs_load_no_proxy_list (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 737 {
 738     /* FixMe: shouldn't be hardcoded!!! */
 739     char *mc_file;
 740 
 741     mc_file = g_build_filename (mc_global.sysconfig_dir, "mc.no_proxy", (char *) NULL);
 742     if (exist_file (mc_file))
 743     {
 744         FILE *npf;
 745 
 746         npf = fopen (mc_file, "r");
 747         if (npf != NULL)
 748         {
 749             char s[BUF_LARGE];  /* provide for BUF_LARGE characters */
 750 
 751             while (fgets (s, sizeof (s), npf) != NULL)
 752             {
 753                 char *p;
 754 
 755                 p = strchr (s, '\n');
 756                 if (p == NULL)  /* skip bogus entries */
 757                 {
 758                     int c;
 759 
 760                     while ((c = fgetc (npf)) != EOF && c != '\n')
 761                         ;
 762                 }
 763                 else if (p != s)
 764                 {
 765                     *p = '\0';
 766                     no_proxy = g_slist_prepend (no_proxy, g_strdup (s));
 767                 }
 768             }
 769 
 770             fclose (npf);
 771         }
 772     }
 773 
 774     g_free (mc_file);
 775 }
 776 
 777 /* --------------------------------------------------------------------------------------------- */
 778 /* Return TRUE if FTP proxy should be used for this host, FALSE otherwise */
 779 
 780 static gboolean
 781 ftpfs_check_proxy (const char *host)
     /* [previous][next][first][last][top][bottom][index][help]  */
 782 {
 783 
 784     if (ftpfs_proxy_host == NULL || *ftpfs_proxy_host == '\0' || host == NULL || *host == '\0')
 785         return FALSE;           /* sanity check */
 786 
 787     if (*host == '!')
 788         return TRUE;
 789 
 790     if (!ftpfs_always_use_proxy)
 791         return FALSE;
 792 
 793     if (strchr (host, '.') == NULL)
 794         return FALSE;
 795 
 796     if (no_proxy == NULL)
 797     {
 798         GSList *npe;
 799 
 800         ftpfs_load_no_proxy_list ();
 801 
 802         for (npe = no_proxy; npe != NULL; npe = g_slist_next (npe))
 803         {
 804             const char *domain = (const char *) npe->data;
 805 
 806             if (domain[0] == '.')
 807             {
 808                 size_t ld, lh;
 809 
 810                 ld = strlen (domain);
 811                 lh = strlen (host);
 812 
 813                 while (ld != 0 && lh != 0 && host[lh - 1] == domain[ld - 1])
 814                 {
 815                     ld--;
 816                     lh--;
 817                 }
 818 
 819                 if (ld == 0)
 820                     return FALSE;
 821             }
 822             else if (g_ascii_strcasecmp (host, domain) == 0)
 823                 return FALSE;
 824         }
 825     }
 826 
 827     return TRUE;
 828 }
 829 
 830 /* --------------------------------------------------------------------------------------------- */
 831 
 832 static void
 833 ftpfs_get_proxy_host_and_port (const char *proxy, char **host, int *port)
     /* [previous][next][first][last][top][bottom][index][help]  */
 834 {
 835     vfs_path_element_t *path_element;
 836 
 837     path_element = vfs_url_split (proxy, FTP_COMMAND_PORT, URL_USE_ANONYMOUS);
 838     *host = path_element->host;
 839     path_element->host = NULL;
 840     *port = path_element->port;
 841     vfs_path_element_free (path_element);
 842 }
 843 
 844 /* --------------------------------------------------------------------------------------------- */
 845 
 846 static int
 847 ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super)
     /* [previous][next][first][last][top][bottom][index][help]  */
 848 {
 849     struct addrinfo hints, *res, *curr_res;
 850     int my_socket = 0;
 851     char *host = NULL;
 852     char port[8];
 853     int tmp_port = 0;
 854     int e;
 855 
 856     (void) me;
 857 
 858     if (super->path_element->host == NULL || *super->path_element->host == '\0')
 859     {
 860         vfs_print_message ("%s", _("ftpfs: Invalid host name."));
 861         me->verrno = EINVAL;
 862         return (-1);
 863     }
 864 
 865     /* Use a proxy host? */
 866     /* Hosts to connect to that start with a ! should use proxy */
 867     if (FTP_SUPER (super)->proxy != NULL)
 868         ftpfs_get_proxy_host_and_port (ftpfs_proxy_host, &host, &tmp_port);
 869     else
 870     {
 871         host = g_strdup (super->path_element->host);
 872         tmp_port = super->path_element->port;
 873     }
 874 
 875     g_snprintf (port, sizeof (port), "%hu", (unsigned short) tmp_port);
 876 
 877     tty_enable_interrupt_key ();        /* clear the interrupt flag */
 878 
 879     memset (&hints, 0, sizeof (hints));
 880     hints.ai_family = AF_UNSPEC;
 881     hints.ai_socktype = SOCK_STREAM;
 882 
 883 #ifdef AI_ADDRCONFIG
 884     /* By default, only look up addresses using address types for
 885      * which a local interface is configured (i.e. no IPv6 if no IPv6
 886      * interfaces, likewise for IPv4 (see RFC 3493 for details). */
 887     hints.ai_flags = AI_ADDRCONFIG;
 888 #endif
 889 
 890     e = getaddrinfo (host, port, &hints, &res);
 891 
 892 #ifdef AI_ADDRCONFIG
 893     if (e == EAI_BADFLAGS)
 894     {
 895         /* Retry with no flags if AI_ADDRCONFIG was rejected. */
 896         hints.ai_flags = 0;
 897         e = getaddrinfo (host, port, &hints, &res);
 898     }
 899 #endif
 900 
 901     *port = '\0';
 902 
 903     if (e != 0)
 904     {
 905         tty_disable_interrupt_key ();
 906         vfs_print_message (_("ftpfs: %s"), gai_strerror (e));
 907         g_free (host);
 908         me->verrno = EINVAL;
 909         return (-1);
 910     }
 911 
 912     for (curr_res = res; curr_res != NULL; curr_res = curr_res->ai_next)
 913     {
 914         my_socket = socket (curr_res->ai_family, curr_res->ai_socktype, curr_res->ai_protocol);
 915 
 916         if (my_socket < 0)
 917         {
 918             if (curr_res->ai_next != NULL)
 919                 continue;
 920 
 921             tty_disable_interrupt_key ();
 922             vfs_print_message (_("ftpfs: %s"), unix_error_string (errno));
 923             g_free (host);
 924             freeaddrinfo (res);
 925             me->verrno = errno;
 926             return (-1);
 927         }
 928 
 929         vfs_print_message (_("ftpfs: making connection to %s"), host);
 930         MC_PTR_FREE (host);
 931 
 932         if (connect (my_socket, curr_res->ai_addr, curr_res->ai_addrlen) >= 0)
 933             break;
 934 
 935         me->verrno = errno;
 936         close (my_socket);
 937 
 938         if (errno == EINTR && tty_got_interrupt ())
 939             vfs_print_message ("%s", _("ftpfs: connection interrupted by user"));
 940         else if (res->ai_next == NULL)
 941             vfs_print_message (_("ftpfs: connection to server failed: %s"),
 942                                unix_error_string (errno));
 943         else
 944             continue;
 945 
 946         freeaddrinfo (res);
 947         tty_disable_interrupt_key ();
 948         return (-1);
 949     }
 950 
 951     freeaddrinfo (res);
 952     tty_disable_interrupt_key ();
 953     return my_socket;
 954 }
 955 
 956 /* --------------------------------------------------------------------------------------------- */
 957 
 958 static int
 959 ftpfs_open_archive_int (struct vfs_class *me, struct vfs_s_super *super)
     /* [previous][next][first][last][top][bottom][index][help]  */
 960 {
 961     ftp_super_t *ftp_super = FTP_SUPER (super);
 962     int retry_seconds = 0;
 963 
 964     /* We do not want to use the passive if we are using proxies */
 965     if (ftp_super->proxy != NULL)
 966         ftp_super->use_passive_connection = ftpfs_use_passive_connections_over_proxy;
 967 
 968     do
 969     {
 970         ftp_super->failed_on_login = FALSE;
 971 
 972         ftp_super->sock = ftpfs_open_socket (me, super);
 973         if (ftp_super->sock == -1)
 974             return (-1);
 975 
 976         if (ftpfs_login_server (me, super, NULL))
 977         {
 978             /* Logged in, no need to retry the connection */
 979             break;
 980         }
 981 
 982         if (!ftp_super->failed_on_login)
 983             return (-1);
 984 
 985         /* Close only the socket descriptor */
 986         close (ftp_super->sock);
 987 
 988         if (ftpfs_retry_seconds != 0)
 989         {
 990             int count_down;
 991 
 992             retry_seconds = ftpfs_retry_seconds;
 993             tty_enable_interrupt_key ();
 994             for (count_down = retry_seconds; count_down != 0; count_down--)
 995             {
 996                 vfs_print_message (_("Waiting to retry... %d (Control-G to cancel)"), count_down);
 997                 sleep (1);
 998                 if (tty_got_interrupt ())
 999                 {
1000                     /* me->verrno = E; */
1001                     tty_disable_interrupt_key ();
1002                     return 0;
1003                 }
1004             }
1005             tty_disable_interrupt_key ();
1006         }
1007     }
1008     while (retry_seconds != 0);
1009 
1010     ftp_super->current_dir = ftpfs_get_current_directory (me, super);
1011     if (ftp_super->current_dir == NULL)
1012         ftp_super->current_dir = g_strdup (PATH_SEP_STR);
1013 
1014     return 0;
1015 }
1016 
1017 /* --------------------------------------------------------------------------------------------- */
1018 
1019 static int
1020 ftpfs_open_archive (struct vfs_s_super *super,
     /* [previous][next][first][last][top][bottom][index][help]  */
1021                     const vfs_path_t *vpath, const vfs_path_element_t *vpath_element)
1022 {
1023     (void) vpath;
1024 
1025     super->path_element = ftpfs_correct_url_parameters (vpath_element);
1026     if (ftpfs_check_proxy (super->path_element->host))
1027         FTP_SUPER (super)->proxy = ftpfs_proxy_host;
1028     super->root =
1029         vfs_s_new_inode (vpath_element->class, super, ftpfs_default_stat (vpath_element->class));
1030 
1031     return ftpfs_open_archive_int (vpath_element->class, super);
1032 }
1033 
1034 /* --------------------------------------------------------------------------------------------- */
1035 
1036 static int
1037 ftpfs_archive_same (const vfs_path_element_t *vpath_element, struct vfs_s_super *super,
     /* [previous][next][first][last][top][bottom][index][help]  */
1038                     const vfs_path_t *vpath, void *cookie)
1039 {
1040     vfs_path_element_t *path_element;
1041     int result;
1042 
1043     (void) vpath;
1044     (void) cookie;
1045 
1046     path_element = ftpfs_correct_url_parameters (vpath_element);
1047 
1048     result = ((strcmp (path_element->host, super->path_element->host) == 0)
1049               && (strcmp (path_element->user, super->path_element->user) == 0)
1050               && (path_element->port == super->path_element->port)) ? 1 : 0;
1051 
1052     vfs_path_element_free (path_element);
1053     return result;
1054 }
1055 
1056 /* --------------------------------------------------------------------------------------------- */
1057 /* The returned directory should always contain a trailing slash */
1058 
1059 static char *
1060 ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super)
     /* [previous][next][first][last][top][bottom][index][help]  */
1061 {
1062     char buf[MC_MAXPATHLEN + 1];
1063 
1064     if (ftpfs_command (me, super, NONE, "%s", "PWD") == COMPLETE &&
1065         ftpfs_get_reply (me, FTP_SUPER (super)->sock, buf, sizeof (buf)) == COMPLETE)
1066     {
1067         char *bufp = NULL;
1068         char *bufq;
1069 
1070         for (bufq = buf; *bufq != '\0'; bufq++)
1071             if (*bufq == '"')
1072             {
1073                 if (bufp == NULL)
1074                     bufp = bufq + 1;
1075                 else
1076                 {
1077                     *bufq = '\0';
1078 
1079                     if (*bufp != '\0')
1080                     {
1081                         if (!IS_PATH_SEP (bufq[-1]))
1082                         {
1083                             *bufq++ = PATH_SEP;
1084                             *bufq = '\0';
1085                         }
1086 
1087                         if (IS_PATH_SEP (*bufp))
1088                             return g_strdup (bufp);
1089 
1090                         /* If the remote server is an Amiga a leading slash
1091                            might be missing. MC needs it because it is used
1092                            as separator between hostname and path internally. */
1093                         return g_strconcat (PATH_SEP_STR, bufp, (char *) NULL);
1094                     }
1095 
1096                     break;
1097                 }
1098             }
1099     }
1100 
1101     me->verrno = EIO;
1102     return NULL;
1103 }
1104 
1105 /* --------------------------------------------------------------------------------------------- */
1106 /* Setup Passive PASV FTP connection */
1107 
1108 static gboolean
1109 ftpfs_setup_passive_pasv (struct vfs_class *me, struct vfs_s_super *super,
     /* [previous][next][first][last][top][bottom][index][help]  */
1110                           int my_socket, struct sockaddr_storage *sa, socklen_t *salen)
1111 {
1112     char *c;
1113     char n[6];
1114     int xa, xb, xc, xd, xe, xf;
1115 
1116     if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "%s", "PASV") != COMPLETE)
1117         return FALSE;
1118 
1119     /* Parse remote parameters */
1120     for (c = reply_str + 4; *c != '\0' && !isdigit ((unsigned char) *c); c++)
1121         ;
1122 
1123     if (*c == '\0' || !isdigit ((unsigned char) *c))
1124         return FALSE;
1125 
1126     /* cppcheck-suppress invalidscanf */
1127     if (sscanf (c, "%d,%d,%d,%d,%d,%d", &xa, &xb, &xc, &xd, &xe, &xf) != 6)
1128         return FALSE;
1129 
1130     n[0] = (unsigned char) xa;
1131     n[1] = (unsigned char) xb;
1132     n[2] = (unsigned char) xc;
1133     n[3] = (unsigned char) xd;
1134     n[4] = (unsigned char) xe;
1135     n[5] = (unsigned char) xf;
1136 
1137     memcpy (&(((struct sockaddr_in *) sa)->sin_addr.s_addr), (void *) n, 4);
1138     memcpy (&(((struct sockaddr_in *) sa)->sin_port), (void *) &n[4], 2);
1139 
1140     return (connect (my_socket, (struct sockaddr *) sa, *salen) >= 0);
1141 }
1142 
1143 /* --------------------------------------------------------------------------------------------- */
1144 /* Setup Passive EPSV FTP connection */
1145 
1146 static gboolean
1147 ftpfs_setup_passive_epsv (struct vfs_class *me, struct vfs_s_super *super,
     /* [previous][next][first][last][top][bottom][index][help]  */
1148                           int my_socket, struct sockaddr_storage *sa, socklen_t *salen)
1149 {
1150     char *c;
1151     int port;
1152 
1153     if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "%s", "EPSV") != COMPLETE)
1154         return FALSE;
1155 
1156     /* (|||<port>|) */
1157     c = strchr (reply_str, '|');
1158     if (c == NULL || strlen (c) <= 3)
1159         return FALSE;
1160 
1161     c += 3;
1162     port = atoi (c);
1163     if (port < 0 || port > 65535)
1164         return FALSE;
1165 
1166     port = htons (port);
1167 
1168     switch (sa->ss_family)
1169     {
1170     case AF_INET:
1171         ((struct sockaddr_in *) sa)->sin_port = port;
1172         break;
1173     case AF_INET6:
1174         ((struct sockaddr_in6 *) sa)->sin6_port = port;
1175         break;
1176     default:
1177         break;
1178     }
1179 
1180     return (connect (my_socket, (struct sockaddr *) sa, *salen) >= 0);
1181 }
1182 
1183 /* --------------------------------------------------------------------------------------------- */
1184 /* Setup Passive ftp connection, we use it for source routed connections */
1185 
1186 static gboolean
1187 ftpfs_setup_passive (struct vfs_class *me, struct vfs_s_super *super,
     /* [previous][next][first][last][top][bottom][index][help]  */
1188                      int my_socket, struct sockaddr_storage *sa, socklen_t *salen)
1189 {
1190     /* It's IPV4, so try PASV first, some servers and ALGs get confused by EPSV */
1191     if (sa->ss_family == AF_INET)
1192     {
1193         if (!ftpfs_setup_passive_pasv (me, super, my_socket, sa, salen))
1194             /* An IPV4 FTP server might support EPSV, so if PASV fails we can try EPSV anyway */
1195             if (!ftpfs_setup_passive_epsv (me, super, my_socket, sa, salen))
1196                 return FALSE;
1197     }
1198     /* It's IPV6, so EPSV is our only hope */
1199     else if (!ftpfs_setup_passive_epsv (me, super, my_socket, sa, salen))
1200         return FALSE;
1201 
1202     return TRUE;
1203 }
1204 
1205 /* --------------------------------------------------------------------------------------------- */
1206 /* Setup Active PORT or EPRT FTP connection */
1207 
1208 static int
1209 ftpfs_setup_active (struct vfs_class *me, struct vfs_s_super *super,
     /* [previous][next][first][last][top][bottom][index][help]  */
1210                     struct sockaddr_storage data_addr, socklen_t data_addrlen)
1211 {
1212     unsigned short int port;
1213     char *addr;
1214     unsigned int af;
1215     int res;
1216 
1217     switch (data_addr.ss_family)
1218     {
1219     case AF_INET:
1220         af = FTP_INET;
1221         port = ((struct sockaddr_in *) &data_addr)->sin_port;
1222         break;
1223     case AF_INET6:
1224         af = FTP_INET6;
1225         port = ((struct sockaddr_in6 *) &data_addr)->sin6_port;
1226         break;
1227     default:
1228         /* Not implemented */
1229         return 0;
1230     }
1231 
1232     addr = g_try_malloc (NI_MAXHOST);
1233     if (addr == NULL)
1234         ERRNOR (ENOMEM, -1);
1235 
1236     res =
1237         getnameinfo ((struct sockaddr *) &data_addr, data_addrlen, addr, NI_MAXHOST, NULL, 0,
1238                      NI_NUMERICHOST);
1239     if (res != 0)
1240     {
1241         const char *err_str;
1242 
1243         g_free (addr);
1244 
1245         if (res == EAI_SYSTEM)
1246         {
1247             me->verrno = errno;
1248             err_str = unix_error_string (me->verrno);
1249         }
1250         else
1251         {
1252             me->verrno = EIO;
1253             err_str = gai_strerror (res);
1254         }
1255 
1256         vfs_print_message (_("ftpfs: could not make address-to-name translation: %s"), err_str);
1257 
1258         return (-1);
1259     }
1260 
1261     /* If we are talking to an IPV4 server, try PORT, and, only if it fails, go for EPRT */
1262     if (af == FTP_INET)
1263     {
1264         unsigned char *a = (unsigned char *) &((struct sockaddr_in *) &data_addr)->sin_addr;
1265         unsigned char *p = (unsigned char *) &port;
1266 
1267         if (ftpfs_command (me, super, WAIT_REPLY,
1268                            "PORT %u,%u,%u,%u,%u,%u", a[0], a[1], a[2], a[3],
1269                            p[0], p[1]) == COMPLETE)
1270         {
1271             g_free (addr);
1272             return 1;
1273         }
1274     }
1275 
1276     /*
1277      * Converts network MSB first order to host byte order (LSB
1278      * first on i386). If we do it earlier, we will run into an
1279      * endianness issue, because the server actually expects to see
1280      * "PORT A,D,D,R,MSB,LSB" in the PORT command.
1281      */
1282     port = ntohs (port);
1283 
1284     /* We are talking to an IPV6 server or PORT failed, so we can try EPRT anyway */
1285     res =
1286         (ftpfs_command (me, super, WAIT_REPLY, "EPRT |%u|%s|%hu|", af, addr, port) ==
1287          COMPLETE) ? 1 : 0;
1288     g_free (addr);
1289     return res;
1290 }
1291 
1292 /* --------------------------------------------------------------------------------------------- */
1293 /* Initialize a socket for FTP DATA connection */
1294 
1295 static int
1296 ftpfs_init_data_socket (struct vfs_class *me, struct vfs_s_super *super,
     /* [previous][next][first][last][top][bottom][index][help]  */
1297                         struct sockaddr_storage *data_addr, socklen_t *data_addrlen)
1298 {
1299     const unsigned int attempts = 10;
1300     unsigned int i;
1301     ftp_super_t *ftp_super = FTP_SUPER (super);
1302     int result;
1303 
1304     for (i = 0; i < attempts; i++)
1305     {
1306         memset (data_addr, 0, sizeof (*data_addr));
1307         *data_addrlen = sizeof (*data_addr);
1308 
1309         if (ftp_super->use_passive_connection)
1310         {
1311             result = getpeername (ftp_super->sock, (struct sockaddr *) data_addr, data_addrlen);
1312             if (result == 0)
1313                 break;
1314 
1315             me->verrno = errno;
1316 
1317             if (me->verrno == ENOTCONN)
1318             {
1319                 vfs_print_message (_("ftpfs: try reconnect to server, attempt %u"), i);
1320                 if (ftpfs_reconnect (me, super))
1321                     continue;   /* get name of new socket */
1322             }
1323             else
1324             {
1325                 /* error -- stop loop */
1326                 vfs_print_message (_("ftpfs: could not get socket name: %s"),
1327                                    unix_error_string (me->verrno));
1328             }
1329         }
1330         else
1331         {
1332             result = getsockname (ftp_super->sock, (struct sockaddr *) data_addr, data_addrlen);
1333             if (result == 0)
1334                 break;
1335 
1336             me->verrno = errno;
1337 
1338             vfs_print_message (_("ftpfs: try reconnect to server, attempt %u"), i);
1339             if (ftpfs_reconnect (me, super))
1340                 continue;       /* get name of new socket */
1341 
1342             /* error -- stop loop */
1343             vfs_print_message ("%s", _("ftpfs: could not reconnect to server"));
1344         }
1345 
1346         i = attempts;
1347     }
1348 
1349     if (i >= attempts)
1350         return (-1);
1351 
1352     switch (data_addr->ss_family)
1353     {
1354     case AF_INET:
1355         ((struct sockaddr_in *) data_addr)->sin_port = 0;
1356         break;
1357     case AF_INET6:
1358         ((struct sockaddr_in6 *) data_addr)->sin6_port = 0;
1359         break;
1360     default:
1361         vfs_print_message ("%s", _("ftpfs: invalid address family"));
1362         ERRNOR (EINVAL, -1);
1363     }
1364 
1365     result = socket (data_addr->ss_family, SOCK_STREAM, IPPROTO_TCP);
1366     if (result < 0)
1367     {
1368         me->verrno = errno;
1369         vfs_print_message (_("ftpfs: could not create socket: %s"), unix_error_string (me->verrno));
1370         result = -1;
1371     }
1372 
1373     return result;
1374 }
1375 
1376 /* --------------------------------------------------------------------------------------------- */
1377 /* Initialize FTP DATA connection */
1378 
1379 static int
1380 ftpfs_initconn (struct vfs_class *me, struct vfs_s_super *super)
     /* [previous][next][first][last][top][bottom][index][help]  */
1381 {
1382     ftp_super_t *ftp_super = FTP_SUPER (super);
1383     struct sockaddr_storage data_addr;
1384     socklen_t data_addrlen;
1385 
1386     /*
1387      * Don't factor socket initialization out of these conditionals,
1388      * because ftpfs_init_data_socket initializes it in different way
1389      * depending on use_passive_connection flag.
1390      */
1391 
1392     /* Try to establish a passive connection first (if requested) */
1393     if (ftp_super->use_passive_connection)
1394     {
1395         int data_sock;
1396 
1397         data_sock = ftpfs_init_data_socket (me, super, &data_addr, &data_addrlen);
1398         if (data_sock < 0)
1399             return (-1);
1400 
1401         if (ftpfs_setup_passive (me, super, data_sock, &data_addr, &data_addrlen))
1402             return data_sock;
1403 
1404         vfs_print_message ("%s", _("ftpfs: could not setup passive mode"));
1405         ftp_super->use_passive_connection = FALSE;
1406 
1407         close (data_sock);
1408     }
1409 
1410     /* If passive setup is disabled or failed, fallback to active connections */
1411     if (!ftp_super->use_passive_connection)
1412     {
1413         int data_sock;
1414 
1415         data_sock = ftpfs_init_data_socket (me, super, &data_addr, &data_addrlen);
1416         if (data_sock < 0)
1417             return (-1);
1418 
1419         if ((bind (data_sock, (struct sockaddr *) &data_addr, data_addrlen) != 0) ||
1420             (getsockname (data_sock, (struct sockaddr *) &data_addr, &data_addrlen) != 0) ||
1421             (listen (data_sock, 1) != 0))
1422         {
1423             close (data_sock);
1424             ERRNOR (errno, -1);
1425         }
1426 
1427         if (ftpfs_setup_active (me, super, data_addr, data_addrlen) != 0)
1428             return data_sock;
1429 
1430         close (data_sock);
1431     }
1432 
1433     /* Restore the initial value of use_passive_connection (for subsequent retries) */
1434     ftp_super->use_passive_connection =
1435         ftp_super->proxy !=
1436         NULL ? ftpfs_use_passive_connections_over_proxy : ftpfs_use_passive_connections;
1437 
1438     me->verrno = EIO;
1439     return (-1);
1440 }
1441 
1442 /* --------------------------------------------------------------------------------------------- */
1443 
1444 static int
1445 ftpfs_open_data_connection (struct vfs_class *me, struct vfs_s_super *super, const char *cmd,
     /* [previous][next][first][last][top][bottom][index][help]  */
1446                             const char *remote, int isbinary, int reget)
1447 {
1448     ftp_super_t *ftp_super = FTP_SUPER (super);
1449     int s, j, data;
1450 
1451     /* FTP doesn't allow to open more than one file at a time */
1452     if (ftp_super->ctl_connection_busy)
1453         return (-1);
1454 
1455     s = ftpfs_initconn (me, super);
1456     if (s == -1)
1457         return (-1);
1458 
1459     if (ftpfs_changetype (me, super, isbinary) == -1)
1460     {
1461         close (s);
1462         return (-1);
1463     }
1464 
1465     if (reget > 0)
1466     {
1467         j = ftpfs_command (me, super, WAIT_REPLY, "REST %d", reget);
1468         if (j != CONTINUE)
1469         {
1470             close (s);
1471             ERRNOR (EIO, -1);
1472         }
1473     }
1474 
1475     if (remote == NULL)
1476         j = ftpfs_command (me, super, WAIT_REPLY, "%s", cmd);
1477     else
1478     {
1479         char *remote_path;
1480 
1481         remote_path = ftpfs_translate_path (me, super, remote);
1482         j = ftpfs_command (me, super, WAIT_REPLY, "%s /%s", cmd,
1483                            /* WarFtpD can't STORE //filename */
1484                            IS_PATH_SEP (*remote_path) ? remote_path + 1 : remote_path);
1485         g_free (remote_path);
1486     }
1487 
1488     if (j != PRELIM)
1489     {
1490         close (s);
1491         ERRNOR (EPERM, -1);
1492     }
1493 
1494     if (ftp_super->use_passive_connection)
1495         data = s;
1496     else
1497     {
1498         struct sockaddr_storage from;
1499         socklen_t fromlen = sizeof (from);
1500 
1501         tty_enable_interrupt_key ();
1502         data = accept (s, (struct sockaddr *) &from, &fromlen);
1503         if (data < 0)
1504             me->verrno = errno;
1505         tty_disable_interrupt_key ();
1506         close (s);
1507         if (data < 0)
1508             return (-1);
1509     }
1510 
1511     ftp_super->ctl_connection_busy = TRUE;
1512     return data;
1513 }
1514 
1515 /* --------------------------------------------------------------------------------------------- */
1516 
1517 static void
1518 ftpfs_linear_abort (struct vfs_class *me, vfs_file_handler_t *fh)
     /* [previous][next][first][last][top][bottom][index][help]  */
1519 {
1520     struct vfs_s_super *super = VFS_FILE_HANDLER_SUPER (fh);
1521     ftp_super_t *ftp_super = FTP_SUPER (super);
1522     static unsigned char const ipbuf[3] = { IAC, IP, IAC };
1523     fd_set mask;
1524     int dsock = FH_SOCK;
1525 
1526     FH_SOCK = -1;
1527     ftp_super->ctl_connection_busy = FALSE;
1528 
1529     vfs_print_message ("%s", _("ftpfs: aborting transfer."));
1530 
1531     if (send (ftp_super->sock, ipbuf, sizeof (ipbuf), MSG_OOB) != sizeof (ipbuf))
1532     {
1533         vfs_print_message (_("ftpfs: abort error: %s"), unix_error_string (errno));
1534         if (dsock != -1)
1535             close (dsock);
1536         return;
1537     }
1538 
1539     if (ftpfs_command (me, super, NONE, "%cABOR", DM) != COMPLETE)
1540     {
1541         vfs_print_message ("%s", _("ftpfs: abort failed"));
1542         if (dsock != -1)
1543             close (dsock);
1544         return;
1545     }
1546 
1547     if (dsock != -1)
1548     {
1549         FD_ZERO (&mask);
1550         FD_SET (dsock, &mask);
1551 
1552         if (select (dsock + 1, &mask, NULL, NULL, NULL) > 0)
1553         {
1554             gint64 start_tim;
1555             char buf[BUF_8K];
1556 
1557             start_tim = g_get_monotonic_time ();
1558 
1559             /* flush the remaining data */
1560             while (read (dsock, buf, sizeof (buf)) > 0)
1561             {
1562                 gint64 tim;
1563 
1564                 tim = g_get_monotonic_time ();
1565 
1566                 if (tim > start_tim + ABORT_TIMEOUT)
1567                 {
1568                     /* server keeps sending, drop the connection and ftpfs_reconnect */
1569                     close (dsock);
1570                     ftpfs_reconnect (me, super);
1571                     return;
1572                 }
1573             }
1574         }
1575         close (dsock);
1576     }
1577 
1578     if ((ftpfs_get_reply (me, ftp_super->sock, NULL, 0) == TRANSIENT) && (code == 426))
1579         ftpfs_get_reply (me, ftp_super->sock, NULL, 0);
1580 }
1581 
1582 /* --------------------------------------------------------------------------------------------- */
1583 
1584 #if 0
1585 static void
1586 resolve_symlink_without_ls_options (struct vfs_class *me, struct vfs_s_super *super,
     /* [previous][next][first][last][top][bottom][index][help]  */
1587                                     struct vfs_s_inode *dir)
1588 {
1589     struct linklist *flist;
1590     struct direntry *fe, *fel;
1591     char tmp[MC_MAXPATHLEN];
1592 
1593     dir->symlink_status = FTPFS_RESOLVING_SYMLINKS;
1594     for (flist = dir->file_list->next; flist != dir->file_list; flist = flist->next)
1595     {
1596         /* flist->data->l_stat is already initialized with 0 */
1597         fel = flist->data;
1598         if (S_ISLNK (fel->s.st_mode) && fel->linkname != NULL)
1599         {
1600             int depth;
1601 
1602             if (IS_PATH_SEP (fel->linkname[0]))
1603             {
1604                 if (strlen (fel->linkname) >= MC_MAXPATHLEN)
1605                     continue;
1606                 strcpy (tmp, fel->linkname);
1607             }
1608             else
1609             {
1610                 if ((strlen (dir->remote_path) + strlen (fel->linkname)) >= MC_MAXPATHLEN)
1611                     continue;
1612                 strcpy (tmp, dir->remote_path);
1613                 if (tmp[1] != '\0')
1614                     strcat (tmp, PATH_SEP_STR);
1615                 strcat (tmp + 1, fel->linkname);
1616             }
1617 
1618             for (depth = 0; depth < 100; depth++)
1619             {                   /* depth protects against recursive symbolic links */
1620                 canonicalize_pathname (tmp);
1621                 fe = _get_file_entry_t (bucket, tmp, 0, 0);
1622                 if (fe != NULL)
1623                 {
1624                     if (S_ISLNK (fe->s.st_mode) && fe->l_stat == 0)
1625                     {
1626                         /* Symlink points to link which isn't resolved, yet. */
1627                         if (IS_PATH_SEP (fe->linkname[0]))
1628                         {
1629                             if (strlen (fe->linkname) >= MC_MAXPATHLEN)
1630                                 break;
1631                             strcpy (tmp, fe->linkname);
1632                         }
1633                         else
1634                         {
1635                             /* at this point tmp looks always like this
1636                                /directory/filename, i.e. no need to check
1637                                strrchr's return value */
1638                             *(strrchr (tmp, PATH_SEP) + 1) = '\0';      /* dirname */
1639                             if ((strlen (tmp) + strlen (fe->linkname)) >= MC_MAXPATHLEN)
1640                                 break;
1641                             strcat (tmp, fe->linkname);
1642                         }
1643                         continue;
1644                     }
1645                     else
1646                     {
1647                         fel->l_stat = g_new (struct stat, 1);
1648                         if (S_ISLNK (fe->s.st_mode))
1649                             *fel->l_stat = *fe->l_stat;
1650                         else
1651                             *fel->l_stat = fe->s;
1652                         (*fel->l_stat).st_ino = bucket->__inode_counter++;
1653                     }
1654                 }
1655                 break;
1656             }
1657         }
1658     }
1659 
1660     dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1661 }
1662 
1663 /* --------------------------------------------------------------------------------------------- */
1664 
1665 static void
1666 resolve_symlink_with_ls_options (struct vfs_class *me, struct vfs_s_super *super,
     /* [previous][next][first][last][top][bottom][index][help]  */
1667                                  struct vfs_s_inode *dir)
1668 {
1669     char buffer[2048] = "", *filename;
1670     int sock;
1671     FILE *fp;
1672     struct stat s;
1673     struct linklist *flist;
1674     struct direntry *fe;
1675     int switch_method = 0;
1676 
1677     dir->symlink_status = FTPFS_RESOLVED_SYMLINKS;
1678     if (strchr (dir->remote_path, ' ') == NULL)
1679         sock = ftpfs_open_data_connection (bucket, "LIST -lLa", dir->remote_path, TYPE_ASCII, 0);
1680     else
1681     {
1682         if (ftpfs_chdir_internal (bucket, dir->remote_path) != COMPLETE)
1683         {
1684             vfs_print_message ("%s", _("ftpfs: CWD failed."));
1685             return;
1686         }
1687 
1688         sock = ftpfs_open_data_connection (bucket, "LIST -lLa", ".", TYPE_ASCII, 0);
1689     }
1690 
1691     if (sock == -1)
1692     {
1693         vfs_print_message ("%s", _("ftpfs: couldn't resolve symlink"));
1694         return;
1695     }
1696 
1697     fp = fdopen (sock, "r");
1698     if (fp == NULL)
1699     {
1700         close (sock);
1701         vfs_print_message ("%s", _("ftpfs: couldn't resolve symlink"));
1702         return;
1703     }
1704     tty_enable_interrupt_key ();
1705     flist = dir->file_list->next;
1706 
1707     while (TRUE)
1708     {
1709         do
1710         {
1711             if (flist == dir->file_list)
1712                 goto done;
1713 
1714             fe = flist->data;
1715             flist = flist->next;
1716         }
1717         while (!S_ISLNK (fe->s.st_mode));
1718 
1719         while (TRUE)
1720         {
1721             if (fgets (buffer, sizeof (buffer), fp) == NULL)
1722                 goto done;
1723 
1724             if (me->logfile != NULL)
1725             {
1726                 fputs (buffer, me->logfile);
1727                 fflush (me->logfile);
1728             }
1729 
1730             vfs_die ("This code should be commented out\n");
1731 
1732             if (vfs_parse_ls_lga (buffer, &s, &filename, NULL))
1733             {
1734                 int r;
1735 
1736                 r = strcmp (fe->name, filename);
1737                 g_free (filename);
1738                 if (r == 0)
1739                 {
1740                     if (S_ISLNK (s.st_mode))
1741                     {
1742                         /* This server doesn't understand LIST -lLa */
1743                         switch_method = 1;
1744                         goto done;
1745                     }
1746 
1747                     fe->l_stat = g_try_new (struct stat, 1);
1748                     if (fe->l_stat == NULL)
1749                         goto done;
1750 
1751                     *fe->l_stat = s;
1752                     (*fe->l_stat).st_ino = bucket->__inode_counter++;
1753                     break;
1754                 }
1755 
1756                 if (r < 0)
1757                     break;
1758             }
1759         }
1760     }
1761 
1762   done:
1763     while (fgets (buffer, sizeof (buffer), fp) != NULL)
1764         ;
1765     tty_disable_interrupt_key ();
1766     fclose (fp);
1767     ftpfs_get_reply (me, FTP_SUPER (super)->sock, NULL, 0);
1768 }
1769 
1770 /* --------------------------------------------------------------------------------------------- */
1771 
1772 static void
1773 resolve_symlink (struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir)
     /* [previous][next][first][last][top][bottom][index][help]  */
1774 {
1775     vfs_print_message ("%s", _("Resolving symlink..."));
1776 
1777     if (FTP_SUPER (super)->strict_rfc959_list_cmd)
1778         resolve_symlink_without_ls_options (me, super, dir);
1779     else
1780         resolve_symlink_with_ls_options (me, super, dir);
1781 }
1782 #endif
1783 
1784 /* --------------------------------------------------------------------------------------------- */
1785 
1786 static int
1787 ftpfs_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, const char *remote_path)
     /* [previous][next][first][last][top][bottom][index][help]  */
1788 {
1789     struct vfs_s_super *super = dir->super;
1790     ftp_super_t *ftp_super = FTP_SUPER (super);
1791     int sock;
1792     char lc_buffer[BUF_8K];
1793     int res;
1794     gboolean cd_first;
1795     GSList *dirlist = NULL;
1796     GSList *entlist;
1797     GSList *iter;
1798     int err_count = 0;
1799 
1800     cd_first = ftpfs_first_cd_then_ls || (ftp_super->strict == RFC_STRICT)
1801         || (strchr (remote_path, ' ') != NULL);
1802 
1803   again:
1804     vfs_print_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1805                        remote_path,
1806                        ftp_super->strict ==
1807                        RFC_STRICT ? _("(strict rfc959)") : "", cd_first ? _("(chdir first)") : "");
1808 
1809     if (cd_first && ftpfs_chdir_internal (me, super, remote_path) != COMPLETE)
1810     {
1811         me->verrno = ENOENT;
1812         vfs_print_message ("%s", _("ftpfs: CWD failed."));
1813         return (-1);
1814     }
1815 
1816     dir->timestamp = g_get_monotonic_time () + ftpfs_directory_timeout * G_USEC_PER_SEC;
1817 
1818     if (ftp_super->strict == RFC_STRICT)
1819         sock = ftpfs_open_data_connection (me, super, "LIST", 0, TYPE_ASCII, 0);
1820     else if (cd_first)
1821         /* Dirty hack to avoid autoprepending / to . */
1822         /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1823         sock = ftpfs_open_data_connection (me, super, "LIST -la", 0, TYPE_ASCII, 0);
1824     else
1825     {
1826         char *path;
1827 
1828         /* Trailing "/." is necessary if remote_path is a symlink */
1829         path = g_strconcat (remote_path, PATH_SEP_STR ".", (char *) NULL);
1830         sock = ftpfs_open_data_connection (me, super, "LIST -la", path, TYPE_ASCII, 0);
1831         g_free (path);
1832     }
1833 
1834     if (sock == -1)
1835     {
1836       fallback:
1837         if (ftp_super->strict == RFC_AUTODETECT)
1838         {
1839             /* It's our first attempt to get a directory listing from this
1840                server (UNIX style LIST command) */
1841             ftp_super->strict = RFC_STRICT;
1842             /* I hate goto, but recursive call needs another 8K on stack */
1843             /* return ftpfs_dir_load (me, dir, remote_path); */
1844             cd_first = TRUE;
1845             goto again;
1846         }
1847 
1848         vfs_print_message ("%s", _("ftpfs: failed; nowhere to fallback to"));
1849         ERRNOR (EACCES, -1);
1850     }
1851 
1852     /* read full directory list, then parse it */
1853     while ((res = vfs_s_get_line_interruptible (me, lc_buffer, sizeof (lc_buffer), sock)) != 0)
1854     {
1855         if (res == EINTR)
1856         {
1857             me->verrno = ECONNRESET;
1858             close (sock);
1859             ftp_super->ctl_connection_busy = FALSE;
1860             ftpfs_get_reply (me, ftp_super->sock, NULL, 0);
1861             g_slist_free_full (dirlist, g_free);
1862             vfs_print_message (_("%s: failure"), me->name);
1863             return (-1);
1864         }
1865 
1866         if (me->logfile != NULL)
1867         {
1868             fputs (lc_buffer, me->logfile);
1869             fputs ("\n", me->logfile);
1870             fflush (me->logfile);
1871         }
1872 
1873         dirlist = g_slist_prepend (dirlist, g_strdup (lc_buffer));
1874     }
1875 
1876     close (sock);
1877     ftp_super->ctl_connection_busy = FALSE;
1878     me->verrno = E_REMOTE;
1879     if ((ftpfs_get_reply (me, ftp_super->sock, NULL, 0) != COMPLETE))
1880     {
1881         g_slist_free_full (dirlist, g_free);
1882         goto fallback;
1883     }
1884 
1885     if (dirlist == NULL && !cd_first)
1886     {
1887         /* The LIST command may produce an empty output. In such scenario
1888            it is not clear whether this is caused by  'remote_path' being
1889            a non-existent path or for some other reason (listing empty
1890            directory without the -a option, non-readable directory, etc.).
1891 
1892            Since 'dir_load' is a crucial method, when it comes to determine
1893            whether a given path is a _directory_, the code must try its best
1894            to determine the type of 'remote_path'. The only reliable way to
1895            achieve this is through issuing a CWD command. */
1896 
1897         cd_first = TRUE;
1898         goto again;
1899     }
1900 
1901     /* parse server's reply */
1902     dirlist = g_slist_reverse (dirlist);        /* restore order */
1903     entlist = ftpfs_parse_long_list (me, dir, dirlist, &err_count);
1904     g_slist_free_full (dirlist, g_free);
1905 
1906     for (iter = entlist; iter != NULL; iter = g_slist_next (iter))
1907         vfs_s_insert_entry (me, dir, VFS_ENTRY (iter->data));
1908 
1909     g_slist_free (entlist);
1910 
1911     if (ftp_super->strict == RFC_AUTODETECT)
1912         ftp_super->strict = RFC_DARING;
1913 
1914     vfs_print_message (_("%s: done."), me->name);
1915     return 0;
1916 }
1917 
1918 /* --------------------------------------------------------------------------------------------- */
1919 
1920 static int
1921 ftpfs_file_store (struct vfs_class *me, vfs_file_handler_t *fh, char *name, char *localname)
     /* [previous][next][first][last][top][bottom][index][help]  */
1922 {
1923     struct vfs_s_super *super = VFS_FILE_HANDLER_SUPER (fh);
1924     ftp_super_t *ftp_super = FTP_SUPER (super);
1925     ftp_file_handler_t *ftp = FTP_FILE_HANDLER (fh);
1926 
1927     int h, sock;
1928     off_t n_stored = 0;
1929 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1930     struct linger li;
1931 #else
1932     int flag_one = 1;
1933 #endif
1934     char lc_buffer[BUF_8K];
1935     struct stat s;
1936     char *w_buf;
1937 
1938     h = open (localname, O_RDONLY);
1939     if (h == -1)
1940         ERRNOR (EIO, -1);
1941 
1942     if (fstat (h, &s) == -1)
1943     {
1944         me->verrno = errno;
1945         close (h);
1946         return (-1);
1947     }
1948 
1949     sock =
1950         ftpfs_open_data_connection (me, super, ftp->append ? "APPE" : "STOR", name, TYPE_BINARY, 0);
1951     if (sock < 0)
1952     {
1953         close (h);
1954         return (-1);
1955     }
1956 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1957     li.l_onoff = 1;
1958     li.l_linger = 120;
1959     setsockopt (sock, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof (li));
1960 #else
1961     setsockopt (sock, SOL_SOCKET, SO_LINGER, &flag_one, sizeof (flag_one));
1962 #endif
1963 
1964     tty_enable_interrupt_key ();
1965     while (TRUE)
1966     {
1967         ssize_t n_read, n_written;
1968 
1969         while ((n_read = read (h, lc_buffer, sizeof (lc_buffer))) == -1)
1970         {
1971             if (errno != EINTR)
1972             {
1973                 me->verrno = errno;
1974                 goto error_return;
1975             }
1976             if (tty_got_interrupt ())
1977             {
1978                 me->verrno = EINTR;
1979                 goto error_return;
1980             }
1981         }
1982         if (n_read == 0)
1983             break;
1984 
1985         n_stored += n_read;
1986         w_buf = lc_buffer;
1987 
1988         while ((n_written = write (sock, w_buf, n_read)) != n_read)
1989         {
1990             if (n_written == -1)
1991             {
1992                 if (errno == EINTR && !tty_got_interrupt ())
1993                     continue;
1994 
1995                 me->verrno = errno;
1996                 goto error_return;
1997             }
1998 
1999             w_buf += n_written;
2000             n_read -= n_written;
2001         }
2002 
2003         vfs_print_message ("%s: %" PRIuMAX "/%" PRIuMAX,
2004                            _("ftpfs: storing file"), (uintmax_t) n_stored, (uintmax_t) s.st_size);
2005     }
2006     tty_disable_interrupt_key ();
2007 
2008     close (sock);
2009     ftp_super->ctl_connection_busy = FALSE;
2010     close (h);
2011 
2012     if (ftpfs_get_reply (me, ftp_super->sock, NULL, 0) != COMPLETE)
2013         ERRNOR (EIO, -1);
2014     return 0;
2015 
2016   error_return:
2017     tty_disable_interrupt_key ();
2018     close (sock);
2019     ftp_super->ctl_connection_busy = FALSE;
2020     close (h);
2021 
2022     ftpfs_get_reply (me, ftp_super->sock, NULL, 0);
2023     return (-1);
2024 }
2025 
2026 /* --------------------------------------------------------------------------------------------- */
2027 
2028 static int
2029 ftpfs_linear_start (struct vfs_class *me, vfs_file_handler_t *fh, off_t offset)
     /* [previous][next][first][last][top][bottom][index][help]  */
2030 {
2031     char *name;
2032 
2033     name = vfs_s_fullpath (me, fh->ino);
2034     if (name == NULL)
2035         return 0;
2036 
2037     FH_SOCK =
2038         ftpfs_open_data_connection (me, VFS_FILE_HANDLER_SUPER (fh), "RETR", name, TYPE_BINARY,
2039                                     offset);
2040     g_free (name);
2041     if (FH_SOCK == -1)
2042         ERRNOR (EACCES, 0);
2043 
2044     fh->linear = LS_LINEAR_OPEN;
2045     FTP_FILE_HANDLER (fh)->append = FALSE;
2046     return 1;
2047 }
2048 
2049 /* --------------------------------------------------------------------------------------------- */
2050 
2051 static ssize_t
2052 ftpfs_linear_read (struct vfs_class *me, vfs_file_handler_t *fh, void *buf, size_t len)
     /* [previous][next][first][last][top][bottom][index][help]  */
2053 {
2054     ssize_t n;
2055     struct vfs_s_super *super = VFS_FILE_HANDLER_SUPER (fh);
2056 
2057     while ((n = read (FH_SOCK, buf, len)) < 0)
2058     {
2059         if ((errno == EINTR) && !tty_got_interrupt ())
2060             continue;
2061         break;
2062     }
2063 
2064     if (n < 0)
2065         ftpfs_linear_abort (me, fh);
2066     else if (n == 0)
2067     {
2068         FTP_SUPER (super)->ctl_connection_busy = FALSE;
2069         close (FH_SOCK);
2070         FH_SOCK = -1;
2071         if ((ftpfs_get_reply (me, FTP_SUPER (super)->sock, NULL, 0) != COMPLETE))
2072             ERRNOR (E_REMOTE, -1);
2073         return 0;
2074     }
2075 
2076     ERRNOR (errno, n);
2077 }
2078 
2079 /* --------------------------------------------------------------------------------------------- */
2080 
2081 static void
2082 ftpfs_linear_close (struct vfs_class *me, vfs_file_handler_t *fh)
     /* [previous][next][first][last][top][bottom][index][help]  */
2083 {
2084     if (FH_SOCK != -1)
2085         ftpfs_linear_abort (me, fh);
2086 }
2087 
2088 /* --------------------------------------------------------------------------------------------- */
2089 
2090 static int
2091 ftpfs_ctl (void *fh, int ctlop, void *arg)
     /* [previous][next][first][last][top][bottom][index][help]  */
2092 {
2093     (void) arg;
2094 
2095     switch (ctlop)
2096     {
2097     case VFS_CTL_IS_NOTREADY:
2098         {
2099             vfs_file_handler_t *file = VFS_FILE_HANDLER (fh);
2100             int v;
2101 
2102             if (file->linear == LS_NOT_LINEAR)
2103                 vfs_die ("You may not do this");
2104             if (file->linear == LS_LINEAR_CLOSED || file->linear == LS_LINEAR_PREOPEN)
2105                 return 0;
2106 
2107             v = vfs_s_select_on_two (FH_SOCK, 0);
2108             return (((v < 0) && (errno == EINTR)) || v == 0) ? 1 : 0;
2109         }
2110     default:
2111         return 0;
2112     }
2113 }
2114 
2115 /* --------------------------------------------------------------------------------------------- */
2116 
2117 static int
2118 ftpfs_send_command (const vfs_path_t *vpath, const char *cmd, int flags)
     /* [previous][next][first][last][top][bottom][index][help]  */
2119 {
2120     const char *rpath;
2121     char *p;
2122     struct vfs_s_super *super;
2123     int r;
2124     struct vfs_class *me;
2125     gboolean flush_directory_cache = (flags & OPT_FLUSH) != 0;
2126 
2127     me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath));
2128 
2129     rpath = vfs_s_get_path (vpath, &super, 0);
2130     if (rpath == NULL)
2131         return (-1);
2132 
2133     p = ftpfs_translate_path (me, super, rpath);
2134     r = ftpfs_command (me, super, WAIT_REPLY, cmd, p);
2135     g_free (p);
2136     vfs_stamp_create (vfs_ftpfs_ops, super);
2137     if ((flags & OPT_IGNORE_ERROR) != 0)
2138         r = COMPLETE;
2139     if (r != COMPLETE)
2140     {
2141         me->verrno = EPERM;
2142         return (-1);
2143     }
2144     if (flush_directory_cache)
2145         vfs_s_invalidate (me, super);
2146     return 0;
2147 }
2148 
2149 /* --------------------------------------------------------------------------------------------- */
2150 
2151 static int
2152 ftpfs_stat (const vfs_path_t *vpath, struct stat *buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
2153 {
2154     int ret;
2155 
2156     ret = vfs_s_stat (vpath, buf);
2157     ftpfs_set_blksize (buf);
2158     return ret;
2159 }
2160 
2161 /* --------------------------------------------------------------------------------------------- */
2162 
2163 static int
2164 ftpfs_lstat (const vfs_path_t *vpath, struct stat *buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
2165 {
2166     int ret;
2167 
2168     ret = vfs_s_lstat (vpath, buf);
2169     ftpfs_set_blksize (buf);
2170     return ret;
2171 }
2172 
2173 /* --------------------------------------------------------------------------------------------- */
2174 
2175 static int
2176 ftpfs_fstat (void *vfs_info, struct stat *buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
2177 {
2178     int ret;
2179 
2180     ret = vfs_s_fstat (vfs_info, buf);
2181     ftpfs_set_blksize (buf);
2182     return ret;
2183 }
2184 
2185 /* --------------------------------------------------------------------------------------------- */
2186 
2187 static int
2188 ftpfs_chmod (const vfs_path_t *vpath, mode_t mode)
     /* [previous][next][first][last][top][bottom][index][help]  */
2189 {
2190     char buf[BUF_SMALL];
2191     int ret;
2192 
2193     g_snprintf (buf, sizeof (buf), "SITE CHMOD %4.4o /%%s", (unsigned int) (mode & 07777));
2194     ret = ftpfs_send_command (vpath, buf, OPT_FLUSH);
2195     return ftpfs_ignore_chattr_errors ? 0 : ret;
2196 }
2197 
2198 /* --------------------------------------------------------------------------------------------- */
2199 
2200 static int
2201 ftpfs_chown (const vfs_path_t *vpath, uid_t owner, gid_t group)
     /* [previous][next][first][last][top][bottom][index][help]  */
2202 {
2203 #if 0
2204     (void) vpath;
2205     (void) owner;
2206     (void) group;
2207 
2208     me->verrno = EPERM;
2209     return (-1);
2210 #else
2211     /* Everyone knows it is not possible to chown remotely, so why bother them.
2212        If someone's root, then copy/move will always try to chown it... */
2213     (void) vpath;
2214     (void) owner;
2215     (void) group;
2216     return 0;
2217 #endif
2218 }
2219 
2220 /* --------------------------------------------------------------------------------------------- */
2221 
2222 static int
2223 ftpfs_unlink (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
2224 {
2225     return ftpfs_send_command (vpath, "DELE /%s", OPT_FLUSH);
2226 }
2227 
2228 /* --------------------------------------------------------------------------------------------- */
2229 
2230 /* Return TRUE if path is the same directory as the one we are in now */
2231 static gboolean
2232 ftpfs_is_same_dir (struct vfs_class *me, struct vfs_s_super *super, const char *path)
     /* [previous][next][first][last][top][bottom][index][help]  */
2233 {
2234     (void) me;
2235 
2236     return (FTP_SUPER (super)->current_dir != NULL
2237             && strcmp (path, FTP_SUPER (super)->current_dir) == 0);
2238 }
2239 
2240 /* --------------------------------------------------------------------------------------------- */
2241 
2242 static int
2243 ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
     /* [previous][next][first][last][top][bottom][index][help]  */
2244 {
2245     ftp_super_t *ftp_super = FTP_SUPER (super);
2246     int r;
2247     char *p;
2248 
2249     if (!ftp_super->cwd_deferred && ftpfs_is_same_dir (me, super, remote_path))
2250         return COMPLETE;
2251 
2252     p = ftpfs_translate_path (me, super, remote_path);
2253     r = ftpfs_command (me, super, WAIT_REPLY, "CWD /%s", p);
2254     g_free (p);
2255 
2256     if (r != COMPLETE)
2257         me->verrno = EIO;
2258     else
2259     {
2260         g_free (ftp_super->current_dir);
2261         ftp_super->current_dir = g_strdup (remote_path);
2262         ftp_super->cwd_deferred = FALSE;
2263     }
2264     return r;
2265 }
2266 
2267 /* --------------------------------------------------------------------------------------------- */
2268 
2269 static int
2270 ftpfs_rename (const vfs_path_t *vpath1, const vfs_path_t *vpath2)
     /* [previous][next][first][last][top][bottom][index][help]  */
2271 {
2272     ftpfs_send_command (vpath1, "RNFR /%s", OPT_FLUSH);
2273     return ftpfs_send_command (vpath2, "RNTO /%s", OPT_FLUSH);
2274 }
2275 
2276 /* --------------------------------------------------------------------------------------------- */
2277 
2278 static int
2279 ftpfs_mkdir (const vfs_path_t *vpath, mode_t mode)
     /* [previous][next][first][last][top][bottom][index][help]  */
2280 {
2281     (void) mode;                /* FIXME: should be used */
2282 
2283     return ftpfs_send_command (vpath, "MKD /%s", OPT_FLUSH);
2284 }
2285 
2286 /* --------------------------------------------------------------------------------------------- */
2287 
2288 static int
2289 ftpfs_rmdir (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
2290 {
2291     return ftpfs_send_command (vpath, "RMD /%s", OPT_FLUSH);
2292 }
2293 
2294 /* --------------------------------------------------------------------------------------------- */
2295 
2296 static vfs_file_handler_t *
2297 ftpfs_fh_new (struct vfs_s_inode *ino, gboolean changed)
     /* [previous][next][first][last][top][bottom][index][help]  */
2298 {
2299     ftp_file_handler_t *fh;
2300 
2301     fh = g_new0 (ftp_file_handler_t, 1);
2302     vfs_s_init_fh (VFS_FILE_HANDLER (fh), ino, changed);
2303     fh->sock = -1;
2304 
2305     return VFS_FILE_HANDLER (fh);
2306 }
2307 
2308 /* --------------------------------------------------------------------------------------------- */
2309 
2310 static int
2311 ftpfs_fh_open (struct vfs_class *me, vfs_file_handler_t *fh, int flags, mode_t mode)
     /* [previous][next][first][last][top][bottom][index][help]  */
2312 {
2313     ftp_file_handler_t *ftp = FTP_FILE_HANDLER (fh);
2314 
2315     (void) mode;
2316 
2317     /* File will be written only, so no need to retrieve it from ftp server */
2318     if (((flags & O_WRONLY) == O_WRONLY) && ((flags & (O_RDONLY | O_RDWR)) == 0))
2319     {
2320 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2321         struct linger li;
2322 #else
2323         int li = 1;
2324 #endif
2325         char *name;
2326 
2327         /* ftpfs_linear_start() called, so data will be written
2328          * to local temporary file and stored to ftp server
2329          * by vfs_s_close later
2330          */
2331         if (FTP_SUPER (VFS_FILE_HANDLER_SUPER (fh))->ctl_connection_busy)
2332         {
2333             if (fh->ino->localname == NULL)
2334             {
2335                 vfs_path_t *vpath;
2336                 int handle;
2337 
2338                 handle = vfs_mkstemps (&vpath, me->name, fh->ino->ent->name);
2339                 if (handle == -1)
2340                     return (-1);
2341 
2342                 close (handle);
2343                 fh->ino->localname = vfs_path_free (vpath, FALSE);
2344                 ftp->append = (flags & O_APPEND) != 0;
2345             }
2346             return 0;
2347         }
2348         name = vfs_s_fullpath (me, fh->ino);
2349         if (name == NULL)
2350             return (-1);
2351 
2352         fh->handle =
2353             ftpfs_open_data_connection (me, VFS_FILE_HANDLER_SUPER (fh),
2354                                         (flags & O_APPEND) != 0 ? "APPE" : "STOR", name,
2355                                         TYPE_BINARY, 0);
2356         g_free (name);
2357 
2358         if (fh->handle < 0)
2359             return (-1);
2360 
2361 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2362         li.l_onoff = 1;
2363         li.l_linger = 120;
2364 #endif
2365         setsockopt (fh->handle, SOL_SOCKET, SO_LINGER, &li, sizeof (li));
2366 
2367         if (fh->ino->localname != NULL)
2368         {
2369             unlink (fh->ino->localname);
2370             MC_PTR_FREE (fh->ino->localname);
2371         }
2372         return 0;
2373     }
2374 
2375     if (fh->ino->localname == NULL && vfs_s_retrieve_file (me, fh->ino) == -1)
2376         return (-1);
2377 
2378     if (fh->ino->localname == NULL)
2379         vfs_die ("retrieve_file failed to fill in localname");
2380     return 0;
2381 }
2382 
2383 /* --------------------------------------------------------------------------------------------- */
2384 
2385 static int
2386 ftpfs_fh_close (struct vfs_class *me, vfs_file_handler_t *fh)
     /* [previous][next][first][last][top][bottom][index][help]  */
2387 {
2388     if (fh->handle != -1 && fh->ino->localname == NULL)
2389     {
2390         ftp_super_t *ftp = FTP_SUPER (VFS_FILE_HANDLER_SUPER (fh));
2391 
2392         close (fh->handle);
2393         fh->handle = -1;
2394         ftp->ctl_connection_busy = FALSE;
2395         /* File is stored to destination already, so
2396          * we prevent VFS_SUBCLASS (me)->ftpfs_file_store() call from vfs_s_close ()
2397          */
2398         fh->changed = FALSE;
2399         if (ftpfs_get_reply (me, ftp->sock, NULL, 0) != COMPLETE)
2400             ERRNOR (EIO, -1);
2401         vfs_s_invalidate (me, VFS_FILE_HANDLER_SUPER (fh));
2402     }
2403 
2404     return 0;
2405 }
2406 
2407 /* --------------------------------------------------------------------------------------------- */
2408 
2409 static void
2410 ftpfs_done (struct vfs_class *me)
     /* [previous][next][first][last][top][bottom][index][help]  */
2411 {
2412     (void) me;
2413 
2414     g_slist_free_full (no_proxy, g_free);
2415 
2416     g_free (ftpfs_anonymous_passwd);
2417     g_free (ftpfs_proxy_host);
2418 }
2419 
2420 /* --------------------------------------------------------------------------------------------- */
2421 
2422 static void
2423 ftpfs_fill_names (struct vfs_class *me, fill_names_f func)
     /* [previous][next][first][last][top][bottom][index][help]  */
2424 {
2425     GList *iter;
2426 
2427     for (iter = VFS_SUBCLASS (me)->supers; iter != NULL; iter = g_list_next (iter))
2428     {
2429         const struct vfs_s_super *super = (const struct vfs_s_super *) iter->data;
2430         GString *name;
2431 
2432         name = vfs_path_element_build_pretty_path_str (super->path_element);
2433 
2434         func (name->str);
2435         g_string_free (name, TRUE);
2436     }
2437 }
2438 
2439 /* --------------------------------------------------------------------------------------------- */
2440 
2441 static keyword_t
2442 ftpfs_netrc_next (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
2443 {
2444     char *p;
2445     keyword_t i;
2446     static const char *const keywords[] = { "default", "machine",
2447         "login", "password", "passwd", "account", "macdef", NULL
2448     };
2449 
2450     while (TRUE)
2451     {
2452         netrcp = skip_separators (netrcp);
2453         if (*netrcp != '\n')
2454             break;
2455         netrcp++;
2456     }
2457     if (*netrcp == '\0')
2458         return NETRC_NONE;
2459 
2460     p = buffer;
2461     if (*netrcp == '"')
2462     {
2463         for (netrcp++; *netrcp != '"' && *netrcp != '\0'; netrcp++)
2464         {
2465             if (*netrcp == '\\')
2466                 netrcp++;
2467             *p++ = *netrcp;
2468         }
2469     }
2470     else
2471     {
2472         for (; *netrcp != '\0' && !whiteness (*netrcp) && *netrcp != ','; netrcp++)
2473         {
2474             if (*netrcp == '\\')
2475                 netrcp++;
2476             *p++ = *netrcp;
2477         }
2478     }
2479 
2480     *p = '\0';
2481     if (*buffer == '\0')
2482         return NETRC_NONE;
2483 
2484     for (i = NETRC_DEFAULT; keywords[i - 1] != NULL; i++)
2485         if (strcmp (keywords[i - 1], buffer) == 0)
2486             return i;
2487 
2488     return NETRC_UNKNOWN;
2489 }
2490 
2491 /* --------------------------------------------------------------------------------------------- */
2492 
2493 static gboolean
2494 ftpfs_netrc_bad_mode (const char *netrcname)
     /* [previous][next][first][last][top][bottom][index][help]  */
2495 {
2496     struct stat mystat;
2497 
2498     if (stat (netrcname, &mystat) >= 0 && (mystat.st_mode & 077) != 0)
2499     {
2500         static gboolean be_angry = TRUE;
2501 
2502         if (be_angry)
2503         {
2504             message (D_ERROR, MSG_ERROR,
2505                      _("~/.netrc file has incorrect mode\nRemove password or correct mode"));
2506             be_angry = FALSE;
2507         }
2508         return TRUE;
2509     }
2510 
2511     return FALSE;
2512 }
2513 
2514 /* --------------------------------------------------------------------------------------------- */
2515 /* Scan .netrc until we find matching "machine" or "default"
2516  * domain is used for additional matching
2517  * No search is done after "default" in compliance with "man netrc"
2518  * Return TRUE if found, FALSE otherwise */
2519 
2520 static gboolean
2521 ftpfs_find_machine (const char *host, const char *domain)
     /* [previous][next][first][last][top][bottom][index][help]  */
2522 {
2523     keyword_t keyword;
2524 
2525     if (host == NULL)
2526         host = "";
2527     if (domain == NULL)
2528         domain = "";
2529 
2530     while ((keyword = ftpfs_netrc_next ()) != NETRC_NONE)
2531     {
2532         if (keyword == NETRC_DEFAULT)
2533             return TRUE;
2534 
2535         if (keyword == NETRC_MACDEF)
2536         {
2537             /* Scan for an empty line, which concludes "macdef" */
2538             do
2539             {
2540                 while (*netrcp != '\0' && *netrcp != '\n')
2541                     netrcp++;
2542                 if (*netrcp != '\n')
2543                     break;
2544                 netrcp++;
2545             }
2546             while (*netrcp != '\0' && *netrcp != '\n');
2547 
2548             continue;
2549         }
2550 
2551         if (keyword != NETRC_MACHINE)
2552             continue;
2553 
2554         /* Take machine name */
2555         if (ftpfs_netrc_next () == NETRC_NONE)
2556             break;
2557 
2558         if (g_ascii_strcasecmp (host, buffer) != 0)
2559         {
2560             const char *host_domain;
2561 
2562             /* Try adding our domain to short names in .netrc */
2563             host_domain = strchr (host, '.');
2564             if (host_domain == NULL)
2565                 continue;
2566 
2567             /* Compare domain part */
2568             if (g_ascii_strcasecmp (host_domain, domain) != 0)
2569                 continue;
2570 
2571             /* Compare local part */
2572             if (g_ascii_strncasecmp (host, buffer, host_domain - host) != 0)
2573                 continue;
2574         }
2575 
2576         return TRUE;
2577     }
2578 
2579     /* end of .netrc */
2580     return FALSE;
2581 }
2582 
2583 /* --------------------------------------------------------------------------------------------- */
2584 /* Extract login and password from .netrc for the host.
2585  * pass may be NULL.
2586  * Returns TRUE for success, FALSE for error */
2587 
2588 static gboolean
2589 ftpfs_netrc_lookup (const char *host, char **login, char **pass)
     /* [previous][next][first][last][top][bottom][index][help]  */
2590 {
2591     char *netrcname;
2592     char *tmp_pass = NULL;
2593     char hostname[MAXHOSTNAMELEN];
2594     const char *domain;
2595     static struct rupcache
2596     {
2597         struct rupcache *next;
2598         char *host;
2599         char *login;
2600         char *pass;
2601     } *rup_cache = NULL, *rupp;
2602 
2603     /* Initialize *login and *pass */
2604     MC_PTR_FREE (*login);
2605     MC_PTR_FREE (*pass);
2606 
2607     /* Look up in the cache first */
2608     for (rupp = rup_cache; rupp != NULL; rupp = rupp->next)
2609         if (strcmp (host, rupp->host) == 0)
2610         {
2611             *login = g_strdup (rupp->login);
2612             *pass = g_strdup (rupp->pass);
2613             return TRUE;
2614         }
2615 
2616     /* Load current .netrc */
2617     netrcname = g_build_filename (mc_config_get_home_dir (), ".netrc", (char *) NULL);
2618     if (!g_file_get_contents (netrcname, &netrc, NULL, NULL))
2619     {
2620         g_free (netrcname);
2621         return TRUE;
2622     }
2623 
2624     netrcp = netrc;
2625 
2626     /* Find our own domain name */
2627     if (gethostname (hostname, sizeof (hostname)) < 0)
2628         *hostname = '\0';
2629 
2630     domain = strchr (hostname, '.');
2631     if (domain == NULL)
2632         domain = "";
2633 
2634     /* Scan for "default" and matching "machine" keywords */
2635     ftpfs_find_machine (host, domain);
2636 
2637     /* Scan for keywords following "default" and "machine" */
2638     while (TRUE)
2639     {
2640         keyword_t keyword;
2641 
2642         gboolean need_break = FALSE;
2643         keyword = ftpfs_netrc_next ();
2644 
2645         switch (keyword)
2646         {
2647         case NETRC_LOGIN:
2648             if (ftpfs_netrc_next () == NETRC_NONE)
2649             {
2650                 need_break = TRUE;
2651                 break;
2652             }
2653 
2654             /* We have another name already - should not happen */
2655             if (*login != NULL)
2656             {
2657                 need_break = TRUE;
2658                 break;
2659             }
2660 
2661             /* We have login name now */
2662             *login = g_strdup (buffer);
2663             break;
2664 
2665         case NETRC_PASSWORD:
2666         case NETRC_PASSWD:
2667             if (ftpfs_netrc_next () == NETRC_NONE)
2668             {
2669                 need_break = TRUE;
2670                 break;
2671             }
2672 
2673             /* Ignore unsafe passwords */
2674             if (*login != NULL &&
2675                 strcmp (*login, "anonymous") != 0 && strcmp (*login, "ftp") != 0
2676                 && ftpfs_netrc_bad_mode (netrcname))
2677             {
2678                 need_break = TRUE;
2679                 break;
2680             }
2681 
2682             /* Remember password.  pass may be NULL, so use tmp_pass */
2683             if (tmp_pass == NULL)
2684                 tmp_pass = g_strdup (buffer);
2685             break;
2686 
2687         case NETRC_ACCOUNT:
2688             /* "account" is followed by a token which we ignore */
2689             if (ftpfs_netrc_next () == NETRC_NONE)
2690             {
2691                 need_break = TRUE;
2692                 break;
2693             }
2694 
2695             /* Ignore account, but warn user anyways */
2696             ftpfs_netrc_bad_mode (netrcname);
2697             break;
2698 
2699         default:
2700             /* Unexpected keyword or end of file */
2701             need_break = TRUE;
2702             break;
2703         }
2704 
2705         if (need_break)
2706             break;
2707     }
2708 
2709     MC_PTR_FREE (netrc);
2710     g_free (netrcname);
2711 
2712     rupp = g_new (struct rupcache, 1);
2713     rupp->host = g_strdup (host);
2714     rupp->login = g_strdup (*login);
2715     rupp->pass = g_strdup (tmp_pass);
2716 
2717     rupp->next = rup_cache;
2718     rup_cache = rupp;
2719 
2720     *pass = tmp_pass;
2721 
2722     return TRUE;
2723 }
2724 
2725 /* --------------------------------------------------------------------------------------------- */
2726 /*** public functions ****************************************************************************/
2727 /* --------------------------------------------------------------------------------------------- */
2728 
2729 /** This routine is called as the last step in load_setup */
2730 void
2731 ftpfs_init_passwd (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
2732 {
2733     ftpfs_anonymous_passwd = load_anon_passwd ();
2734 
2735     if (ftpfs_anonymous_passwd == NULL)
2736     {
2737         /* If there is no anonymous ftp password specified
2738          * then we'll just use anonymous@
2739          * We don't send any other thing because:
2740          * - We want to remain anonymous
2741          * - We want to stop SPAM
2742          * - We don't want to let ftp sites to discriminate by the user,
2743          *   host or country.
2744          */
2745         ftpfs_anonymous_passwd = g_strdup ("anonymous@");
2746     }
2747 }
2748 
2749 /* --------------------------------------------------------------------------------------------- */
2750 
2751 void
2752 vfs_init_ftpfs (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
2753 {
2754     tcp_init ();
2755 
2756     vfs_init_subclass (&ftpfs_subclass, "ftpfs", VFSF_NOLINKS | VFSF_REMOTE | VFSF_USETMP, "ftp");
2757     vfs_ftpfs_ops->done = ftpfs_done;
2758     vfs_ftpfs_ops->fill_names = ftpfs_fill_names;
2759     vfs_ftpfs_ops->stat = ftpfs_stat;
2760     vfs_ftpfs_ops->lstat = ftpfs_lstat;
2761     vfs_ftpfs_ops->fstat = ftpfs_fstat;
2762     vfs_ftpfs_ops->chmod = ftpfs_chmod;
2763     vfs_ftpfs_ops->chown = ftpfs_chown;
2764     vfs_ftpfs_ops->unlink = ftpfs_unlink;
2765     vfs_ftpfs_ops->rename = ftpfs_rename;
2766     vfs_ftpfs_ops->mkdir = ftpfs_mkdir;
2767     vfs_ftpfs_ops->rmdir = ftpfs_rmdir;
2768     vfs_ftpfs_ops->ctl = ftpfs_ctl;
2769     ftpfs_subclass.archive_same = ftpfs_archive_same;
2770     ftpfs_subclass.new_archive = ftpfs_new_archive;
2771     ftpfs_subclass.open_archive = ftpfs_open_archive;
2772     ftpfs_subclass.free_archive = ftpfs_free_archive;
2773     ftpfs_subclass.fh_new = ftpfs_fh_new;
2774     ftpfs_subclass.fh_open = ftpfs_fh_open;
2775     ftpfs_subclass.fh_close = ftpfs_fh_close;
2776     ftpfs_subclass.dir_load = ftpfs_dir_load;
2777     ftpfs_subclass.file_store = ftpfs_file_store;
2778     ftpfs_subclass.linear_start = ftpfs_linear_start;
2779     ftpfs_subclass.linear_read = ftpfs_linear_read;
2780     ftpfs_subclass.linear_close = ftpfs_linear_close;
2781     vfs_register_class (vfs_ftpfs_ops);
2782 }
2783 
2784 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */