root/src/vfs/shell/shell.c

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

DEFINITIONS

This source file includes following definitions.
  1. shell_set_blksize
  2. shell_default_stat
  3. shell_load_script_from_file
  4. shell_decode_reply
  5. shell_get_reply
  6. shell_command
  7. G_GNUC_PRINTF
  8. G_GNUC_PRINTF
  9. G_GNUC_PRINTF
  10. shell_new_archive
  11. shell_free_archive
  12. shell_pipeopen
  13. shell_set_env
  14. shell_info
  15. shell_open_archive_pipeopen
  16. shell_open_archive_talk
  17. shell_open_archive_int
  18. shell_open_archive
  19. shell_archive_same
  20. shell_parse_ls
  21. shell_dir_load
  22. shell_file_store
  23. shell_linear_start
  24. shell_linear_abort
  25. shell_linear_read
  26. shell_linear_close
  27. shell_ctl
  28. shell_rename
  29. shell_link
  30. shell_symlink
  31. shell_stat
  32. shell_lstat
  33. shell_fstat
  34. shell_chmod
  35. shell_chown
  36. shell_utime
  37. shell_unlink
  38. shell_exists
  39. shell_mkdir
  40. shell_rmdir
  41. shell_fh_new
  42. shell_fh_open
  43. shell_fill_names
  44. shell_open
  45. vfs_init_shell

   1 /*
   2    Virtual File System: SHELL implementation for transferring files over
   3    shell connections.
   4 
   5    Copyright (C) 1998-2024
   6    Free Software Foundation, Inc.
   7 
   8    Written by:
   9    Pavel Machek, 1998
  10    Michal Svec, 2000
  11    Andrew Borodin <aborodin@vmail.ru>, 2010-2022
  12    Slava Zanko <slavazanko@gmail.com>, 2010, 2013
  13    Ilia Maslakov <il.smind@gmail.com>, 2010
  14 
  15    Derived from ftpfs.c.
  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: SHELL implementation for transferring files over
  36  * shell connections
  37  * \author Pavel Machek
  38  * \author Michal Svec
  39  * \date 1998, 2000
  40  *
  41  * Derived from ftpfs.c
  42  * Read README.shell for protocol specification.
  43  *
  44  * Syntax of path is: \verbatim sh://user@host[:Cr]/path \endverbatim
  45  *      where C means you want compressed connection,
  46  *      and r means you want to use rsh
  47  *
  48  * Namespace: shell_vfs_ops exported.
  49  */
  50 
  51 /* Define this if your ssh can take -I option */
  52 
  53 #include <config.h>
  54 #include <errno.h>
  55 #include <pwd.h>
  56 #include <grp.h>
  57 #include <stdlib.h>
  58 #include <string.h>
  59 #include <inttypes.h>           /* uintmax_t */
  60 
  61 #include "lib/global.h"
  62 #include "lib/tty/tty.h"        /* enable/disable interrupt key */
  63 #include "lib/strutil.h"
  64 #include "lib/unixcompat.h"
  65 #include "lib/fileloc.h"
  66 #include "lib/util.h"           /* my_exit() */
  67 #include "lib/mcconfig.h"
  68 
  69 #include "src/execute.h"        /* pre_exec, post_exec */
  70 
  71 #include "lib/vfs/vfs.h"
  72 #include "lib/vfs/utilvfs.h"
  73 #include "lib/vfs/netutil.h"
  74 #include "lib/vfs/xdirentry.h"
  75 #include "lib/vfs/gc.h"         /* vfs_stamp_create */
  76 
  77 #include "shell.h"
  78 #include "shelldef.h"
  79 
  80 /*** global variables ****************************************************************************/
  81 
  82 int shell_directory_timeout = 900;
  83 
  84 /*** file scope macro definitions ****************************************************************/
  85 
  86 #define DO_RESOLVE_SYMLINK 1
  87 #define DO_OPEN            2
  88 #define DO_FREE_RESOURCE   4
  89 
  90 #define SHELL_FLAG_COMPRESSED 1
  91 #define SHELL_FLAG_RSH        2
  92 
  93 #define OPT_FLUSH        1
  94 #define OPT_IGNORE_ERROR 2
  95 
  96 /*
  97  * Reply codes.
  98  */
  99 #define PRELIM          1       /* positive preliminary */
 100 #define COMPLETE        2       /* positive completion */
 101 #define CONTINUE        3       /* positive intermediate */
 102 #define TRANSIENT       4       /* transient negative completion */
 103 #define ERROR           5       /* permanent negative completion */
 104 
 105 /* command wait_flag: */
 106 #define NONE        0x00
 107 #define WAIT_REPLY  0x01
 108 #define WANT_STRING 0x02
 109 
 110 /* environment flags */
 111 #define SHELL_HAVE_HEAD        1
 112 #define SHELL_HAVE_SED         2
 113 #define SHELL_HAVE_AWK         4
 114 #define SHELL_HAVE_PERL        8
 115 #define SHELL_HAVE_LSQ        16
 116 #define SHELL_HAVE_DATE_MDYT  32
 117 #define SHELL_HAVE_TAIL       64
 118 
 119 #define SHELL_SUPER(super) ((shell_super_t *) (super))
 120 #define SHELL_FILE_HANDLER(fh) ((shell_file_handler_t *) fh)
 121 
 122 /*** file scope type declarations ****************************************************************/
 123 
 124 typedef struct
 125 {
 126     struct vfs_s_super base;    /* base class */
 127 
 128     int sockr;
 129     int sockw;
 130     char *scr_ls;
 131     char *scr_chmod;
 132     char *scr_utime;
 133     char *scr_exists;
 134     char *scr_mkdir;
 135     char *scr_unlink;
 136     char *scr_chown;
 137     char *scr_rmdir;
 138     char *scr_ln;
 139     char *scr_mv;
 140     char *scr_hardlink;
 141     char *scr_get;
 142     char *scr_send;
 143     char *scr_append;
 144     char *scr_info;
 145     int host_flags;
 146     GString *scr_env;
 147 } shell_super_t;
 148 
 149 typedef struct
 150 {
 151     vfs_file_handler_t base;    /* base class */
 152 
 153     off_t got;
 154     off_t total;
 155     gboolean append;
 156 } shell_file_handler_t;
 157 
 158 /*** forward declarations (file scope functions) *************************************************/
 159 
 160 /*** file scope variables ************************************************************************/
 161 
 162 static char reply_str[80];
 163 
 164 static struct vfs_s_subclass shell_subclass;
 165 static struct vfs_class *vfs_shell_ops = VFS_CLASS (&shell_subclass);
 166 
 167 /* --------------------------------------------------------------------------------------------- */
 168 /*** file scope functions ************************************************************************/
 169 /* --------------------------------------------------------------------------------------------- */
 170 
 171 static void
 172 shell_set_blksize (struct stat *s)
     /* [previous][next][first][last][top][bottom][index][help]  */
 173 {
 174 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
 175     /* redefine block size */
 176     s->st_blksize = 64 * 1024;  /* FIXME */
 177 #endif
 178 }
 179 
 180 /* --------------------------------------------------------------------------------------------- */
 181 
 182 static struct stat *
 183 shell_default_stat (struct vfs_class *me)
     /* [previous][next][first][last][top][bottom][index][help]  */
 184 {
 185     struct stat *s;
 186 
 187     s = vfs_s_default_stat (me, S_IFDIR | 0755);
 188     shell_set_blksize (s);
 189     vfs_adjust_stat (s);
 190 
 191     return s;
 192 }
 193 
 194 /* --------------------------------------------------------------------------------------------- */
 195 
 196 static char *
 197 shell_load_script_from_file (const char *hostname, const char *script_name, const char *def_content)
     /* [previous][next][first][last][top][bottom][index][help]  */
 198 {
 199     char *scr_filename = NULL;
 200     char *scr_content;
 201     gsize scr_len = 0;
 202 
 203     /* 1st: scan user directory */
 204     scr_filename =
 205         g_build_path (PATH_SEP_STR, mc_config_get_data_path (), VFS_SHELL_PREFIX, hostname,
 206                       script_name, (char *) NULL);
 207     /* silent about user dir */
 208     g_file_get_contents (scr_filename, &scr_content, &scr_len, NULL);
 209     g_free (scr_filename);
 210     /* 2nd: scan system dir */
 211     if (scr_content == NULL)
 212     {
 213         scr_filename =
 214             g_build_path (PATH_SEP_STR, LIBEXECDIR, VFS_SHELL_PREFIX, script_name, (char *) NULL);
 215         g_file_get_contents (scr_filename, &scr_content, &scr_len, NULL);
 216         g_free (scr_filename);
 217     }
 218 
 219     if (scr_content != NULL)
 220         return scr_content;
 221 
 222     return g_strdup (def_content);
 223 }
 224 
 225 /* --------------------------------------------------------------------------------------------- */
 226 
 227 static int
 228 shell_decode_reply (char *s, gboolean was_garbage)
     /* [previous][next][first][last][top][bottom][index][help]  */
 229 {
 230     int code;
 231 
 232     /* cppcheck-suppress invalidscanf */
 233     if (sscanf (s, "%d", &code) == 0)
 234     {
 235         code = 500;
 236         return 5;
 237     }
 238     if (code < 100)
 239         return was_garbage ? ERROR : (code == 0 ? COMPLETE : PRELIM);
 240     return code / 100;
 241 }
 242 
 243 /* --------------------------------------------------------------------------------------------- */
 244 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
 245 
 246 static int
 247 shell_get_reply (struct vfs_class *me, int sock, char *string_buf, int string_len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 248 {
 249     char answer[BUF_1K];
 250     gboolean was_garbage = FALSE;
 251 
 252     while (TRUE)
 253     {
 254         if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n'))
 255         {
 256             if (string_buf != NULL)
 257                 *string_buf = '\0';
 258             return 4;
 259         }
 260 
 261         if (strncmp (answer, "### ", 4) == 0)
 262             return shell_decode_reply (answer + 4, was_garbage ? 1 : 0);
 263 
 264         was_garbage = TRUE;
 265         if (string_buf != NULL)
 266             g_strlcpy (string_buf, answer, string_len);
 267     }
 268 }
 269 
 270 /* --------------------------------------------------------------------------------------------- */
 271 
 272 static int
 273 shell_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *cmd,
     /* [previous][next][first][last][top][bottom][index][help]  */
 274                size_t cmd_len)
 275 {
 276     ssize_t status;
 277     FILE *logfile = me->logfile;
 278 
 279     if (cmd_len == (size_t) (-1))
 280         cmd_len = strlen (cmd);
 281 
 282     if (logfile != NULL)
 283     {
 284         size_t ret;
 285 
 286         ret = fwrite (cmd, cmd_len, 1, logfile);
 287         ret = fflush (logfile);
 288         (void) ret;
 289     }
 290 
 291     tty_enable_interrupt_key ();
 292     status = write (SHELL_SUPER (super)->sockw, cmd, cmd_len);
 293     tty_disable_interrupt_key ();
 294 
 295     if (status < 0)
 296         return TRANSIENT;
 297 
 298     if (wait_reply)
 299         return shell_get_reply (me, SHELL_SUPER (super)->sockr,
 300                                 (wait_reply & WANT_STRING) != 0 ? reply_str : NULL,
 301                                 sizeof (reply_str) - 1);
 302     return COMPLETE;
 303 }
 304 
 305 /* --------------------------------------------------------------------------------------------- */
 306 
 307 static int
 308 G_GNUC_PRINTF (5, 0)
     /* [previous][next][first][last][top][bottom][index][help]  */
 309 shell_command_va (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *scr,
 310                   const char *vars, va_list ap)
 311 {
 312     int r;
 313     GString *command;
 314 
 315     command = mc_g_string_dup (SHELL_SUPER (super)->scr_env);
 316     g_string_append_vprintf (command, vars, ap);
 317     g_string_append (command, scr);
 318     r = shell_command (me, super, wait_reply, command->str, command->len);
 319     g_string_free (command, TRUE);
 320 
 321     return r;
 322 }
 323 
 324 /* --------------------------------------------------------------------------------------------- */
 325 
 326 static int
 327 G_GNUC_PRINTF (5, 6)
     /* [previous][next][first][last][top][bottom][index][help]  */
 328 shell_command_v (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *scr,
 329                  const char *vars, ...)
 330 {
 331     int r;
 332     va_list ap;
 333 
 334     va_start (ap, vars);
 335     r = shell_command_va (me, super, wait_reply, scr, vars, ap);
 336     va_end (ap);
 337 
 338     return r;
 339 }
 340 
 341 /* --------------------------------------------------------------------------------------------- */
 342 
 343 static int
 344 G_GNUC_PRINTF (5, 6)
     /* [previous][next][first][last][top][bottom][index][help]  */
 345 shell_send_command (struct vfs_class *me, struct vfs_s_super *super, int flags, const char *scr,
 346                     const char *vars, ...)
 347 {
 348     int r;
 349     va_list ap;
 350 
 351     va_start (ap, vars);
 352     r = shell_command_va (me, super, WAIT_REPLY, scr, vars, ap);
 353     va_end (ap);
 354     vfs_stamp_create (vfs_shell_ops, super);
 355 
 356     if (r != COMPLETE)
 357         ERRNOR (E_REMOTE, -1);
 358     if ((flags & OPT_FLUSH) != 0)
 359         vfs_s_invalidate (me, super);
 360 
 361     return 0;
 362 }
 363 
 364 /* --------------------------------------------------------------------------------------------- */
 365 
 366 static struct vfs_s_super *
 367 shell_new_archive (struct vfs_class *me)
     /* [previous][next][first][last][top][bottom][index][help]  */
 368 {
 369     shell_super_t *arch;
 370 
 371     arch = g_new0 (shell_super_t, 1);
 372     arch->base.me = me;
 373 
 374     return VFS_SUPER (arch);
 375 }
 376 
 377 /* --------------------------------------------------------------------------------------------- */
 378 
 379 static void
 380 shell_free_archive (struct vfs_class *me, struct vfs_s_super *super)
     /* [previous][next][first][last][top][bottom][index][help]  */
 381 {
 382     shell_super_t *shell_super = SHELL_SUPER (super);
 383 
 384     if ((shell_super->sockw != -1) || (shell_super->sockr != -1))
 385         vfs_print_message (_("shell: Disconnecting from %s"), super->name ? super->name : "???");
 386 
 387     if (shell_super->sockw != -1)
 388     {
 389         shell_command (me, super, NONE, "exit\n", -1);
 390         close (shell_super->sockw);
 391         shell_super->sockw = -1;
 392     }
 393 
 394     if (shell_super->sockr != -1)
 395     {
 396         close (shell_super->sockr);
 397         shell_super->sockr = -1;
 398     }
 399 
 400     g_free (shell_super->scr_ls);
 401     g_free (shell_super->scr_exists);
 402     g_free (shell_super->scr_mkdir);
 403     g_free (shell_super->scr_unlink);
 404     g_free (shell_super->scr_chown);
 405     g_free (shell_super->scr_chmod);
 406     g_free (shell_super->scr_utime);
 407     g_free (shell_super->scr_rmdir);
 408     g_free (shell_super->scr_ln);
 409     g_free (shell_super->scr_mv);
 410     g_free (shell_super->scr_hardlink);
 411     g_free (shell_super->scr_get);
 412     g_free (shell_super->scr_send);
 413     g_free (shell_super->scr_append);
 414     g_free (shell_super->scr_info);
 415     g_string_free (shell_super->scr_env, TRUE);
 416 }
 417 
 418 /* --------------------------------------------------------------------------------------------- */
 419 
 420 static void
 421 shell_pipeopen (struct vfs_s_super *super, const char *path, const char *argv[])
     /* [previous][next][first][last][top][bottom][index][help]  */
 422 {
 423     int fileset1[2], fileset2[2];
 424     int res;
 425 
 426     if ((pipe (fileset1) < 0) || (pipe (fileset2) < 0))
 427         vfs_die ("Cannot pipe(): %m.");
 428 
 429     res = my_fork ();
 430 
 431     if (res != 0)
 432     {
 433         if (res < 0)
 434             vfs_die ("Cannot fork(): %m.");
 435         /* We are the parent */
 436         close (fileset1[0]);
 437         SHELL_SUPER (super)->sockw = fileset1[1];
 438         close (fileset2[1]);
 439         SHELL_SUPER (super)->sockr = fileset2[0];
 440     }
 441     else
 442     {
 443         res = dup2 (fileset1[0], STDIN_FILENO);
 444         close (fileset1[0]);
 445         close (fileset1[1]);
 446         res = dup2 (fileset2[1], STDOUT_FILENO);
 447         close (STDERR_FILENO);
 448         /* stderr to /dev/null */
 449         res = open ("/dev/null", O_WRONLY);
 450         close (fileset2[0]);
 451         close (fileset2[1]);
 452         my_execvp (path, (char **) argv);
 453         my_exit (3);
 454     }
 455 }
 456 
 457 /* --------------------------------------------------------------------------------------------- */
 458 
 459 static GString *
 460 shell_set_env (int flags)
     /* [previous][next][first][last][top][bottom][index][help]  */
 461 {
 462     GString *ret;
 463 
 464     ret = g_string_sized_new (256);
 465 
 466     if ((flags & SHELL_HAVE_HEAD) != 0)
 467         g_string_append (ret, "SHELL_HAVE_HEAD=1 export SHELL_HAVE_HEAD; ");
 468 
 469     if ((flags & SHELL_HAVE_SED) != 0)
 470         g_string_append (ret, "SHELL_HAVE_SED=1 export SHELL_HAVE_SED; ");
 471 
 472     if ((flags & SHELL_HAVE_AWK) != 0)
 473         g_string_append (ret, "SHELL_HAVE_AWK=1 export SHELL_HAVE_AWK; ");
 474 
 475     if ((flags & SHELL_HAVE_PERL) != 0)
 476         g_string_append (ret, "SHELL_HAVE_PERL=1 export SHELL_HAVE_PERL; ");
 477 
 478     if ((flags & SHELL_HAVE_LSQ) != 0)
 479         g_string_append (ret, "SHELL_HAVE_LSQ=1 export SHELL_HAVE_LSQ; ");
 480 
 481     if ((flags & SHELL_HAVE_DATE_MDYT) != 0)
 482         g_string_append (ret, "SHELL_HAVE_DATE_MDYT=1 export SHELL_HAVE_DATE_MDYT; ");
 483 
 484     if ((flags & SHELL_HAVE_TAIL) != 0)
 485         g_string_append (ret, "SHELL_HAVE_TAIL=1 export SHELL_HAVE_TAIL; ");
 486 
 487     return ret;
 488 }
 489 
 490 /* --------------------------------------------------------------------------------------------- */
 491 
 492 static gboolean
 493 shell_info (struct vfs_class *me, struct vfs_s_super *super)
     /* [previous][next][first][last][top][bottom][index][help]  */
 494 {
 495     shell_super_t *shell_super = SHELL_SUPER (super);
 496 
 497     if (shell_command (me, super, NONE, shell_super->scr_info, -1) == COMPLETE)
 498     {
 499         while (TRUE)
 500         {
 501             int res;
 502             char buffer[BUF_8K] = "";
 503 
 504             res = vfs_s_get_line_interruptible (me, buffer, sizeof (buffer), shell_super->sockr);
 505             if ((res == 0) || (res == EINTR))
 506                 ERRNOR (ECONNRESET, FALSE);
 507             if (strncmp (buffer, "### ", 4) == 0)
 508                 break;
 509             shell_super->host_flags = atol (buffer);
 510         }
 511         return TRUE;
 512     }
 513     ERRNOR (E_PROTO, FALSE);
 514 }
 515 
 516 /* --------------------------------------------------------------------------------------------- */
 517 
 518 static void
 519 shell_open_archive_pipeopen (struct vfs_s_super *super)
     /* [previous][next][first][last][top][bottom][index][help]  */
 520 {
 521     char gbuf[10];
 522     const char *argv[10];       /* All of 10 is used now */
 523     const char *xsh = (super->path_element->port == SHELL_FLAG_RSH ? "rsh" : "ssh");
 524     int i = 0;
 525 
 526     argv[i++] = xsh;
 527     if (super->path_element->port == SHELL_FLAG_COMPRESSED)
 528         argv[i++] = "-C";
 529 
 530     if (super->path_element->port > SHELL_FLAG_RSH)
 531     {
 532         argv[i++] = "-p";
 533         g_snprintf (gbuf, sizeof (gbuf), "%d", super->path_element->port);
 534         argv[i++] = gbuf;
 535     }
 536 
 537     /*
 538      * Add the user name to the ssh command line only if it was explicitly
 539      * set in vfs URL. rsh/ssh will get current user by default
 540      * plus we can set convenient overrides in  ~/.ssh/config (explicit -l
 541      * option breaks it for some)
 542      */
 543 
 544     if (super->path_element->user != NULL)
 545     {
 546         argv[i++] = "-l";
 547         argv[i++] = super->path_element->user;
 548     }
 549     else
 550     {
 551         /* The rest of the code assumes it to be a valid username */
 552         super->path_element->user = vfs_get_local_username ();
 553     }
 554 
 555     argv[i++] = super->path_element->host;
 556     argv[i++] = "echo SHELL:; /bin/sh";
 557     argv[i++] = NULL;
 558 
 559     shell_pipeopen (super, xsh, argv);
 560 }
 561 
 562 /* --------------------------------------------------------------------------------------------- */
 563 
 564 static gboolean
 565 shell_open_archive_talk (struct vfs_class *me, struct vfs_s_super *super)
     /* [previous][next][first][last][top][bottom][index][help]  */
 566 {
 567     shell_super_t *shell_super = SHELL_SUPER (super);
 568     char answer[2048];
 569 
 570     printf ("\n%s\n", _("shell: Waiting for initial line..."));
 571 
 572     if (vfs_s_get_line (me, shell_super->sockr, answer, sizeof (answer), ':') == 0)
 573         return FALSE;
 574 
 575     if (strstr (answer, "assword") != NULL)
 576     {
 577         /* Currently, this does not work. ssh reads passwords from
 578            /dev/tty, not from stdin :-(. */
 579 
 580         printf ("\n%s\n", _("Sorry, we cannot do password authenticated connections for now."));
 581 
 582         return FALSE;
 583 #if 0
 584         if (super->path_element->password == NULL)
 585         {
 586             char *p, *op;
 587 
 588             p = g_strdup_printf (_("shell: Password is required for %s"),
 589                                  super->path_element->user);
 590             op = vfs_get_password (p);
 591             g_free (p);
 592             if (op == NULL)
 593                 return FALSE;
 594             super->path_element->password = op;
 595         }
 596 
 597         printf ("\n%s\n", _("shell: Sending password..."));
 598 
 599         {
 600             size_t str_len;
 601 
 602             str_len = strlen (super->path_element->password);
 603             if ((write (shell_super.sockw, super->path_element->password, str_len) !=
 604                  (ssize_t) str_len) || (write (shell_super->sockw, "\n", 1) != 1))
 605                 return FALSE;
 606         }
 607 #endif
 608     }
 609     return TRUE;
 610 }
 611 
 612 /* --------------------------------------------------------------------------------------------- */
 613 
 614 static int
 615 shell_open_archive_int (struct vfs_class *me, struct vfs_s_super *super)
     /* [previous][next][first][last][top][bottom][index][help]  */
 616 {
 617     gboolean ftalk;
 618 
 619     /* hide panels */
 620     pre_exec ();
 621 
 622     /* open pipe */
 623     shell_open_archive_pipeopen (super);
 624 
 625     /* Start talk with ssh-server (password prompt, etc ) */
 626     ftalk = shell_open_archive_talk (me, super);
 627 
 628     /* show panels */
 629     post_exec ();
 630 
 631     if (!ftalk)
 632         ERRNOR (E_PROTO, -1);
 633 
 634     vfs_print_message ("%s", _("shell: Sending initial line..."));
 635 
 636     /* Set up remote locale to C, otherwise dates cannot be recognized */
 637     if (shell_command
 638         (me, super, WAIT_REPLY,
 639          "LANG=C LC_ALL=C LC_TIME=C; export LANG LC_ALL LC_TIME;\n" "echo '### 200'\n",
 640          -1) != COMPLETE)
 641         ERRNOR (E_PROTO, -1);
 642 
 643     vfs_print_message ("%s", _("shell: Getting host info..."));
 644     if (shell_info (me, super))
 645         SHELL_SUPER (super)->scr_env = shell_set_env (SHELL_SUPER (super)->host_flags);
 646 
 647 #if 0
 648     super->name =
 649         g_strconcat ("sh://", super->path_element->user, "@", super->path_element->host,
 650                      PATH_SEP_STR, (char *) NULL);
 651 #else
 652     super->name = g_strdup (PATH_SEP_STR);
 653 #endif
 654 
 655     super->root = vfs_s_new_inode (me, super, shell_default_stat (me));
 656 
 657     return 0;
 658 }
 659 
 660 /* --------------------------------------------------------------------------------------------- */
 661 
 662 static int
 663 shell_open_archive (struct vfs_s_super *super,
     /* [previous][next][first][last][top][bottom][index][help]  */
 664                     const vfs_path_t *vpath, const vfs_path_element_t *vpath_element)
 665 {
 666     shell_super_t *shell_super = SHELL_SUPER (super);
 667 
 668     (void) vpath;
 669 
 670     super->path_element = vfs_path_element_clone (vpath_element);
 671 
 672     if (strncmp (vpath_element->vfs_prefix, "rsh", 3) == 0)
 673         super->path_element->port = SHELL_FLAG_RSH;
 674 
 675     shell_super->scr_ls =
 676         shell_load_script_from_file (super->path_element->host, VFS_SHELL_LS_FILE,
 677                                      VFS_SHELL_LS_DEF_CONTENT);
 678     shell_super->scr_exists =
 679         shell_load_script_from_file (super->path_element->host, VFS_SHELL_EXISTS_FILE,
 680                                      VFS_SHELL_EXISTS_DEF_CONTENT);
 681     shell_super->scr_mkdir =
 682         shell_load_script_from_file (super->path_element->host, VFS_SHELL_MKDIR_FILE,
 683                                      VFS_SHELL_MKDIR_DEF_CONTENT);
 684     shell_super->scr_unlink =
 685         shell_load_script_from_file (super->path_element->host, VFS_SHELL_UNLINK_FILE,
 686                                      VFS_SHELL_UNLINK_DEF_CONTENT);
 687     shell_super->scr_chown =
 688         shell_load_script_from_file (super->path_element->host, VFS_SHELL_CHOWN_FILE,
 689                                      VFS_SHELL_CHOWN_DEF_CONTENT);
 690     shell_super->scr_chmod =
 691         shell_load_script_from_file (super->path_element->host, VFS_SHELL_CHMOD_FILE,
 692                                      VFS_SHELL_CHMOD_DEF_CONTENT);
 693     shell_super->scr_utime =
 694         shell_load_script_from_file (super->path_element->host, VFS_SHELL_UTIME_FILE,
 695                                      VFS_SHELL_UTIME_DEF_CONTENT);
 696     shell_super->scr_rmdir =
 697         shell_load_script_from_file (super->path_element->host, VFS_SHELL_RMDIR_FILE,
 698                                      VFS_SHELL_RMDIR_DEF_CONTENT);
 699     shell_super->scr_ln =
 700         shell_load_script_from_file (super->path_element->host, VFS_SHELL_LN_FILE,
 701                                      VFS_SHELL_LN_DEF_CONTENT);
 702     shell_super->scr_mv =
 703         shell_load_script_from_file (super->path_element->host, VFS_SHELL_MV_FILE,
 704                                      VFS_SHELL_MV_DEF_CONTENT);
 705     shell_super->scr_hardlink =
 706         shell_load_script_from_file (super->path_element->host, VFS_SHELL_HARDLINK_FILE,
 707                                      VFS_SHELL_HARDLINK_DEF_CONTENT);
 708     shell_super->scr_get =
 709         shell_load_script_from_file (super->path_element->host, VFS_SHELL_GET_FILE,
 710                                      VFS_SHELL_GET_DEF_CONTENT);
 711     shell_super->scr_send =
 712         shell_load_script_from_file (super->path_element->host, VFS_SHELL_SEND_FILE,
 713                                      VFS_SHELL_SEND_DEF_CONTENT);
 714     shell_super->scr_append =
 715         shell_load_script_from_file (super->path_element->host, VFS_SHELL_APPEND_FILE,
 716                                      VFS_SHELL_APPEND_DEF_CONTENT);
 717     shell_super->scr_info =
 718         shell_load_script_from_file (super->path_element->host, VFS_SHELL_INFO_FILE,
 719                                      VFS_SHELL_INFO_DEF_CONTENT);
 720 
 721     return shell_open_archive_int (vpath_element->class, super);
 722 }
 723 
 724 /* --------------------------------------------------------------------------------------------- */
 725 
 726 static int
 727 shell_archive_same (const vfs_path_element_t *vpath_element, struct vfs_s_super *super,
     /* [previous][next][first][last][top][bottom][index][help]  */
 728                     const vfs_path_t *vpath, void *cookie)
 729 {
 730     vfs_path_element_t *path_element;
 731     int result;
 732 
 733     (void) vpath;
 734     (void) cookie;
 735 
 736     path_element = vfs_path_element_clone (vpath_element);
 737 
 738     if (path_element->user == NULL)
 739         path_element->user = vfs_get_local_username ();
 740 
 741     result = ((strcmp (path_element->host, super->path_element->host) == 0)
 742               && (strcmp (path_element->user, super->path_element->user) == 0)
 743               && (path_element->port == super->path_element->port)) ? 1 : 0;
 744 
 745     vfs_path_element_free (path_element);
 746 
 747     return result;
 748 }
 749 
 750 /* --------------------------------------------------------------------------------------------- */
 751 
 752 static void
 753 shell_parse_ls (char *buffer, struct vfs_s_entry *ent)
     /* [previous][next][first][last][top][bottom][index][help]  */
 754 {
 755 #define ST ent->ino->st
 756 
 757     buffer++;
 758 
 759     switch (buffer[-1])
 760     {
 761     case ':':
 762         {
 763             char *filename;
 764             char *filename_bound;
 765             char *temp;
 766 
 767             filename = buffer;
 768 
 769             if (strcmp (filename, "\".\"") == 0 || strcmp (filename, "\"..\"") == 0)
 770                 break;          /* We'll do "." and ".." ourselves */
 771 
 772             filename_bound = filename + strlen (filename);
 773 
 774             if (S_ISLNK (ST.st_mode))
 775             {
 776                 char *linkname;
 777                 char *linkname_bound;
 778                 /* we expect: "escaped-name" -> "escaped-name"
 779                    //     -> cannot occur in filenames,
 780                    //     because it will be escaped to -\> */
 781 
 782 
 783                 linkname_bound = filename_bound;
 784 
 785                 if (*filename == '"')
 786                     ++filename;
 787 
 788                 linkname = strstr (filename, "\" -> \"");
 789                 if (linkname == NULL)
 790                 {
 791                     /* broken client, or smth goes wrong */
 792                     linkname = filename_bound;
 793                     if (filename_bound > filename && *(filename_bound - 1) == '"')
 794                         --filename_bound;       /* skip trailing " */
 795                 }
 796                 else
 797                 {
 798                     filename_bound = linkname;
 799                     linkname += 6;      /* strlen ("\" -> \"") */
 800                     if (*(linkname_bound - 1) == '"')
 801                         --linkname_bound;       /* skip trailing " */
 802                 }
 803 
 804                 ent->name = g_strndup (filename, filename_bound - filename);
 805                 temp = ent->name;
 806                 ent->name = str_shell_unescape (ent->name);
 807                 g_free (temp);
 808 
 809                 ent->ino->linkname = g_strndup (linkname, linkname_bound - linkname);
 810                 temp = ent->ino->linkname;
 811                 ent->ino->linkname = str_shell_unescape (ent->ino->linkname);
 812                 g_free (temp);
 813             }
 814             else
 815             {
 816                 /* we expect: "escaped-name" */
 817                 if (filename_bound - filename > 2)
 818                 {
 819                     /*
 820                        there is at least 2 "
 821                        and we skip them
 822                      */
 823                     if (*filename == '"')
 824                         ++filename;
 825                     if (*(filename_bound - 1) == '"')
 826                         --filename_bound;
 827                 }
 828 
 829                 ent->name = g_strndup (filename, filename_bound - filename);
 830                 temp = ent->name;
 831                 ent->name = str_shell_unescape (ent->name);
 832                 g_free (temp);
 833             }
 834             break;
 835         }
 836 
 837     case 'S':
 838         ST.st_size = (off_t) g_ascii_strtoll (buffer, NULL, 10);
 839         break;
 840 
 841     case 'P':
 842         {
 843             size_t skipped;
 844 
 845             vfs_parse_filemode (buffer, &skipped, &ST.st_mode);
 846             break;
 847         }
 848 
 849     case 'R':
 850         {
 851             /*
 852                raw filemode:
 853                we expect: Roctal-filemode octal-filetype uid.gid
 854              */
 855             size_t skipped;
 856 
 857             vfs_parse_raw_filemode (buffer, &skipped, &ST.st_mode);
 858             break;
 859         }
 860 
 861     case 'd':
 862         vfs_split_text (buffer);
 863         vfs_zero_stat_times (&ST);
 864         if (vfs_parse_filedate (0, &ST.st_ctime) == 0)
 865             break;
 866         ST.st_atime = ST.st_mtime = ST.st_ctime;
 867         break;
 868 
 869     case 'D':
 870         {
 871             struct tm tim;
 872 
 873             memset (&tim, 0, sizeof (tim));
 874             /* cppcheck-suppress invalidscanf */
 875             if (sscanf (buffer, "%d %d %d %d %d %d", &tim.tm_year, &tim.tm_mon,
 876                         &tim.tm_mday, &tim.tm_hour, &tim.tm_min, &tim.tm_sec) != 6)
 877                 break;
 878             vfs_zero_stat_times (&ST);
 879             ST.st_atime = ST.st_mtime = ST.st_ctime = mktime (&tim);
 880         }
 881         break;
 882 
 883     case 'E':
 884         {
 885             int maj, min;
 886 
 887             /* cppcheck-suppress invalidscanf */
 888             if (sscanf (buffer, "%d,%d", &maj, &min) != 2)
 889                 break;
 890 #ifdef HAVE_STRUCT_STAT_ST_RDEV
 891             ST.st_rdev = makedev (maj, min);
 892 #endif
 893         }
 894         break;
 895 
 896     default:
 897         break;
 898     }
 899 
 900 #undef ST
 901 }
 902 
 903 /* --------------------------------------------------------------------------------------------- */
 904 
 905 static int
 906 shell_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, const char *remote_path)
     /* [previous][next][first][last][top][bottom][index][help]  */
 907 {
 908     struct vfs_s_super *super = dir->super;
 909     char buffer[BUF_8K] = "\0";
 910     struct vfs_s_entry *ent = NULL;
 911     char *quoted_path;
 912     int reply_code;
 913 
 914     /*
 915      * Simple SHELL debug interface :]
 916      */
 917 #if 0
 918     if (me->logfile == NULL)
 919         me->logfile = fopen ("/tmp/mc-SHELL.sh", "w");
 920 #endif
 921 
 922     vfs_print_message (_("shell: Reading directory %s..."), remote_path);
 923 
 924     dir->timestamp = g_get_monotonic_time () + shell_directory_timeout * G_USEC_PER_SEC;
 925 
 926     quoted_path = str_shell_escape (remote_path);
 927     (void) shell_command_v (me, super, NONE, SHELL_SUPER (super)->scr_ls, "SHELL_FILENAME=%s;\n",
 928                             quoted_path);
 929     g_free (quoted_path);
 930 
 931     ent = vfs_s_generate_entry (me, NULL, dir, 0);
 932 
 933     while (TRUE)
 934     {
 935         int res;
 936 
 937         res =
 938             vfs_s_get_line_interruptible (me, buffer, sizeof (buffer), SHELL_SUPER (super)->sockr);
 939 
 940         if ((res == 0) || (res == EINTR))
 941         {
 942             vfs_s_free_entry (me, ent);
 943             me->verrno = ECONNRESET;
 944             goto error;
 945         }
 946         if (me->logfile != NULL)
 947         {
 948             fputs (buffer, me->logfile);
 949             fputs ("\n", me->logfile);
 950             fflush (me->logfile);
 951         }
 952         if (strncmp (buffer, "### ", 4) == 0)
 953             break;
 954 
 955         if (buffer[0] != '\0')
 956             shell_parse_ls (buffer, ent);
 957         else if (ent->name != NULL)
 958         {
 959             vfs_s_insert_entry (me, dir, ent);
 960             ent = vfs_s_generate_entry (me, NULL, dir, 0);
 961         }
 962     }
 963 
 964     vfs_s_free_entry (me, ent);
 965     reply_code = shell_decode_reply (buffer + 4, 0);
 966     if (reply_code == COMPLETE)
 967     {
 968         vfs_print_message (_("%s: done."), me->name);
 969         return 0;
 970     }
 971 
 972     me->verrno = reply_code == ERROR ? EACCES : E_REMOTE;
 973 
 974   error:
 975     vfs_print_message (_("%s: failure"), me->name);
 976     return -1;
 977 }
 978 
 979 /* --------------------------------------------------------------------------------------------- */
 980 
 981 static int
 982 shell_file_store (struct vfs_class *me, vfs_file_handler_t *fh, char *name, char *localname)
     /* [previous][next][first][last][top][bottom][index][help]  */
 983 {
 984     shell_file_handler_t *shell = SHELL_FILE_HANDLER (fh);
 985     struct vfs_s_super *super = VFS_FILE_HANDLER_SUPER (fh);
 986     shell_super_t *shell_super = SHELL_SUPER (super);
 987     int code;
 988     off_t total = 0;
 989     char buffer[BUF_8K];
 990     struct stat s;
 991     int h;
 992     char *quoted_name;
 993 
 994     h = open (localname, O_RDONLY);
 995     if (h == -1)
 996         ERRNOR (EIO, -1);
 997     if (fstat (h, &s) < 0)
 998     {
 999         close (h);
1000         ERRNOR (EIO, -1);
1001     }
1002 
1003     /* First, try this as stor:
1004      *
1005      *     ( head -c number ) | ( cat > file; cat >/dev/null )
1006      *
1007      *  If 'head' is not present on the remote system, 'dd' will be used.
1008      * Unfortunately, we cannot trust most non-GNU 'head' implementations
1009      * even if '-c' options is supported. Therefore, we separate GNU head
1010      * (and other modern heads?) using '-q' and '-' . This causes another
1011      * implementations to fail (because of "incorrect options").
1012      *
1013      *  Fallback is:
1014      *
1015      *     rest=<number>
1016      *     while [ $rest -gt 0 ]
1017      *     do
1018      *        cnt=`expr \( $rest + 255 \) / 256`
1019      *        n=`dd bs=256 count=$cnt | tee -a <target_file> | wc -c`
1020      *        rest=`expr $rest - $n`
1021      *     done
1022      *
1023      *  'dd' was not designed for full filling of input buffers,
1024      *  and does not report exact number of bytes (not blocks).
1025      *  Therefore a more complex shell script is needed.
1026      *
1027      *   On some systems non-GNU head writes "Usage:" error report to stdout
1028      *  instead of stderr. It makes impossible the use of "head || dd"
1029      *  algorithm for file appending case, therefore just "dd" is used for it.
1030      */
1031 
1032     quoted_name = str_shell_escape (name);
1033     vfs_print_message (_("shell: store %s: sending command..."), quoted_name);
1034 
1035     /* FIXME: File size is limited to ULONG_MAX */
1036     code =
1037         shell_command_v (me, super, WAIT_REPLY,
1038                          shell->append ? shell_super->scr_append : shell_super->scr_send,
1039                          "SHELL_FILENAME=%s SHELL_FILESIZE=%" PRIuMAX ";\n", quoted_name,
1040                          (uintmax_t) s.st_size);
1041     g_free (quoted_name);
1042 
1043     if (code != PRELIM)
1044     {
1045         close (h);
1046         ERRNOR (E_REMOTE, -1);
1047     }
1048 
1049     while (TRUE)
1050     {
1051         ssize_t n, t;
1052 
1053         while ((n = read (h, buffer, sizeof (buffer))) < 0)
1054         {
1055             if ((errno == EINTR) && tty_got_interrupt ())
1056                 continue;
1057             vfs_print_message ("%s", _("shell: Local read failed, sending zeros"));
1058             close (h);
1059             h = open ("/dev/zero", O_RDONLY);
1060         }
1061 
1062         if (n == 0)
1063             break;
1064 
1065         t = write (shell_super->sockw, buffer, n);
1066         if (t != n)
1067         {
1068             if (t == -1)
1069                 me->verrno = errno;
1070             else
1071                 me->verrno = EIO;
1072             goto error_return;
1073         }
1074         tty_disable_interrupt_key ();
1075         total += n;
1076         vfs_print_message ("%s: %" PRIuMAX "/%" PRIuMAX, _("shell: storing file"),
1077                            (uintmax_t) total, (uintmax_t) s.st_size);
1078     }
1079     close (h);
1080 
1081     if (shell_get_reply (me, shell_super->sockr, NULL, 0) != COMPLETE)
1082         ERRNOR (E_REMOTE, -1);
1083     return 0;
1084 
1085   error_return:
1086     close (h);
1087     shell_get_reply (me, shell_super->sockr, NULL, 0);
1088     return -1;
1089 }
1090 
1091 /* --------------------------------------------------------------------------------------------- */
1092 
1093 static int
1094 shell_linear_start (struct vfs_class *me, vfs_file_handler_t *fh, off_t offset)
     /* [previous][next][first][last][top][bottom][index][help]  */
