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

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