root/src/vfs/ftpfs/ftpfs.c

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

DEFINITIONS

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

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

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