Manual pages: mcmcdiffmceditmcview

root/src/vfs/ftpfs/ftpfs.c

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

DEFINITIONS

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

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

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