Manual pages: mc • mcdiff • mcedit • mcview

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. ftpfs_utime
  60. vfs_init_ftpfs

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

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