/*
**	$VER: rlogin.c 1.1 (08.02.94)
**
**	remote login command
**
**	© Copyright 1994 by Norbert Püschel
**	All Rights Reserved
*/

#include <proto/exec.h>
#include <proto/dos.h>
#include <clib/envoy_protos.h>
#include <pragmas/envoy_pragmas.h>
#include <clib/nipc_protos.h>
#include <pragmas/nipc_pragmas.h>
#include <clib/services_protos.h>
#include <pragmas/services_pragmas.h>
#include <clib/alib_stdio_protos.h>

#include <exec/memory.h>
#include <devices/conunit.h>
#include <dos/dosextens.h>
#include <envoy/envoy.h>

#include <string.h>

#include <debug.h>

struct Library *NIPCBase;
struct Library *ServicesBase;
struct Library *EnvoyBase;

long __oslibversion = 37;

const UBYTE version[] = "$VER: rlogin 1.1 " __AMIGADATE__ ;

UBYTE template[] = "HOST,USERID,GUI/S";
LONG args[3];

#define HBUFFLEN   256
#define UBUFFLEN   32
#define PWDBUFFLEN 16

#ifdef LATTICE
#ifndef __SASC_60
int CXBRK(void)     { return(0); }  /* Disable Lattice CTRL/C handling */
void chkabort(void) { return; }     /* really */
#else
void _CXBRK(void) {}
#endif
#endif

void killnl(STRPTR s)

{
  while(*s && *s != '\n' && *s != '\r') s++;
  *s = '\0';
}

BOOL out(STRPTR s)

{
  LONG l = strlen(s);

  return((BOOL)(Write(Output(),s,l) == l));
}

BOOL in(STRPTR s,LONG l)

{
  LONG r;

  r = Read(Input(),s,l-1);
  if(r > -1) {
    s[r] = '\0';
    return(TRUE);
  }
  else return(FALSE);
}

BOOL rawin(STRPTR s,LONG l)

{
  LONG len = 0;

  BPTR input = Input();
  BPTR output = Output();
  BOOL done = FALSE;

  SetMode(input,1);

  l--;

  while(len < l && !done) {
    if(Read(input,s,1) != 1) break;
    switch(*s) {
      case '\b':
        if(len) {
          Write(output,"\b",1);
          len--;
          s--;
        }
        break;
      case '\n':
      case '\r':
        done = TRUE;
      default:
        if(((*s & 0x7F) > 31 || *s == '\n' || *s == '\r') && *s != 127) {
          Write(output," ",1);
          len++;
          s++;
        }
        else {
          Write(output,"\007",1); /* bell */
        }
        break;
    }
  }
  Write(output,"\n",1);
  if(len == l) done = TRUE;
  *s = '\0';
  SetMode(input,0);
  return(done);
}

#define req(trans,n) (void *)(((UBYTE *)(trans->trans_RequestData))+(n))
#define resp(trans,n) (void *)(((UBYTE *)(trans->trans_ResponseData))+(n))

#define OFFS_CONUNIT  0
#define SIZE_CONUNIT  14*sizeof(WORD)
#define OFFS_RES1     OFFS_CONUNIT+SIZE_CONUNIT
#define SIZE_RES1     sizeof(LONG)
#define OFFS_RES2     OFFS_RES1+SIZE_RES1
#define SIZE_RES2     sizeof(LONG)
#define OFFS_RESPDATA OFFS_RES2+SIZE_RES2

#define OFFS_ARG1     0
#define SIZE_ARG1     sizeof(LONG)
#define OFFS_ARG2     OFFS_ARG1+SIZE_ARG1
#define SIZE_ARG2     sizeof(LONG)
#define OFFS_REQDATA  OFFS_ARG2+SIZE_ARG2

#define CONUNIT(ci) (APTR)&(((struct ConUnit *)(ci.ci_IOStdReq->io_Unit))->cu_XCP)

struct ConInfo {
  struct MinList   ci_Actions;
  BOOL             ci_IsOpen;
  struct Entity   *ci_Dest;
  struct Entity   *ci_Source;
  struct MsgPort  *ci_Handler;
  struct MsgPort  *ci_Port;
  struct IOStdReq *ci_IOStdReq;
} ci;