1095 {
1096     shell_file_handler_t *shell = SHELL_FILE_HANDLER (fh);
1097     struct vfs_s_super *super = VFS_FILE_HANDLER_SUPER (fh);
1098     char *name;
1099     char *quoted_name;
1100 
1101     name = vfs_s_fullpath (me, fh->ino);
1102     if (name == NULL)
1103         return 0;
1104     quoted_name = str_shell_escape (name);
1105     g_free (name);
1106     shell->append = FALSE;
1107 
1108     /*
1109      * Check whether the remote file is readable by using 'dd' to copy 
1110      * a single byte from the remote file to /dev/null. If 'dd' completes
1111      * with exit status of 0 use 'cat' to send the file contents to the
1112      * standard output (i.e. over the network).
1113      */
1114 
1115     offset =
1116         shell_command_v (me, super, WANT_STRING, SHELL_SUPER (super)->scr_get,
1117                          "SHELL_FILENAME=%s SHELL_START_OFFSET=%" PRIuMAX ";\n", quoted_name,
1118                          (uintmax_t) offset);
1119     g_free (quoted_name);
1120 
1121     if (offset != PRELIM)
1122         ERRNOR (E_REMOTE, 0);
1123     fh->linear = LS_LINEAR_OPEN;
1124     shell->got = 0;
1125     errno = 0;
1126 #if SIZEOF_OFF_T == SIZEOF_LONG
1127     shell->total = (off_t) strtol (reply_str, NULL, 10);
1128 #else
1129     shell->total = (off_t) g_ascii_strtoll (reply_str, NULL, 10);
1130 #endif
1131     if (errno != 0)
1132         ERRNOR (E_REMOTE, 0);
1133     return 1;
1134 }
1135 
1136 /* --------------------------------------------------------------------------------------------- */
1137 
1138 static void
1139 shell_linear_abort (struct vfs_class *me, vfs_file_handler_t *fh)
     /* [previous][next][first][last][top][bottom][index][help]  */
