/*
   $VER: su.c V1.0 (27.07.96)
   Author: Finn Nielsen <zznyyd@diku.dk>
   Date: Sat 27-Jul-1996
   Copyright: © 1996 ZzNyYd! Design.
   Short: Spawn a new shell under another user.

   This command spawns a new shell under a user specified in the command
   line. This is done by logging in as that user, spawning a new shell 
   and then logging out again. 
   In addition the local variable $Home will be set if the login is 
   successful.

   During logout the muT_Quiet tag is suplied in order to avoid a login
   request if 'nobody' is spawning a new shell.
   To the authors of MultiUser:
	Please do NOT remove the muT_Quiet tag from the library because
	it is actually useful for this purpose.

   This command is pure and can be made resident.
*/

#define __USE_SYSBASE

#include <exec/types.h>
#include <utility/tagitem.h>
#include <exec/execbase.h>
#include <exec/memory.h>
#include <dos/dosextens.h>
#include <dos/rdargs.h>
#include <libraries/multiuser.h>

#include <string.h>

#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/multiuser.h>


// ------------------------------------------------------------------------
// ----- Strings ----------------------------------------------------------
// ------------------------------------------------------------------------

#define Template	"USERID/A,PW=PASSWD/K,PUB=PUBSCR/K"
#define LogBad		"su failed\n"
#define NomuFS		"Couldn't open multiuser.library V39+\n"
#define LF		"\n"
#define ColonSpace	": "
#define HomeVar		"Home"
#define CMD		"NewShell"

// ------------------------------------------------------------------------
// ----- Let the fun begin... ---------------------------------------------
// ------------------------------------------------------------------------
const char ver[]="$VER: su V1.0 (27.07.96)  by Finn Nielsen <zznyyd@diku.dk>";

long cmd_su(void)
{
  struct ExecBase	*SysBase = (*((struct ExecBase **) 4));
  struct DosLibrary	*DOSBase;
  struct muBase		*muBase;
  struct RDArgs		*rdargs;
  struct Process	*thisproc;
  long			opts[5], *optptr, tags[11], *tagptr;
  long			opt, user;
  long			pub = 0;
  long			rc = 0;

// ------------------------------------------------------------------------
// ----- Initialization ---------------------------------------------------
// ------------------------------------------------------------------------

  thisproc = (struct Process *)FindTask(NULL);
  if ((DOSBase = (struct DosLibrary *)OpenLibrary(DOSNAME, 37L)))
  {
    if ((muBase = (struct muBase *)OpenLibrary(MULTIUSERNAME, 39L)))
    {
      memset((char *)opts, 0, sizeof(opts));
      if (rdargs = ReadArgs(Template, opts, NULL))
      {
	while ( ((char *)opts)[0] == '?' && ((char *)opts)[1] == 0 )
	{
	  PutStr(Template); PutStr(ColonSpace); rdargs->RDA_Buffer=0;
	  memset((char *)opts, 0, sizeof(opts));
	  if (!ReadArgs(Template, opts, rdargs))
	  {
	    FreeArgs(rdargs); PrintFault(IoErr(), NULL);
	    rc=10; break;
	  }
	}
	if (!rc) {

// ------------------------------------------------------------------------
// ----- Argument parsing -------------------------------------------------
// ------------------------------------------------------------------------

	optptr=opts; tagptr = tags;

//	USERID
	if (opt = *optptr++)
	{
	  *tagptr++ = muT_UserID; *tagptr++ = opt;
	}

//	PASSWD/K
	if (opt = *optptr++)
	{
	  *tagptr++ = muT_Password; *tagptr++ = opt;
	}

//	PUBSCR/K
	if (opt = *optptr)
	{
	  *tagptr++ = muT_PubScrName; *tagptr++ = opt; pub = 1;
	}

//	GUI (automatic)
	if (pub || (long)thisproc->pr_WindowPtr != -1)
	{
	  *tagptr++ = muT_Graphical; *tagptr++ = 1;
	}

	*tagptr++ = muT_Quiet; *tagptr++ = 1;
	*tagptr = TAG_END;


// ------------------------------------------------------------------------
// ----- Do the magic... --------------------------------------------------
// ------------------------------------------------------------------------

	if (!rc)
	{
	  if (user = muLoginA((struct TagItem *)tags))
	  {
	    struct muUserInfo *info;
	    if (info = muAllocUserInfo())
	    {
	      info->uid = (UWORD)(user>>16);
	      if (muGetUserInfo(info, muKeyType_uid))
	      {
		SetVar(HomeVar, info->HomeDir, -1, GVF_LOCAL_ONLY);
	      }
	    }
	    Execute(CMD, NULL, Output());
	    user = muLogoutA((struct TagItem *)tags);
	    if (info)
	    {
	      if (user)
	      {
		info->uid = (UWORD)(user>>16);
		if (muGetUserInfo(info, muKeyType_uid))
		{
		  SetVar(HomeVar, info->HomeDir, -1, GVF_LOCAL_ONLY);
		}
	      } else DeleteVar(HomeVar, GVF_LOCAL_ONLY);
	      muFreeUserInfo(info);
	    }
	  } else { PutStr(LogBad); rc=10; }
	  thisproc->pr_Result2 = 0;
	}

// ------------------------------------------------------------------------
// ----- Cleanup + Error handling -----------------------------------------
// ------------------------------------------------------------------------

	FreeArgs(rdargs);
      }
      } else { rc=10; PrintFault(IoErr(), NULL); }
      CloseLibrary((struct Library *)muBase);
    } else { rc=20; PutStr(NomuFS); }
    CloseLibrary((struct Library *)DOSBase);
  } else { rc=20; thisproc->pr_Result2 = ERROR_INVALID_RESIDENT_LIBRARY; }
  return(rc);
}

