/* CVS rsh client stuff.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.  */
#include <sys/cdefs.h>
__RCSID("$NetBSD: rsh-client.c,v 1.2.8.1 2018/02/12 00:20:01 snj Exp $");

#include <config.h>

#include "cvs.h"
#include "buffer.h"

#ifdef CLIENT_SUPPORT

#include "rsh-client.h"

#ifndef NO_EXT_METHOD

/* Contact the server by starting it with rsh.  */

/* Right now, we have two different definitions for this function,
   depending on whether we start the rsh server using popenRW or not.
   This isn't ideal, and the best thing would probably be to change
   the OS/2 port to be more like the regular Unix client (i.e., by
   implementing piped_child)... but I'm doing something else at the
   moment, and wish to make only one change at a time.  -Karl */

# ifdef START_RSH_WITH_POPEN_RW



/* This is actually a crock -- it's OS/2-specific, for no one else
   uses it.  If I get time, I want to make piped_child and all the
   other stuff in os2/run.c work right.  In the meantime, this gets us
   up and running, and that's most important. */
void
start_rsh_server (cvsroot_t *root, struct buffer **to_server_p,
                  struct buffer **from_server_p)
{
    int pipes[2];
    int child_pid;

    /* If you're working through firewalls, you can set the
       CVS_RSH environment variable to a script which uses rsh to
       invoke another rsh on a proxy machine.  */
    char *cvs_rsh = (root->cvs_rsh != NULL
		     ? root->cvs_rsh : getenv ("CVS_RSH"));
    char *cvs_server = (root->cvs_server != NULL
			? root->cvs_server : getenv ("CVS_SERVER"));
    int i = 0;
    /* This needs to fit "rsh", "-b", "-l", "USER", "--", "host",
       "cmd (w/ args)", and NULL.  We leave some room to grow. */
    char *rsh_argv[16];

    if (!cvs_rsh || !*cvs_rsh)
	/* People sometimes suggest or assume that this should default
	   to "remsh" on systems like HPUX in which that is the
	   system-supplied name for the rsh program.  However, that
	   causes various problems (keep in mind that systems such as
	   HPUX might have non-system-supplied versions of "rsh", like
	   a Kerberized one, which one might want to use).  If we
	   based the name on what is found in the PATH of the person
	   who runs configure, that would make it harder to
	   consistently produce the same result in the face of
	   different people producing binary distributions.  If we
	   based it on "remsh" always being the default for HPUX
	   (e.g. based on uname), that might be slightly better but
	   would require us to keep track of what the defaults are for
	   each system type, and probably would cope poorly if the
	   existence of remsh or rsh varies from OS version to OS
	   version.  Therefore, it seems best to have the default
	   remain "rsh", and tell HPUX users to specify remsh, for
	   example in CVS_RSH or other such mechanisms to be devised,
	   if that is what they want (the manual already tells them
	   that).  */
	cvs_rsh = RSH_DFLT;
    if (!cvs_server)
	cvs_server = "cvs";

    /* The command line starts out with rsh. */
    rsh_argv[i++] = cvs_rsh;

#   ifdef RSH_NEEDS_BINARY_FLAG
    /* "-b" for binary, under OS/2. */
    rsh_argv[i++] = "-b";
#   endif /* RSH_NEEDS_BINARY_FLAG */

    /* Then we strcat more things on the end one by one. */
    if (root->username != NULL)
    {
	rsh_argv[i++] = "-l";
	rsh_argv[i++] = root->username;
    }

    /* Only non-option arguments from here. (CVE-2017-12836) */
    rsh_argv[i++] = "--";

    rsh_argv[i++] = root->hostname;
    rsh_argv[i++] = cvs_server;
    rsh_argv[i++] = "server";

    /* Mark the end of the arg list. */
    rsh_argv[i]   = NULL;

    if (trace)
    {
	fprintf (stderr, " -> Starting server: ");
	for (i = 0; rsh_argv[i]; i++)
	    fprintf (stderr, "%s ", rsh_argv[i]);
	putc ('\n', stderr);
    }

    /* Do the deed. */
    child_pid = popenRW (rsh_argv, pipes);
    if (child_pid < 0)
	error (1, errno, "cannot start server via rsh");

    /* Give caller the file descriptors in a form it can deal with. */
    make_bufs_from_fds (pipes[0], pipes[1], child_pid, to_server_p,
                        from_server_p, 0);
}

# else /* ! START_RSH_WITH_POPEN_RW */

void
start_rsh_server (cvsroot_t *root, struct buffer **to_server_p,
                  struct buffer **from_server_p)
{
    /* If you're working through firewalls, you can set the
       CVS_RSH environment variable to a script which uses rsh to
       invoke another rsh on a proxy machine.  */
    char *cvs_rsh = (root->cvs_rsh != NULL
		     ? root->cvs_rsh : getenv ("CVS_RSH"));
    char *cvs_server = (root->cvs_server != NULL
			? root->cvs_server : getenv ("CVS_SERVER"));
    char *command;
    int tofd, fromfd;
    int child_pid;

    if (!cvs_rsh)
	cvs_rsh = RSH_DFLT;
    if (!cvs_server)
	cvs_server = "cvs";

    /* Pass the command to rsh as a single string.  This shouldn't
     * affect most rsh servers at all, and will pacify some buggy
     * versions of rsh that grab switches out of the middle of the
     * command (they're calling the GNU getopt routines incorrectly).
     *
     * If you are running a very old (Nov 3, 1994, before 1.5)
     * version of the server, you need to make sure that your .bashrc
     * on the server machine does not set CVSROOT to something
     * containing a colon (or better yet, upgrade the server).
     */
    command = Xasprintf ("%s server", cvs_server);

    {
        char *argv[16];
	char **p = argv;

	*p++ = cvs_rsh;

	/* If the login names differ between client and server
	 * pass it on to rsh.
	 */
	if (root->username != NULL)
	{
	    *p++ = "-l";
	    *p++ = root->username;
	}

	*p++ = "--";

	*p++ = root->hostname;
	*p++ = command;
	*p++ = NULL;

	if (trace)
        {
	    int i;

            fprintf (stderr, " -> Starting server: ");
	    for (i = 0; argv[i]; i++)
	        fprintf (stderr, "%s ", argv[i]);
	    putc ('\n', stderr);
	}
	child_pid = piped_child (argv, &tofd, &fromfd, true);

	if (child_pid < 0)
	    error (1, errno, "cannot start server via rsh");
    }
    free (command);

    make_bufs_from_fds (tofd, fromfd, child_pid, root, to_server_p,
                        from_server_p, 0);
}

# endif /* START_RSH_WITH_POPEN_RW */

#endif /* NO_EXT_METHOD */

#endif /* CLIENT_SUPPORT */
