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-2021
   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_super *super = dir->super;
1729     ftp_super_t *ftp_super = FTP_SUPER (super);
1730     int sock;
1731     char lc_buffer[BUF_8K];
1732     int res;
1733     gboolean cd_first;
1734     GSList *dirlist = NULL;
1735     GSList *entlist;
1736     GSList *iter;
1737     int err_count = 0;
1738 
1739     cd_first = ftpfs_first_cd_then_ls || (ftp_super->strict == RFC_STRICT)
1740         || (strchr (remote_path, ' ') != NULL);
1741 
1742   again:
1743     vfs_print_message (_("ftpfs: Reading FTP directory %s... %s%s"),
1744                        remote_path,
1745                        ftp_super->strict ==
1746                        RFC_STRICT ? _("(strict rfc959)") : "", cd_first ? _("(chdir first)") : "");
1747 
1748     if (cd_first && ftpfs_chdir_internal (me, super, remote_path) != COMPLETE)
1749     {
1750         ftpfs_errno = ENOENT;
1751         vfs_print_message ("%s", _("ftpfs: CWD failed."));
1752         return (-1);
1753     }
1754 
1755     dir->timestamp = g_get_real_time () + ftpfs_directory_timeout * G_USEC_PER_SEC;
1756 
1757     if (ftp_super->strict == RFC_STRICT)
1758         sock = ftpfs_open_data_connection (me, super, "LIST", 0, TYPE_ASCII, 0);
1759     else if (cd_first)
1760         /* Dirty hack to avoid autoprepending / to . */
1761         /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */
1762         sock = ftpfs_open_data_connection (me, super, "LIST -la", 0, TYPE_ASCII, 0);
1763     else
1764     {
1765         char *path;
1766 
1767         /* Trailing "/." is necessary if remote_path is a symlink */
1768         path = mc_build_filename (remote_path, ".", (char *) NULL);
1769         sock = ftpfs_open_data_connection (me, super, "LIST -la", path, TYPE_ASCII, 0);
1770         g_free (path);
1771     }
1772 
1773     if (sock == -1)
1774     {
1775       fallback:
1776         if (ftp_super->strict == RFC_AUTODETECT)
1777         {
1778             /* It's our first attempt to get a directory listing from this
1779                server (UNIX style LIST command) */
1780             ftp_super->strict = RFC_STRICT;
1781             /* I hate goto, but recursive call needs another 8K on stack */
1782             /* return ftpfs_dir_load (me, dir, remote_path); */
1783             cd_first = TRUE;
1784             goto again;
1785         }
1786 
1787         vfs_print_message ("%s", _("ftpfs: failed; nowhere to fallback to"));
1788         ERRNOR (EACCES, -1);
1789     }
1790 
1791     /* read full directory list, then parse it */
1792     while ((res = vfs_s_get_line_interruptible (me, lc_buffer, sizeof (lc_buffer), sock)) != 0)
1793     {
1794         if (res == EINTR)
1795         {
1796             me->verrno = ECONNRESET;
1797             close (sock);
1798             ftp_super->ctl_connection_busy = FALSE;
1799             ftpfs_get_reply (me, ftp_super->sock, NULL, 0);
1800             g_slist_free_full (dirlist, g_free);
1801             vfs_print_message (_("%s: failure"), me->name);
1802             return (-1);
1803         }
1804 
1805         if (me->logfile != NULL)
1806         {
1807             fputs (lc_buffer, me->logfile);
1808             fputs ("\n", me->logfile);
1809             fflush (me->logfile);
1810         }
1811 
1812         dirlist = g_slist_prepend (dirlist, g_strdup (lc_buffer));
1813     }
1814 
1815     close (sock);
1816     ftp_super->ctl_connection_busy = FALSE;
1817     me->verrno = E_REMOTE;
1818     if ((ftpfs_get_reply (me, ftp_super->sock, NULL, 0) != COMPLETE))
1819     {
1820         g_slist_free_full (dirlist, g_free);
1821         goto fallback;
1822     }
1823 
1824     if (dirlist == NULL && !cd_first)
1825     {
1826         /* The LIST command may produce an empty output. In such scenario
1827            it is not clear whether this is caused by  'remote_path' being
1828            a non-existent path or for some other reason (listing emtpy
1829            directory without the -a option, non-readable directory, etc.).
1830 
1831            Since 'dir_load' is a crucial method, when it comes to determine
1832            whether a given path is a _directory_, the code must try its best
1833            to determine the type of 'remote_path'. The only reliable way to
1834            achieve this is trough issuing a CWD command. */
1835 
1836         cd_first = TRUE;
1837         goto again;
1838     }
1839 
1840     /* parse server's reply */
1841     dirlist = g_slist_reverse (dirlist);        /* restore order */
1842     entlist = ftpfs_parse_long_list (me, dir, dirlist, &err_count);
1843     g_slist_free_full (dirlist, g_free);
1844 
1845     for (iter = entlist; iter != NULL; iter = g_slist_next (iter))
1846         vfs_s_insert_entry (me, dir, VFS_ENTRY (iter->data));
1847 
1848     g_slist_free (entlist);
1849 
1850     if (ftp_super->strict == RFC_AUTODETECT)
1851         ftp_super->strict = RFC_DARING;
1852 
1853     vfs_print_message (_("%s: done."), me->name);
1854     return 0;
1855 }
1856 
1857 /* --------------------------------------------------------------------------------------------- */
1858 
1859 static int
1860 ftpfs_file_store (struct vfs_class *me, vfs_file_handler_t * fh, char *name, char *localname)
     /* [previous][next][first][last][top][bottom][index][help]  */