1140 {
1141     shell_file_handler_t *shell = SHELL_FILE_HANDLER (fh);
1142     struct vfs_s_super *super = VFS_FILE_HANDLER_SUPER (fh);
1143     char buffer[BUF_8K];
1144     ssize_t n;
1145 
1146     vfs_print_message ("%s", _("Aborting transfer..."));
1147 
1148     do
1149     {
1150         n = MIN ((off_t) sizeof (buffer), (shell->total - shell->got));
1151         if (n != 0)
1152         {
1153             n = read (SHELL_SUPER (super)->sockr, buffer, n);
1154             if (n < 0)
1155                 return;
1156             shell->got += n;
1157         }
1158     }
1159     while (n != 0);
1160 
1161     if (shell_get_reply (me, SHELL_SUPER (super)->sockr, NULL, 0) != COMPLETE)
1162         vfs_print_message ("%s", _("Error reported after abort."));
1163     else
1164         vfs_print_message ("%s", _("Aborted transfer would be successful."));
1165 }
1166 
1167 /* --------------------------------------------------------------------------------------------- */
1168 
1169 static ssize_t
1170 shell_linear_read (struct vfs_class *me, vfs_file_handler_t *fh, void *buf, size_t len)
     /* [previous][next][first][last][top][bottom][index][help]  */
