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

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