void do_packet(struct DosPacket *dp);

void do_transaction(struct Transaction *trans);

int main(int argc,char **argv) 

{
  UBYTE hname[HBUFFLEN];
  UBYTE uname[UBUFFLEN];
  UBYTE pwd[PWDBUFFLEN];
  int retval,rc2 = ERROR_NO_FREE_STORE;

  UBYTE ename[32];
  BYTE signum;
  ULONG mask,rmask;

#define bmask \
        (SIGBREAKF_CTRL_C|SIGBREAKF_CTRL_D|SIGBREAKF_CTRL_E|SIGBREAKF_CTRL_F)

  struct Transaction *trans;

  struct Message *msg;
  struct DosPacket *dp;
  struct InfoData *id;

  struct RDArgs *rda;

  if(argc == 0) return(20); /* no WB ! */

  rda = ReadArgs(template,args,0);
  if(rda == 0) return(10);
    
  if(args[0]) { /* host given */
    strncpy(hname,(STRPTR)(args[0]),HBUFFLEN);
  }
  
  if(args[1]) { /* user given */
    strncpy(uname,(STRPTR)(args[1]),UBUFFLEN);
  }

  if(args[2]) {
    EnvoyBase = OpenLibrary("envoy.library",37);
    if(EnvoyBase) {
      if(args[0] == 0) {
        retval = 
          HostRequest(HREQ_Buffer,hname,
                      HREQ_BuffSize,HBUFFLEN,
                      TAG_DONE) ? 0 : 5;
      }
      else retval = 0;
      if(retval == 0) {
        retval = 
          LoginRequest(LREQ_NameBuff,uname,
                       LREQ_NameBuffLen,UBUFFLEN,
                       LREQ_PassBuff,pwd,
                       LREQ_PassBuffLen,PWDBUFFLEN,
                       TAG_DONE) ? 0 : 5;
      }
      CloseLibrary(EnvoyBase);
    }
    else retval = 20;
  }
  else {
    if(args[0] == 0) {
      retval = (out("Host    : ") && in(hname,HBUFFLEN)) ? 0 : 5;
    }
    else retval = 0;
    if(retval == 0 && args[1] == 0) {
      retval = (out("User    : ") && in(uname,UBUFFLEN)) ? 0 : 5;
    }
    else retval = 0;
    if(retval == 0) { 
      retval = (out("Password: \033[8m") &&
                rawin(pwd,PWDBUFFLEN) &&
                out("\033[28m")) ? 0 : 5;
    }
    if(retval == 0) {
      killnl(hname);
      killnl(uname);
      killnl(pwd);
    }
    debug2("Password: %s\n",pwd);
  }

  FreeArgs(rda);

  if(retval) {
    out("Login failed.\n");
    return(retval);
  }

  if(!IsInteractive(Input()) || !IsInteractive(Output())) return(20);

  ci.ci_Handler = ((struct FileHandle *)(BADDR(Input())))->fh_Type;

  ci.ci_IOStdReq = 0;
  id = (struct InfoData *)AllocMem(sizeof(struct InfoData),MEMF_PUBLIC);
  if(id) {
    if(DoPkt(ci.ci_Handler,ACTION_DISK_INFO,MKBADDR(id),0,0,0,0)) {
      ci.ci_IOStdReq = (struct IOStdReq *)(id->id_InUse);
    }
    FreeMem(id,sizeof(struct InfoData));
  }

  retval = 20;

  ci.ci_Port = CreateMsgPort();
  if(ci.ci_Port) {
    signum = AllocSignal(-1);
    if(signum != -1) {
      NIPCBase = OpenLibrary("nipc.library",39);
      if(NIPCBase) {
        ServicesBase = OpenLibrary("services.library",37);
        if(ServicesBase) {
          sprintf(ename,"Remote Login %ld",
                  ((struct Process *)FindTask(0))->pr_TaskNum);
          ci.ci_Source = CreateEntity(ENT_Name,ename,
                                      ENT_Public,TRUE,
                                      ENT_Signal,signum,
                                      TAG_DONE);
          if(ci.ci_Source) {
            debug("Entity created\n");
            ci.ci_Dest = FindService(*hname ? hname : 0,
                                     "Remote Login",ci.ci_Source,
                                     FSVC_UserName,uname,
                                     FSVC_PassWord,pwd,
                                     FSVC_Error,&rc2,
                                     TAG_DONE);
            
            if(ci.ci_Dest) {
              debug("Service found\n");
              trans = AllocTransaction(TRN_AllocReqBuffer,
                                       SIZE_CONUNIT+strlen(ename)+1,
                                       TAG_DONE);
              if(trans) {
                debug("Transaction allocated\n");
                if(ci.ci_IOStdReq) {
                  trans->trans_Command = 1;
                  CopyMem(CONUNIT(ci),req(trans,OFFS_CONUNIT),SIZE_CONUNIT);
                }
                strcpy((STRPTR)req(trans,SIZE_CONUNIT),ename);
                DoTransaction(ci.ci_Dest,ci.ci_Source,trans);
                retval = trans->trans_Error;
                FreeTransaction(trans);
                if(retval == 0) {
                  NewList((struct List *)&(ci.ci_Actions));
                  ci.ci_IsOpen = TRUE;

                  mask = (1L<<signum)|(1L<<ci.ci_Port->mp_SigBit)|bmask;

                  do {
                    rmask = Wait(mask);
                    if(rmask &= bmask) goto do_break;
                    do {
                      if(rmask = bmask & SetSignal(0,bmask)) {
                      do_break:
                        trans = AllocTransaction(TAG_DONE);
                        if(trans) {
                          trans->trans_Command = rmask>>12;
                          BeginTransaction(ci.ci_Dest,ci.ci_Source,trans);
                        }
                      }
                      if(msg = GetMsg(ci.ci_Port)) {
                        dp = (struct DosPacket *)(msg->mn_Node.ln_Name);
                        do_packet(dp);
                      }
                      if(trans = GetTransaction(ci.ci_Source)) {
                        if(trans->trans_Type == TYPE_RESPONSE) {
                          FreeTransaction(trans);
                        }
                        else {
                          do_transaction(trans);
                        }
                      }
                    } while(msg != 0 || trans != 0);                    
                  } while(ci.ci_IsOpen);
                  out("\n");
                }
              }
              LoseEntity(ci.ci_Dest);
            }
            DeleteEntity(ci.ci_Source);
          }
          CloseLibrary(ServicesBase);
        }
        CloseLibrary(NIPCBase);
      }
      FreeSignal(signum);
    }
    DeleteMsgPort(ci.ci_Port);
  }
  if(retval) {
    out("Login failed.\n");
    SetIoErr(rc2);
  }
  return(retval);
}