1861 {
1862     struct vfs_s_super *super = VFS_FILE_HANDLER_SUPER (fh);
1863     ftp_super_t *ftp_super = FTP_SUPER (super);
1864     ftp_file_handler_t *ftp = FTP_FILE_HANDLER (fh);
1865 
1866     int h, sock;
1867     off_t n_stored = 0;
1868 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1869     struct linger li;
1870 #else
1871     int flag_one = 1;
1872 #endif
1873     char lc_buffer[BUF_8K];
1874     struct stat s;
1875     char *w_buf;
1876 
1877     h = open (localname, O_RDONLY);
1878     if (h == -1)
1879         ERRNOR (EIO, -1);
1880 
1881     if (fstat (h, &s) == -1)
1882     {
1883         close (h);
1884         return (-1);
1885     }
1886 
1887     sock =
1888         ftpfs_open_data_connection (me, super, ftp->append ? "APPE" : "STOR", name, TYPE_BINARY, 0);
1889     if (sock < 0)
1890     {
1891         close (h);
1892         return (-1);
1893     }
1894 #ifdef HAVE_STRUCT_LINGER_L_LINGER
1895     li.l_onoff = 1;
1896     li.l_linger = 120;
1897     setsockopt (sock, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof (li));
1898 #else
1899     setsockopt (sock, SOL_SOCKET, SO_LINGER, &flag_one, sizeof (flag_one));
1900 #endif
1901 
1902     tty_enable_interrupt_key ();
1903     while (TRUE)
1904     {
1905         ssize_t n_read, n_written;
1906 
1907         while ((n_read = read (h, lc_buffer, sizeof (lc_buffer))) == -1)
1908         {
1909             if (errno != EINTR)
1910             {
1911                 ftpfs_errno = errno;
1912                 goto error_return;
1913             }
1914             if (tty_got_interrupt ())
1915             {
1916                 ftpfs_errno = EINTR;
1917                 goto error_return;
1918             }
1919         }
1920         if (n_read == 0)
1921             break;
1922 
1923         n_stored += n_read;
1924         w_buf = lc_buffer;
1925 
1926         while ((n_written = write (sock, w_buf, n_read)) != n_read)
1927         {
1928             if (n_written == -1)
1929             {
1930                 if (errno == EINTR && !tty_got_interrupt ())
1931                     continue;
1932 
1933                 ftpfs_errno = errno;
1934                 goto error_return;
1935             }
1936 
1937             w_buf += n_written;
1938             n_read -= n_written;
1939         }
1940 
1941         vfs_print_message ("%s: %" PRIuMAX "/%" PRIuMAX,
1942                            _("ftpfs: storing file"), (uintmax_t) n_stored, (uintmax_t) s.st_size);
1943     }
1944     tty_disable_interrupt_key ();
1945 
1946     close (sock);
1947     ftp_super->ctl_connection_busy = FALSE;
1948     close (h);
1949 
1950     if (ftpfs_get_reply (me, ftp_super->sock, NULL, 0) != COMPLETE)
1951         ERRNOR (EIO, -1);
1952     return 0;
1953 
1954   error_return:
1955     tty_disable_interrupt_key ();
1956     close (sock);
1957     ftp_super->ctl_connection_busy = FALSE;
1958     close (h);
1959 
1960     ftpfs_get_reply (me, ftp_super->sock, NULL, 0);
1961     return (-1);
1962 }
1963 
1964 /* --------------------------------------------------------------------------------------------- */
1965 
1966 static int
1967 ftpfs_linear_start (struct vfs_class *me, vfs_file_handler_t * fh, off_t offset)
     /* [previous][next][first][last][top][bottom][index][help]  */