1171 {
1172     shell_file_handler_t *shell = SHELL_FILE_HANDLER (fh);
1173     struct vfs_s_super *super = VFS_FILE_HANDLER_SUPER (fh);
1174     ssize_t n = 0;
1175 
1176     len = MIN ((size_t) (shell->total - shell->got), len);
1177     tty_disable_interrupt_key ();
1178     while (len != 0 && ((n = read (SHELL_SUPER (super)->sockr, buf, len)) < 0))
1179     {
1180         if ((errno == EINTR) && !tty_got_interrupt ())
1181             continue;
1182         break;
1183     }
1184     tty_enable_interrupt_key ();
1185 
1186     if (n > 0)
1187         shell->got += n;
1188     else if (n < 0)
1189         shell_linear_abort (me, fh);
1190     else if (shell_get_reply (me, SHELL_SUPER (super)->sockr, NULL, 0) != COMPLETE)
1191         ERRNOR (E_REMOTE, -1);
1192     ERRNOR (errno, n);
1193 }
1194 
1195 /* --------------------------------------------------------------------------------------------- */
1196 
1197 static void
1198 shell_linear_close (struct vfs_class *me, vfs_file_handler_t *fh)
     /* [previous][next][first][last][top][bottom][index][help]  */
1199 {
1200     shell_file_handler_t *shell = SHELL_FILE_HANDLER (fh);
1201 
1202     if (shell->total != shell->got)
1203         shell_linear_abort (me, fh);
1204 }
1205 
1206 /* --------------------------------------------------------------------------------------------- */
1207 
1208 static int
1209 shell_ctl (void *fh, int ctlop, void *arg)
     /* [previous][next][first][last][top][bottom][index][help]  */
