Manual pages: mcmcdiffmceditmcview

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

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