1968 {
1969     char *name;
1970 
1971     name = vfs_s_fullpath (me, fh->ino);
1972     if (name == NULL)
1973         return 0;
1974 
1975     FH_SOCK =
1976         ftpfs_open_data_connection (me, VFS_FILE_HANDLER_SUPER (fh), "RETR", name, TYPE_BINARY,
1977                                     offset);
1978     g_free (name);
1979     if (FH_SOCK == -1)
1980         ERRNOR (EACCES, 0);
1981 
1982     fh->linear = LS_LINEAR_OPEN;
1983     FTP_FILE_HANDLER (fh)->append = FALSE;
1984     return 1;
1985 }
1986 
1987 /* --------------------------------------------------------------------------------------------- */
1988 
1989 static ssize_t
1990 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]  */
1991 {
1992     ssize_t n;
1993     struct vfs_s_super *super = VFS_FILE_HANDLER_SUPER (fh);
1994 
1995     while ((n = read (FH_SOCK, buf, len)) < 0)
1996     {
1997         if ((errno == EINTR) && !tty_got_interrupt ())
1998             continue;
1999         break;
2000     }
2001 
2002     if (n < 0)
2003         ftpfs_linear_abort (me, fh);
2004     else if (n == 0)
2005     {
2006         FTP_SUPER (super)->ctl_connection_busy = FALSE;
2007         close (FH_SOCK);
2008         FH_SOCK = -1;
2009         if ((ftpfs_get_reply (me, FTP_SUPER (super)->sock, NULL, 0) != COMPLETE))
2010             ERRNOR (E_REMOTE, -1);
2011         return 0;
2012     }
2013 
2014     ERRNOR (errno, n);
2015 }
2016 
2017 /* --------------------------------------------------------------------------------------------- */
2018 
2019 static void
2020 ftpfs_linear_close (struct vfs_class *me, vfs_file_handler_t * fh)
     /* [previous][next][first][last][top][bottom][index][help]  */
2021 {
2022     if (FH_SOCK != -1)
2023         ftpfs_linear_abort (me, fh);
2024 }
2025 
2026 /* --------------------------------------------------------------------------------------------- */
2027 
2028 static int
2029 ftpfs_ctl (void *fh, int ctlop, void *arg)
     /* [previous][next][first][last][top][bottom][index][help]  */
2030 {
2031     (void) arg;
2032 
2033     switch (ctlop)
2034     {
2035     case VFS_CTL_IS_NOTREADY:
2036         {
2037             vfs_file_handler_t *file = VFS_FILE_HANDLER (fh);
2038             int v;
2039 
2040             if (file->linear == LS_NOT_LINEAR)
2041                 vfs_die ("You may not do this");
2042             if (file->linear == LS_LINEAR_CLOSED || file->linear == LS_LINEAR_PREOPEN)
2043                 return 0;
2044 
2045             v = vfs_s_select_on_two (FH_SOCK, 0);
2046             return (((v < 0) && (errno == EINTR)) || v == 0) ? 1 : 0;
2047         }
2048     default:
2049         return 0;
2050     }
2051 }
2052 
2053 /* --------------------------------------------------------------------------------------------- */
2054 
2055 static int
2056 ftpfs_send_command (const vfs_path_t * vpath, const char *cmd, int flags)
     /* [previous][next][first][last][top][bottom][index][help]  */