1210 {
1211     (void) arg;
1212     (void) fh;
1213     (void) ctlop;
1214 
1215     return 0;
1216 
1217 #if 0
1218     switch (ctlop)
1219     {
1220     case VFS_CTL_IS_NOTREADY:
1221         {
1222             vfs_file_handler_t *file = VFS_FILE_HANDLER (fh);
1223             int v;
1224 
1225             if (file->linear == LS_NOT_LINEAR)
1226                 vfs_die ("You may not do this");
1227             if (file->linear == LS_LINEAR_CLOSED || file->linear == LS_LINEAR_PREOPEN)
1228                 return 0;
1229 
1230             v = vfs_s_select_on_two (VFS_FILE_HANDLER_SUPER (fh)->u.shell.sockr, 0);
1231 
1232             return (((v < 0) && (errno == EINTR)) || v == 0) ? 1 : 0;
1233         }
1234     default:
1235         return 0;
1236     }
1237 #endif
1238 }
1239 
1240 /* --------------------------------------------------------------------------------------------- */
1241 
1242 static int
1243 shell_rename (const vfs_path_t *vpath1, const vfs_path_t *vpath2)
     /* [previous][next][first][last][top][bottom][index][help]  */
1244 {
1245     const char *crpath1, *crpath2;
1246     char *rpath1, *rpath2;
1247     struct vfs_s_super *super, *super2;
1248     struct vfs_class *me;
1249     int ret;
1250 
1251     crpath1 = vfs_s_get_path (vpath1, &super, 0);
1252     if (crpath1 == NULL)
1253         return -1;
1254 
1255     crpath2 = vfs_s_get_path (vpath2, &super2, 0);
1256     if (crpath2 == NULL)
1257         return -1;
1258 
1259     rpath1 = str_shell_escape (crpath1);
1260     rpath2 = str_shell_escape (crpath2);
1261 
1262     me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath1));
1263 
1264     ret =
1265         shell_send_command (me, super2, OPT_FLUSH, SHELL_SUPER (super)->scr_mv,
1266                             "SHELL_FILEFROM=%s SHELL_FILETO=%s;\n", rpath1, rpath2);
1267 
1268     g_free (rpath1);
1269     g_free (rpath2);
1270 
1271     return ret;
1272 }
1273 
1274 /* --------------------------------------------------------------------------------------------- */
1275 
1276 static int
1277 shell_link (const vfs_path_t *vpath1, const vfs_path_t *vpath2)
     /* [previous][next][first][last][top][bottom][index][help]  */