struct ActionNode {
  struct MinNode      an_Node;
  struct DosPacket   *an_Packet;
  struct Transaction *an_Trans;
};

void do_packet(struct DosPacket *dp)

{
  struct ActionNode *an;
  struct Transaction *trans;

  an = (struct ActionNode *)(ci.ci_Actions.mlh_Head);
  while(an->an_Node.mln_Succ) {
    if(an->an_Packet == dp) break;
    an = (struct ActionNode *)(an->an_Node.mln_Succ);
  }
  if(an->an_Node.mln_Succ) {
    trans = an->an_Trans;
    if(ci.ci_IOStdReq) {
      CopyMem(CONUNIT(ci),resp(trans,OFFS_CONUNIT),SIZE_CONUNIT);
    }
    *(LONG *)resp(trans,OFFS_RES1) = dp->dp_Res1;
    *(LONG *)resp(trans,OFFS_RES2) = dp->dp_Res2;
    ReplyTransaction(trans);
    Remove((struct Node *)an);
    FreeMem(an,sizeof(struct ActionNode));
  }
  FreeDosObject(DOS_STDPKT,dp);
}

void do_transaction(struct Transaction *trans)

{
  struct ActionNode *an;
  struct DosPacket *dp;
  struct Message *msg;

  if(trans->trans_Command == 0) {
    an = (struct ActionNode *)AllocMem(sizeof(struct ActionNode),0);
    if(an) {
      dp = (struct DosPacket *)AllocDosObjectTags(DOS_STDPKT,TAG_DONE);
      if(dp) {
        dp->dp_Type = *(LONG *)req(trans,OFFS_ARG1);
        switch(dp->dp_Type) {
          case ACTION_READ:
          case ACTION_WRITE:
          case 2001: /* ACTION_FORCE */
          case 2002: /* ACTION_STACK */
          case 2003: /* ACTION_QUEUE */
            dp->dp_Arg1 = 0;
            dp->dp_Arg3 = *(LONG *)req(trans,OFFS_ARG2);
            if(dp->dp_Type == ACTION_READ) {
              dp->dp_Arg2 = (LONG)resp(trans,OFFS_RESPDATA);
            }
            else {
              dp->dp_Arg2 = (LONG)req(trans,OFFS_REQDATA);
            }          
            break;
          case ACTION_WAIT_CHAR:
          case ACTION_SCREEN_MODE:
          case 2004: /* ACTION_DROP */
            dp->dp_Arg1 = *(LONG *)req(trans,OFFS_ARG2);
            break;
          default:
            if(ci.ci_IOStdReq) {
              CopyMem(CONUNIT(ci),resp(trans,OFFS_CONUNIT),SIZE_CONUNIT);
            }
            *(LONG *)resp(trans,OFFS_RES1) = 
              (dp->dp_Type == ACTION_SEEK) ? -1 : DOSFALSE;
            *(LONG *)resp(trans,OFFS_RES2) = ERROR_ACTION_NOT_KNOWN;
            ReplyTransaction(trans);
            FreeDosObject(DOS_STDPKT,dp);
            FreeMem(an,sizeof(struct ActionNode));
            return;
        }
        an->an_Packet = dp;
        an->an_Trans = trans;
        AddTail((struct List *)&(ci.ci_Actions),(struct Node *)an);
        dp->dp_Port = ci.ci_Port;
        PutMsg(ci.ci_Handler,dp->dp_Link);
        return;
      }
      FreeMem(an,sizeof(struct ActionNode));
    }
  }
  else {
    an = (struct ActionNode *)(ci.ci_Actions.mlh_Head);
    while(an->an_Node.mln_Succ) {
      dp = an->an_Packet;
      AbortPkt(ci.ci_Handler,dp);
      if(ci.ci_IOStdReq) {
        CopyMem(CONUNIT(ci),resp(trans,OFFS_CONUNIT),SIZE_CONUNIT);
      }
      if(dp->dp_Type == ACTION_READ || dp->dp_Type == ACTION_WRITE) {
        *(LONG *)resp(an->an_Trans,OFFS_RES1) = -1;
      }
      else {
        *(LONG *)resp(an->an_Trans,OFFS_RES1) = DOSFALSE;
      }
      *(LONG *)resp(an->an_Trans,OFFS_RES2) = ERROR_INVALID_LOCK;
      ReplyTransaction(an->an_Trans);
      an = (struct ActionNode *)(an->an_Node.mln_Succ);
    }      
    while(!IsListEmpty((struct List *)&(ci.ci_Actions))) {
      WaitPort(ci.ci_Port);
      while(msg = GetMsg(ci.ci_Port)) {
        dp = (struct DosPacket *)(msg->mn_Node.ln_Name);
        an = (struct ActionNode *)(ci.ci_Actions.mlh_Head);
        while(an->an_Node.mln_Succ) {
          if(an->an_Packet == dp) break;
          an = (struct ActionNode *)(an->an_Node.mln_Succ);
        }
        if(an->an_Node.mln_Succ) {
          Remove((struct Node *)an);
          FreeMem(an,sizeof(struct ActionNode));
        }
        FreeDosObject(DOS_STDPKT,dp);
      }
    }  
    ReplyTransaction(trans);
    ci.ci_IsOpen = FALSE;
  }   
}
