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

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