1278 {
1279     const char *crpath1, *crpath2;
1280     char *rpath1, *rpath2;
1281     struct vfs_s_super *super, *super2;
1282     struct vfs_class *me;
1283     int ret;
1284 
1285     crpath1 = vfs_s_get_path (vpath1, &super, 0);
1286     if (crpath1 == NULL)
1287         return -1;
1288 
1289     crpath2 = vfs_s_get_path (vpath2, &super2, 0);
1290     if (crpath2 == NULL)
1291         return -1;
1292 
1293     rpath1 = str_shell_escape (crpath1);
1294     rpath2 = str_shell_escape (crpath2);
1295 
1296     me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath1));
1297 
1298     ret =
1299         shell_send_command (me, super2, OPT_FLUSH, SHELL_SUPER (super)->scr_hardlink,
1300                             "SHELL_FILEFROM=%s SHELL_FILETO=%s;\n", rpath1, rpath2);
1301 
1302     g_free (rpath1);
1303     g_free (rpath2);
1304 
1305     return ret;
1306 }
1307 
1308 /* --------------------------------------------------------------------------------------------- */
1309 
1310 static int
1311 shell_symlink (const vfs_path_t *vpath1, const vfs_path_t *vpath2)
     /* [previous][next][first][last][top][bottom][index][help]  */