2057 {
2058     const char *rpath;
2059     char *p;
2060     struct vfs_s_super *super;
2061     int r;
2062     const vfs_path_element_t *path_element;
2063     gboolean flush_directory_cache = (flags & OPT_FLUSH) != 0;
2064 
2065     path_element = vfs_path_get_by_index (vpath, -1);
2066 
2067     rpath = vfs_s_get_path (vpath, &super, 0);
2068     if (rpath == NULL)
2069         return (-1);
2070 
2071     p = ftpfs_translate_path (path_element->class, super, rpath);
2072     r = ftpfs_command (path_element->class, super, WAIT_REPLY, cmd, p);
2073     g_free (p);
2074     vfs_stamp_create (vfs_ftpfs_ops, super);
2075     if ((flags & OPT_IGNORE_ERROR) != 0)
2076         r = COMPLETE;
2077     if (r != COMPLETE)
2078     {
2079         path_element->class->verrno = EPERM;
2080         return (-1);
2081     }
2082     if (flush_directory_cache)
2083         vfs_s_invalidate (path_element->class, super);
2084     return 0;
2085 }
2086 
2087 /* --------------------------------------------------------------------------------------------- */
2088 
2089 static int
2090 ftpfs_stat (const vfs_path_t * vpath, struct stat *buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
2091 {
2092     int ret;
2093 
2094     ret = vfs_s_stat (vpath, buf);
2095     ftpfs_set_blksize (buf);
2096     return ret;
2097 }
2098 
2099 /* --------------------------------------------------------------------------------------------- */
2100 
2101 static int
2102 ftpfs_lstat (const vfs_path_t * vpath, struct stat *buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
2103 {
2104     int ret;
2105 
2106     ret = vfs_s_lstat (vpath, buf);
2107     ftpfs_set_blksize (buf);
2108     return ret;
2109 }
2110 
2111 /* --------------------------------------------------------------------------------------------- */
2112 
2113 static int
2114 ftpfs_fstat (void *vfs_info, struct stat *buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
2115 {
2116     int ret;
2117 
2118     ret = vfs_s_fstat (vfs_info, buf);
2119     ftpfs_set_blksize (buf);
2120     return ret;
2121 }
2122 
2123 /* --------------------------------------------------------------------------------------------- */
2124 
2125 static int
2126 ftpfs_chmod (const vfs_path_t * vpath, mode_t mode)
     /* [previous][next][first][last][top][bottom][index][help]  */
2127 {
2128     char buf[BUF_SMALL];
2129     int ret;
2130 
2131     g_snprintf (buf, sizeof (buf), "SITE CHMOD %4.4o /%%s", (unsigned int) (mode & 07777));
2132     ret = ftpfs_send_command (vpath, buf, OPT_FLUSH);
2133     return ftpfs_ignore_chattr_errors ? 0 : ret;
2134 }
2135 
2136 /* --------------------------------------------------------------------------------------------- */
2137 
2138 static int
2139 ftpfs_chown (const vfs_path_t * vpath, uid_t owner, gid_t group)
     /* [previous][next][first][last][top][bottom][index][help]  */
2140 {
2141 #if 0
2142     (void) vpath;
2143     (void) owner;
2144     (void) group;
2145 
2146     ftpfs_errno = EPERM;
2147     return (-1);
2148 #else
2149     /* Everyone knows it is not possible to chown remotely, so why bother them.
2150        If someone's root, then copy/move will always try to chown it... */
2151     (void) vpath;
2152     (void) owner;
2153     (void) group;
2154     return 0;
2155 #endif
2156 }
2157 
2158 /* --------------------------------------------------------------------------------------------- */
2159 
2160 static int
2161 ftpfs_unlink (const vfs_path_t * vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
2162 {
2163     return ftpfs_send_command (vpath, "DELE /%s", OPT_FLUSH);
2164 }
2165 
2166 /* --------------------------------------------------------------------------------------------- */
2167 
2168 /* Return TRUE if path is the same directory as the one we are in now */
2169 static gboolean
2170 ftpfs_is_same_dir (struct vfs_class *me, struct vfs_s_super *super, const char *path)
     /* [previous][next][first][last][top][bottom][index][help]  */
2171 {
2172     (void) me;
2173 
2174     return (FTP_SUPER (super)->current_dir != NULL
2175             && strcmp (path, FTP_SUPER (super)->current_dir) == 0);
2176 }
2177 
2178 /* --------------------------------------------------------------------------------------------- */
2179 
2180 static int
2181 ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path)
     /* [previous][next][first][last][top][bottom][index][help]  */
2182 {
2183     ftp_super_t *ftp_super = FTP_SUPER (super);
2184     int r;
2185     char *p;
2186 
2187     if (!ftp_super->cwd_deferred && ftpfs_is_same_dir (me, super, remote_path))
2188         return COMPLETE;
2189 
2190     p = ftpfs_translate_path (me, super, remote_path);
2191     r = ftpfs_command (me, super, WAIT_REPLY, "CWD /%s", p);
2192     g_free (p);
2193 
2194     if (r != COMPLETE)
2195         ftpfs_errno = EIO;
2196     else
2197     {
2198         g_free (ftp_super->current_dir);
2199         ftp_super->current_dir = g_strdup (remote_path);
2200         ftp_super->cwd_deferred = FALSE;
2201     }
2202     return r;
2203 }
2204 
2205 /* --------------------------------------------------------------------------------------------- */
2206 
2207 static int
2208 ftpfs_rename (const vfs_path_t * vpath1, const vfs_path_t * vpath2)
     /* [previous][next][first][last][top][bottom][index][help]  */
2209 {
2210     ftpfs_send_command (vpath1, "RNFR /%s", OPT_FLUSH);
2211     return ftpfs_send_command (vpath2, "RNTO /%s", OPT_FLUSH);
2212 }
2213 
2214 /* --------------------------------------------------------------------------------------------- */
2215 
2216 static int
2217 ftpfs_mkdir (const vfs_path_t * vpath, mode_t mode)
     /* [previous][next][first][last][top][bottom][index][help]  */
2218 {
2219     (void) mode;                /* FIXME: should be used */
2220 
2221     return ftpfs_send_command (vpath, "MKD /%s", OPT_FLUSH);
2222 }
2223 
2224 /* --------------------------------------------------------------------------------------------- */
2225 
2226 static int
2227 ftpfs_rmdir (const vfs_path_t * vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
2228 {
2229     return ftpfs_send_command (vpath, "RMD /%s", OPT_FLUSH);
2230 }
2231 
2232 /* --------------------------------------------------------------------------------------------- */
2233 
2234 static vfs_file_handler_t *
2235 ftpfs_fh_new (struct vfs_s_inode *ino, gboolean changed)
     /* [previous][next][first][last][top][bottom][index][help]  */
2236 {
2237     ftp_file_handler_t *fh;
2238 
2239     fh = g_new0 (ftp_file_handler_t, 1);
2240     vfs_s_init_fh (VFS_FILE_HANDLER (fh), ino, changed);
2241     fh->sock = -1;
2242 
2243     return VFS_FILE_HANDLER (fh);
2244 }
2245 
2246 /* --------------------------------------------------------------------------------------------- */
2247 
2248 static int
2249 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]  */
2250 {
2251     ftp_file_handler_t *ftp = FTP_FILE_HANDLER (fh);
2252 
2253     (void) mode;
2254 
2255     /* File will be written only, so no need to retrieve it from ftp server */
2256     if (((flags & O_WRONLY) == O_WRONLY) && ((flags & (O_RDONLY | O_RDWR)) == 0))
2257     {
2258 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2259         struct linger li;
2260 #else
2261         int li = 1;
2262 #endif
2263         char *name;
2264 
2265         /* ftpfs_linear_start() called, so data will be written
2266          * to local temporary file and stored to ftp server
2267          * by vfs_s_close later
2268          */
2269         if (FTP_SUPER (VFS_FILE_HANDLER_SUPER (fh))->ctl_connection_busy)
2270         {
2271             if (fh->ino->localname == NULL)
2272             {
2273                 vfs_path_t *vpath;
2274                 int handle;
2275 
2276                 handle = vfs_mkstemps (&vpath, me->name, fh->ino->ent->name);
2277                 if (handle == -1)
2278                     return (-1);
2279 
2280                 close (handle);
2281                 fh->ino->localname = vfs_path_free (vpath, FALSE);
2282                 ftp->append = (flags & O_APPEND) != 0;
2283             }
2284             return 0;
2285         }
2286         name = vfs_s_fullpath (me, fh->ino);
2287         if (name == NULL)
2288             return (-1);
2289 
2290         fh->handle =
2291             ftpfs_open_data_connection (me, VFS_FILE_HANDLER_SUPER (fh),
2292                                         (flags & O_APPEND) != 0 ? "APPE" : "STOR", name,
2293                                         TYPE_BINARY, 0);
2294         g_free (name);
2295 
2296         if (fh->handle < 0)
2297             return (-1);
2298 
2299 #ifdef HAVE_STRUCT_LINGER_L_LINGER
2300         li.l_onoff = 1;
2301         li.l_linger = 120;
2302 #endif
2303         setsockopt (fh->handle, SOL_SOCKET, SO_LINGER, &li, sizeof (li));
2304 
2305         if (fh->ino->localname != NULL)
2306         {
2307             unlink (fh->ino->localname);
2308             MC_PTR_FREE (fh->ino->localname);
2309         }
2310         return 0;
2311     }
2312 
2313     if (fh->ino->localname == NULL && vfs_s_retrieve_file (me, fh->ino) == -1)
2314         return (-1);
2315 
2316     if (fh->ino->localname == NULL)
2317         vfs_die ("retrieve_file failed to fill in localname");
2318     return 0;
2319 }
2320 
2321 /* --------------------------------------------------------------------------------------------- */
2322 
2323 static int
2324 ftpfs_fh_close (struct vfs_class *me, vfs_file_handler_t * fh)
     /* [previous][next][first][last][top][bottom][index][help]  */
2325 {
2326     if (fh->handle != -1 && fh->ino->localname == NULL)
2327     {
2328         ftp_super_t *ftp = FTP_SUPER (VFS_FILE_HANDLER_SUPER (fh));
2329 
2330         close (fh->handle);
2331         fh->handle = -1;
2332         ftp->ctl_connection_busy = FALSE;
2333         /* File is stored to destination already, so
2334          * we prevent VFS_SUBCLASS (me)->ftpfs_file_store() call from vfs_s_close ()
2335          */
2336         fh->changed = FALSE;
2337         if (ftpfs_get_reply (me, ftp->sock, NULL, 0) != COMPLETE)
2338             ERRNOR (EIO, -1);
2339         vfs_s_invalidate (me, VFS_FILE_HANDLER_SUPER (fh));
2340     }
2341 
2342     return 0;
2343 }
2344 
2345 /* --------------------------------------------------------------------------------------------- */
2346 
2347 static void
2348 ftpfs_done (struct vfs_class *me)
     /* [previous][next][first][last][top][bottom][index][help]  */
2349 {
2350     (void) me;
2351 
2352     g_slist_free_full (no_proxy, g_free);
2353 
2354     g_free (ftpfs_anonymous_passwd);
2355     g_free (ftpfs_proxy_host);
2356 }
2357 
2358 /* --------------------------------------------------------------------------------------------- */
2359 
2360 static void
2361 ftpfs_fill_names (struct vfs_class *me, fill_names_f func)
     /* [previous][next][first][last][top][bottom][index][help]  */
2362 {
2363     GList *iter;
2364 
2365     for (iter = VFS_SUBCLASS (me)->supers; iter != NULL; iter = g_list_next (iter))
2366     {
2367         const struct vfs_s_super *super = (const struct vfs_s_super *) iter->data;
2368         char *name;
2369 
2370         name = vfs_path_element_build_pretty_path_str (super->path_element);
2371 
2372         func (name);
2373         g_free (name);
2374     }
2375 }
2376 
2377 /* --------------------------------------------------------------------------------------------- */
2378 
2379 static keyword_t
2380 ftpfs_netrc_next (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
2381 {
2382     char *p;
2383     keyword_t i;
2384     static const char *const keywords[] = { "default", "machine",
2385         "login", "password", "passwd", "account", "macdef", NULL
2386     };
2387 
2388     while (TRUE)
2389     {
2390         netrcp = skip_separators (netrcp);
2391         if (*netrcp != '\n')
2392             break;
2393         netrcp++;
2394     }
2395     if (*netrcp == '\0')
2396         return NETRC_NONE;
2397 
2398     p = buffer;
2399     if (*netrcp == '"')
2400     {
2401         for (netrcp++; *netrcp != '"' && *netrcp != '\0'; netrcp++)
2402         {
2403             if (*netrcp == '\\')
2404                 netrcp++;
2405             *p++ = *netrcp;
2406         }
2407     }
2408     else
2409     {
2410         for (; *netrcp != '\0' && !whiteness (*netrcp) && *netrcp != ','; netrcp++)
2411         {
2412             if (*netrcp == '\\')
2413                 netrcp++;
2414             *p++ = *netrcp;
2415         }
2416     }
2417 
2418     *p = '\0';
2419     if (*buffer == '\0')
2420         return NETRC_NONE;
2421 
2422     for (i = NETRC_DEFAULT; keywords[i - 1] != NULL; i++)
2423         if (strcmp (keywords[i - 1], buffer) == 0)
2424             return i;
2425 
2426     return NETRC_UNKNOWN;
2427 }
2428 
2429 /* --------------------------------------------------------------------------------------------- */
2430 
2431 static gboolean
2432 ftpfs_netrc_bad_mode (const char *netrcname)
     /* [previous][next][first][last][top][bottom][index][help]  */
2433 {
2434     struct stat mystat;
2435 
2436     if (stat (netrcname, &mystat) >= 0 && (mystat.st_mode & 077) != 0)
2437     {
2438         static gboolean be_angry = TRUE;
2439 
2440         if (be_angry)
2441         {
2442             message (D_ERROR, MSG_ERROR,
2443                      _("~/.netrc file has incorrect mode\nRemove password or correct mode"));
2444             be_angry = FALSE;
2445         }
2446         return TRUE;
2447     }
2448 
2449     return FALSE;
2450 }
2451 
2452 /* --------------------------------------------------------------------------------------------- */
2453 /* Scan .netrc until we find matching "machine" or "default"
2454  * domain is used for additional matching
2455  * No search is done after "default" in compliance with "man netrc"
2456  * Return TRUE if found, FALSE otherwise */
2457 
2458 static gboolean
2459 ftpfs_find_machine (const char *host, const char *domain)
     /* [previous][next][first][last][top][bottom][index][help]  */
2460 {
2461     keyword_t keyword;
2462 
2463     if (host == NULL)
2464         host = "";
2465     if (domain == NULL)
2466         domain = "";
2467 
2468     while ((keyword = ftpfs_netrc_next ()) != NETRC_NONE)
2469     {
2470         if (keyword == NETRC_DEFAULT)
2471             return TRUE;
2472 
2473         if (keyword == NETRC_MACDEF)
2474         {
2475             /* Scan for an empty line, which concludes "macdef" */
2476             do
2477             {
2478                 while (*netrcp != '\0' && *netrcp != '\n')
2479                     netrcp++;
2480                 if (*netrcp != '\n')
2481                     break;
2482                 netrcp++;
2483             }
2484             while (*netrcp != '\0' && *netrcp != '\n');
2485 
2486             continue;
2487         }
2488 
2489         if (keyword != NETRC_MACHINE)
2490             continue;
2491 
2492         /* Take machine name */
2493         if (ftpfs_netrc_next () == NETRC_NONE)
2494             break;
2495 
2496         if (g_ascii_strcasecmp (host, buffer) != 0)
2497         {
2498             const char *host_domain;
2499 
2500             /* Try adding our domain to short names in .netrc */
2501             host_domain = strchr (host, '.');
2502             if (host_domain == NULL)
2503                 continue;
2504 
2505             /* Compare domain part */
2506             if (g_ascii_strcasecmp (host_domain, domain) != 0)
2507                 continue;
2508 
2509             /* Compare local part */
2510             if (g_ascii_strncasecmp (host, buffer, host_domain - host) != 0)
2511                 continue;
2512         }
2513 
2514         return TRUE;
2515     }
2516 
2517     /* end of .netrc */
2518     return FALSE;
2519 }
2520 
2521 /* --------------------------------------------------------------------------------------------- */
2522 /* Extract login and password from .netrc for the host.
2523  * pass may be NULL.
2524  * Returns TRUE for success, FALSE for error */
2525 
2526 static gboolean
2527 ftpfs_netrc_lookup (const char *host, char **login, char **pass)
     /* [previous][next][first][last][top][bottom][index][help]  */
2528 {
2529     char *netrcname;
2530     char *tmp_pass = NULL;
2531     char hostname[MAXHOSTNAMELEN];
2532     const char *domain;
2533     static struct rupcache
2534     {
2535         struct rupcache *next;
2536         char *host;
2537         char *login;
2538         char *pass;
2539     } *rup_cache = NULL, *rupp;
2540 
2541     /* Initialize *login and *pass */
2542     MC_PTR_FREE (*login);
2543     MC_PTR_FREE (*pass);
2544 
2545     /* Look up in the cache first */
2546     for (rupp = rup_cache; rupp != NULL; rupp = rupp->next)
2547         if (strcmp (host, rupp->host) == 0)
2548         {
2549             *login = g_strdup (rupp->login);
2550             *pass = g_strdup (rupp->pass);
2551             return TRUE;
2552         }
2553 
2554     /* Load current .netrc */
2555     netrcname = g_build_filename (mc_config_get_home_dir (), ".netrc", (char *) NULL);
2556     if (!g_file_get_contents (netrcname, &netrc, NULL, NULL))
2557     {
2558         g_free (netrcname);
2559         return TRUE;
2560     }
2561 
2562     netrcp = netrc;
2563 
2564     /* Find our own domain name */
2565     if (gethostname (hostname, sizeof (hostname)) < 0)
2566         *hostname = '\0';
2567 
2568     domain = strchr (hostname, '.');
2569     if (domain == NULL)
2570         domain = "";
2571 
2572     /* Scan for "default" and matching "machine" keywords */
2573     ftpfs_find_machine (host, domain);
2574 
2575     /* Scan for keywords following "default" and "machine" */
2576     while (TRUE)
2577     {
2578         keyword_t keyword;
2579 
2580         gboolean need_break = FALSE;
2581         keyword = ftpfs_netrc_next ();
2582 
2583         switch (keyword)
2584         {
2585         case NETRC_LOGIN:
2586             if (ftpfs_netrc_next () == NETRC_NONE)
2587             {
2588                 need_break = TRUE;
2589                 break;
2590             }
2591 
2592             /* We have another name already - should not happen */
2593             if (*login != NULL)
2594             {
2595                 need_break = TRUE;
2596                 break;
2597             }
2598 
2599             /* We have login name now */
2600             *login = g_strdup (buffer);
2601             break;
2602 
2603         case NETRC_PASSWORD:
2604         case NETRC_PASSWD:
2605             if (ftpfs_netrc_next () == NETRC_NONE)
2606             {
2607                 need_break = TRUE;
2608                 break;
2609             }
2610 
2611             /* Ignore unsafe passwords */
2612             if (*login != NULL &&
2613                 strcmp (*login, "anonymous") != 0 && strcmp (*login, "ftp") != 0
2614                 && ftpfs_netrc_bad_mode (netrcname))
2615             {
2616                 need_break = TRUE;
2617                 break;
2618             }
2619 
2620             /* Remember password.  pass may be NULL, so use tmp_pass */
2621             if (tmp_pass == NULL)
2622                 tmp_pass = g_strdup (buffer);
2623             break;
2624 
2625         case NETRC_ACCOUNT:
2626             /* "account" is followed by a token which we ignore */
2627             if (ftpfs_netrc_next () == NETRC_NONE)
2628             {
2629                 need_break = TRUE;
2630                 break;
2631             }
2632 
2633             /* Ignore account, but warn user anyways */
2634             ftpfs_netrc_bad_mode (netrcname);
2635             break;
2636 
2637         default:
2638             /* Unexpected keyword or end of file */
2639             need_break = TRUE;
2640             break;
2641         }
2642 
2643         if (need_break)
2644             break;
2645     }
2646 
2647     MC_PTR_FREE (netrc);
2648     g_free (netrcname);
2649 
2650     rupp = g_new (struct rupcache, 1);
2651     rupp->host = g_strdup (host);
2652     rupp->login = g_strdup (*login);
2653     rupp->pass = g_strdup (tmp_pass);
2654 
2655     rupp->next = rup_cache;
2656     rup_cache = rupp;
2657 
2658     *pass = tmp_pass;
2659 
2660     return TRUE;
2661 }
2662 
2663 /* --------------------------------------------------------------------------------------------- */
2664 /*** public functions ****************************************************************************/
2665 /* --------------------------------------------------------------------------------------------- */
2666 
2667 /** This routine is called as the last step in load_setup */
2668 void
2669 ftpfs_init_passwd (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
2670 {
2671     ftpfs_anonymous_passwd = load_anon_passwd ();
2672 
2673     if (ftpfs_anonymous_passwd == NULL)
2674     {
2675         /* If there is no anonymous ftp password specified
2676          * then we'll just use anonymous@
2677          * We don't send any other thing because:
2678          * - We want to remain anonymous
2679          * - We want to stop SPAM
2680          * - We don't want to let ftp sites to discriminate by the user,
2681          *   host or country.
2682          */
2683         ftpfs_anonymous_passwd = g_strdup ("anonymous@");
2684     }
2685 }
2686 
2687 /* --------------------------------------------------------------------------------------------- */
2688 
2689 void
2690 vfs_init_ftpfs (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
2691 {
2692     tcp_init ();
2693 
2694     vfs_init_subclass (&ftpfs_subclass, "ftpfs", VFSF_NOLINKS | VFSF_REMOTE | VFSF_USETMP, "ftp");
2695     vfs_ftpfs_ops->done = ftpfs_done;
2696     vfs_ftpfs_ops->fill_names = ftpfs_fill_names;
2697     vfs_ftpfs_ops->stat = ftpfs_stat;
2698     vfs_ftpfs_ops->lstat = ftpfs_lstat;
2699     vfs_ftpfs_ops->fstat = ftpfs_fstat;
2700     vfs_ftpfs_ops->chmod = ftpfs_chmod;
2701     vfs_ftpfs_ops->chown = ftpfs_chown;
2702     vfs_ftpfs_ops->unlink = ftpfs_unlink;
2703     vfs_ftpfs_ops->rename = ftpfs_rename;
2704     vfs_ftpfs_ops->mkdir = ftpfs_mkdir;
2705     vfs_ftpfs_ops->rmdir = ftpfs_rmdir;
2706     vfs_ftpfs_ops->ctl = ftpfs_ctl;
2707     ftpfs_subclass.archive_same = ftpfs_archive_same;
2708     ftpfs_subclass.new_archive = ftpfs_new_archive;
2709     ftpfs_subclass.open_archive = ftpfs_open_archive;
2710     ftpfs_subclass.free_archive = ftpfs_free_archive;
2711     ftpfs_subclass.fh_new = ftpfs_fh_new;
2712     ftpfs_subclass.fh_open = ftpfs_fh_open;
2713     ftpfs_subclass.fh_close = ftpfs_fh_close;
2714     ftpfs_subclass.dir_load = ftpfs_dir_load;
2715     ftpfs_subclass.file_store = ftpfs_file_store;
2716     ftpfs_subclass.linear_start = ftpfs_linear_start;
2717     ftpfs_subclass.linear_read = ftpfs_linear_read;
2718     ftpfs_subclass.linear_close = ftpfs_linear_close;
2719     vfs_register_class (vfs_ftpfs_ops);
2720 }
2721 
2722 /* --------------------------------------------------------------------------------------------- */

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