1312 {
1313     char *qsetto;
1314     const char *crpath;
1315     char *rpath;
1316     struct vfs_s_super *super;
1317     struct vfs_class *me;
1318     int ret;
1319 
1320     crpath = vfs_s_get_path (vpath2, &super, 0);
1321     if (crpath == NULL)
1322         return -1;
1323 
1324     rpath = str_shell_escape (crpath);
1325     qsetto = str_shell_escape (vfs_path_get_last_path_str (vpath1));
1326 
1327     me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath2));
1328 
1329     ret =
1330         shell_send_command (me, super, OPT_FLUSH, SHELL_SUPER (super)->scr_ln,
1331                             "SHELL_FILEFROM=%s SHELL_FILETO=%s;\n", qsetto, rpath);
1332 
1333     g_free (qsetto);
1334     g_free (rpath);
1335 
1336     return ret;
1337 }
1338 
1339 /* --------------------------------------------------------------------------------------------- */
1340 
1341 static int
1342 shell_stat (const vfs_path_t *vpath, struct stat *buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
1343 {
1344     int ret;
1345 
1346     ret = vfs_s_stat (vpath, buf);
1347     shell_set_blksize (buf);
1348     return ret;
1349 }
1350 
1351 /* --------------------------------------------------------------------------------------------- */
1352 
1353 static int
1354 shell_lstat (const vfs_path_t *vpath, struct stat *buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
1355 {
1356     int ret;
1357 
1358     ret = vfs_s_lstat (vpath, buf);
1359     shell_set_blksize (buf);
1360     return ret;
1361 }
1362 
1363 /* --------------------------------------------------------------------------------------------- */
1364 
1365 static int
1366 shell_fstat (void *vfs_info, struct stat *buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
1367 {
1368     int ret;
1369 
1370     ret = vfs_s_fstat (vfs_info, buf);
1371     shell_set_blksize (buf);
1372     return ret;
1373 }
1374 
1375 /* --------------------------------------------------------------------------------------------- */
1376 
1377 static int
1378 shell_chmod (const vfs_path_t *vpath, mode_t mode)
     /* [previous][next][first][last][top][bottom][index][help]  */
1379 {
1380     const char *crpath;
1381     char *rpath;
1382     struct vfs_s_super *super;
1383     struct vfs_class *me;
1384     int ret;
1385 
1386     crpath = vfs_s_get_path (vpath, &super, 0);
1387     if (crpath == NULL)
1388         return -1;
1389 
1390     rpath = str_shell_escape (crpath);
1391 
1392     me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath));
1393 
1394     ret =
1395         shell_send_command (me, super, OPT_FLUSH, SHELL_SUPER (super)->scr_chmod,
1396                             "SHELL_FILENAME=%s SHELL_FILEMODE=%4.4o;\n", rpath,
1397                             (unsigned int) (mode & 07777));
1398 
1399     g_free (rpath);
1400 
1401     return ret;;
1402 }
1403 
1404 /* --------------------------------------------------------------------------------------------- */
1405 
1406 static int
1407 shell_chown (const vfs_path_t *vpath, uid_t owner, gid_t group)
     /* [previous][next][first][last][top][bottom][index][help]  */
1408 {
1409     char *sowner, *sgroup;
1410     struct passwd *pw;
1411     struct group *gr;
1412     const char *crpath;
1413     char *rpath;
1414     struct vfs_s_super *super;
1415     struct vfs_class *me;
1416     int ret;
1417 
1418     pw = getpwuid (owner);
1419     if (pw == NULL)
1420         return 0;
1421 
1422     gr = getgrgid (group);
1423     if (gr == NULL)
1424         return 0;
1425 
1426     sowner = pw->pw_name;
1427     sgroup = gr->gr_name;
1428 
1429     crpath = vfs_s_get_path (vpath, &super, 0);
1430     if (crpath == NULL)
1431         return -1;
1432 
1433     rpath = str_shell_escape (crpath);
1434 
1435     me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath));
1436 
1437     /* FIXME: what should we report if chgrp succeeds but chown fails? */
1438     ret =
1439         shell_send_command (me, super, OPT_FLUSH, SHELL_SUPER (super)->scr_chown,
1440                             "SHELL_FILENAME=%s SHELL_FILEOWNER=%s SHELL_FILEGROUP=%s;\n", rpath,
1441                             sowner, sgroup);
1442 
1443     g_free (rpath);
1444 
1445     return ret;
1446 }
1447 
1448 /* --------------------------------------------------------------------------------------------- */
1449 
1450 static int
1451 shell_utime (const vfs_path_t *vpath, mc_timesbuf_t *times)
     /* [previous][next][first][last][top][bottom][index][help]  */
1452 {
1453     char utcatime[16], utcmtime[16];
1454     char utcatime_w_nsec[30], utcmtime_w_nsec[30];
1455     mc_timespec_t atime, mtime;
1456     struct tm *gmt;
1457     const char *crpath;
1458     char *rpath;
1459     struct vfs_s_super *super;
1460     struct vfs_class *me;
1461     int ret;
1462 
1463     crpath = vfs_s_get_path (vpath, &super, 0);
1464     if (crpath == NULL)
1465         return -1;
1466 
1467     rpath = str_shell_escape (crpath);
1468 
1469     vfs_get_timespecs_from_timesbuf (times, &atime, &mtime);
1470 
1471     gmt = gmtime (&atime.tv_sec);
1472     g_snprintf (utcatime, sizeof (utcatime), "%04d%02d%02d%02d%02d.%02d",
1473                 gmt->tm_year + 1900, gmt->tm_mon + 1, gmt->tm_mday,
1474                 gmt->tm_hour, gmt->tm_min, gmt->tm_sec);
1475     g_snprintf (utcatime_w_nsec, sizeof (utcatime_w_nsec), "%04d-%02d-%02d %02d:%02d:%02d.%09ld",
1476                 gmt->tm_year + 1900, gmt->tm_mon + 1, gmt->tm_mday,
1477                 gmt->tm_hour, gmt->tm_min, gmt->tm_sec, atime.tv_nsec);
1478 
1479     gmt = gmtime (&mtime.tv_sec);
1480     g_snprintf (utcmtime, sizeof (utcmtime), "%04d%02d%02d%02d%02d.%02d",
1481                 gmt->tm_year + 1900, gmt->tm_mon + 1, gmt->tm_mday,
1482                 gmt->tm_hour, gmt->tm_min, gmt->tm_sec);
1483     g_snprintf (utcmtime_w_nsec, sizeof (utcmtime_w_nsec), "%04d-%02d-%02d %02d:%02d:%02d.%09ld",
1484                 gmt->tm_year + 1900, gmt->tm_mon + 1, gmt->tm_mday,
1485                 gmt->tm_hour, gmt->tm_min, gmt->tm_sec, mtime.tv_nsec);
1486 
1487     me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath));
1488 
1489     ret = shell_send_command (me, super, OPT_FLUSH, SHELL_SUPER (super)->scr_utime,
1490                               "SHELL_FILENAME=%s SHELL_FILEATIME=%ju SHELL_FILEMTIME=%ju "
1491                               "SHELL_TOUCHATIME=%s SHELL_TOUCHMTIME=%s SHELL_TOUCHATIME_W_NSEC=\"%s\" "
1492                               "SHELL_TOUCHMTIME_W_NSEC=\"%s\";\n", rpath, (uintmax_t) atime.tv_sec,
1493                               (uintmax_t) mtime.tv_sec, utcatime, utcmtime, utcatime_w_nsec,
1494                               utcmtime_w_nsec);
1495 
1496     g_free (rpath);
1497 
1498     return ret;
1499 }
1500 
1501 /* --------------------------------------------------------------------------------------------- */
1502 
1503 static int
1504 shell_unlink (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1505 {
1506     const char *crpath;
1507     char *rpath;
1508     struct vfs_s_super *super;
1509     struct vfs_class *me;
1510     int ret;
1511 
1512     crpath = vfs_s_get_path (vpath, &super, 0);
1513     if (crpath == NULL)
1514         return -1;
1515 
1516     rpath = str_shell_escape (crpath);
1517 
1518     me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath));
1519 
1520     ret =
1521         shell_send_command (me, super, OPT_FLUSH, SHELL_SUPER (super)->scr_unlink,
1522                             "SHELL_FILENAME=%s;\n", rpath);
1523 
1524     g_free (rpath);
1525 
1526     return ret;
1527 }
1528 
1529 /* --------------------------------------------------------------------------------------------- */
1530 
1531 static int
1532 shell_exists (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1533 {
1534     const char *crpath;
1535     char *rpath;
1536     struct vfs_s_super *super;
1537     struct vfs_class *me;
1538     int ret;
1539 
1540     crpath = vfs_s_get_path (vpath, &super, 0);
1541     if (crpath == NULL)
1542         return -1;
1543 
1544     rpath = str_shell_escape (crpath);
1545 
1546     me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath));
1547 
1548     ret =
1549         shell_send_command (me, super, OPT_FLUSH, SHELL_SUPER (super)->scr_exists,
1550                             "SHELL_FILENAME=%s;\n", rpath);
1551 
1552     g_free (rpath);
1553 
1554     return (ret == 0 ? 1 : 0);
1555 }
1556 
1557 /* --------------------------------------------------------------------------------------------- */
1558 
1559 static int
1560 shell_mkdir (const vfs_path_t *vpath, mode_t mode)
     /* [previous][next][first][last][top][bottom][index][help]  */
1561 {
1562     const char *crpath;
1563     char *rpath;
1564     struct vfs_s_super *super;
1565     struct vfs_class *me;
1566     int ret;
1567 
1568     (void) mode;
1569 
1570     crpath = vfs_s_get_path (vpath, &super, 0);
1571     if (crpath == NULL)
1572         return -1;
1573 
1574     rpath = str_shell_escape (crpath);
1575 
1576     me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath));
1577 
1578     ret =
1579         shell_send_command (me, super, OPT_FLUSH, SHELL_SUPER (super)->scr_mkdir,
1580                             "SHELL_FILENAME=%s;\n", rpath);
1581     g_free (rpath);
1582 
1583     if (ret != 0)
1584         return ret;
1585 
1586     if (shell_exists (vpath) == 0)
1587     {
1588         me->verrno = EACCES;
1589         return -1;
1590     }
1591     return 0;
1592 }
1593 
1594 /* --------------------------------------------------------------------------------------------- */
1595 
1596 static int
1597 shell_rmdir (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1598 {
1599     const char *crpath;
1600     char *rpath;
1601     struct vfs_s_super *super;
1602     struct vfs_class *me;
1603     int ret;
1604 
1605     crpath = vfs_s_get_path (vpath, &super, 0);
1606     if (crpath == NULL)
1607         return -1;
1608 
1609     rpath = str_shell_escape (crpath);
1610 
1611     me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath));
1612 
1613     ret =
1614         shell_send_command (me, super, OPT_FLUSH, SHELL_SUPER (super)->scr_rmdir,
1615                             "SHELL_FILENAME=%s;\n", rpath);
1616 
1617     g_free (rpath);
1618 
1619     return ret;
1620 }
1621 
1622 /* --------------------------------------------------------------------------------------------- */
1623 
1624 static vfs_file_handler_t *
1625 shell_fh_new (struct vfs_s_inode *ino, gboolean changed)
     /* [previous][next][first][last][top][bottom][index][help]  */
1626 {
1627     shell_file_handler_t *fh;
1628 
1629     fh = g_new0 (shell_file_handler_t, 1);
1630     vfs_s_init_fh (VFS_FILE_HANDLER (fh), ino, changed);
1631 
1632     return VFS_FILE_HANDLER (fh);
1633 }
1634 
1635 /* --------------------------------------------------------------------------------------------- */
1636 
1637 static int
1638 shell_fh_open (struct vfs_class *me, vfs_file_handler_t *fh, int flags, mode_t mode)
     /* [previous][next][first][last][top][bottom][index][help]  */
1639 {
1640     shell_file_handler_t *shell = SHELL_FILE_HANDLER (fh);
1641 
1642     (void) mode;
1643 
1644     /* File will be written only, so no need to retrieve it */
1645     if (((flags & O_WRONLY) == O_WRONLY) && ((flags & (O_RDONLY | O_RDWR)) == 0))
1646     {
1647         /* user pressed the button [ Append ] in the "Copy" dialog */
1648         if ((flags & O_APPEND) != 0)
1649             shell->append = TRUE;
1650 
1651         if (fh->ino->localname == NULL)
1652         {
1653             vfs_path_t *vpath = NULL;
1654             int tmp_handle;
1655 
1656             tmp_handle = vfs_mkstemps (&vpath, me->name, fh->ino->ent->name);
1657             if (tmp_handle == -1)
1658                 return (-1);
1659 
1660             fh->ino->localname = vfs_path_free (vpath, FALSE);
1661             close (tmp_handle);
1662         }
1663         return 0;
1664     }
1665 
1666     if (fh->ino->localname == NULL && vfs_s_retrieve_file (me, fh->ino) == -1)
1667         return (-1);
1668 
1669     if (fh->ino->localname == NULL)
1670         vfs_die ("retrieve_file failed to fill in localname");
1671     return 0;
1672 }
1673 
1674 /* --------------------------------------------------------------------------------------------- */
1675 
1676 static void
1677 shell_fill_names (struct vfs_class *me, fill_names_f func)
     /* [previous][next][first][last][top][bottom][index][help]  */
1678 {
1679     GList *iter;
1680 
1681     for (iter = VFS_SUBCLASS (me)->supers; iter != NULL; iter = g_list_next (iter))
1682     {
1683         const struct vfs_s_super *super = (const struct vfs_s_super *) iter->data;
1684 
1685         char *name;
1686         char gbuf[10];
1687         const char *flags = "";
1688 
1689         switch (super->path_element->port)
1690         {
1691         case SHELL_FLAG_RSH:
1692             flags = ":r";
1693             break;
1694         case SHELL_FLAG_COMPRESSED:
1695             flags = ":C";
1696             break;
1697         default:
1698             if (super->path_element->port > SHELL_FLAG_RSH)
1699             {
1700                 g_snprintf (gbuf, sizeof (gbuf), ":%d", super->path_element->port);
1701                 flags = gbuf;
1702             }
1703             break;
1704         }
1705 
1706         name =
1707             g_strconcat (vfs_shell_ops->prefix, VFS_PATH_URL_DELIMITER,
1708                          super->path_element->user, "@", super->path_element->host, flags,
1709                          PATH_SEP_STR, super->path_element->path, (char *) NULL);
1710         func (name);
1711         g_free (name);
1712     }
1713 }
1714 
1715 /* --------------------------------------------------------------------------------------------- */
1716 
1717 static void *
1718 shell_open (const vfs_path_t *vpath, int flags, mode_t mode)
     /* [previous][next][first][last][top][bottom][index][help]  */
1719 {
1720     /*
1721        sorry, i've places hack here
1722        cause shell don't able to open files with O_EXCL flag
1723      */
1724     flags &= ~O_EXCL;
1725     return vfs_s_open (vpath, flags, mode);
1726 }
1727 
1728 /* --------------------------------------------------------------------------------------------- */
1729 /*** public functions ****************************************************************************/
1730 /* --------------------------------------------------------------------------------------------- */
1731 
1732 void
1733 vfs_init_shell (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1734 {
1735     tcp_init ();
1736 
1737     vfs_init_subclass (&shell_subclass, "shell", VFSF_REMOTE | VFSF_USETMP, "sh");
1738     vfs_shell_ops->fill_names = shell_fill_names;
1739     vfs_shell_ops->stat = shell_stat;
1740     vfs_shell_ops->lstat = shell_lstat;
1741     vfs_shell_ops->fstat = shell_fstat;
1742     vfs_shell_ops->chmod = shell_chmod;
1743     vfs_shell_ops->chown = shell_chown;
1744     vfs_shell_ops->utime = shell_utime;
1745     vfs_shell_ops->open = shell_open;
1746     vfs_shell_ops->symlink = shell_symlink;
1747     vfs_shell_ops->link = shell_link;
1748     vfs_shell_ops->unlink = shell_unlink;
1749     vfs_shell_ops->rename = shell_rename;
1750     vfs_shell_ops->mkdir = shell_mkdir;
1751     vfs_shell_ops->rmdir = shell_rmdir;
1752     vfs_shell_ops->ctl = shell_ctl;
1753     shell_subclass.archive_same = shell_archive_same;
1754     shell_subclass.new_archive = shell_new_archive;
1755     shell_subclass.open_archive = shell_open_archive;
1756     shell_subclass.free_archive = shell_free_archive;
1757     shell_subclass.fh_new = shell_fh_new;
1758     shell_subclass.fh_open = shell_fh_open;
1759     shell_subclass.dir_load = shell_dir_load;
1760     shell_subclass.file_store = shell_file_store;
1761     shell_subclass.linear_start = shell_linear_start;
1762     shell_subclass.linear_read = shell_linear_read;
1763     shell_subclass.linear_close = shell_linear_close;
1764     vfs_register_class (vfs_shell_ops);
1765 }
1766 
1767 /* --------------------------------------------------------------------------------------------- */

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