/**
 * Scout - The Amiga System Monitor
 *
 *------------------------------------------------------------------
 *
 * 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 of the License, or
 * 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * You must not use this source code to gain profit of any kind!
 *
 *------------------------------------------------------------------
 *
 * @author Andreas Gelhausen
 * @author Richard Körber <rkoerber@gmx.de>
 */



#include "system_headers.h"

extern APTR    AP_Scout;
extern STRPTR  _ProgramName;

/********************************************************************/
/*                      Benötigte Prototypes                        */
/********************************************************************/

struct Library *MyOpenLibrary (char *, ULONG);

void PrintAllocations(char *);
void PrintAssigns(char *);
void PrintCommands(char *);
void PrintDevices(char *);
void PrintExpansions(char *);
void PrintFonts(char *);
void PrintInputHandlers(char *);
void PrintInterrupts(char *);
void PrintLibraries(char *);
void PrintMemory(char *);
void PrintMounts(char *);
void PrintLocks(char *);
void PrintPorts(char *);
void PrintResidents(char *);
void PrintSemaphores(char *);
void PrintTasks(char *);
void PrintResources(char *);
void PrintVectors(char *);
void PrintWindows(char *);
void PrintLowMemory(char *);
void PrintCx(char *);
void PrintClass(char *);
void PrintSMode(char *);
void PrintSystem(char *);
void PrintTimer(char *);

void SendAlcList(void);
void SendAssList(void);
void SendComList(void);
void SendDevList(void);
void SendExpList(void);
void SendFontList(void);
void SendInputList(void);
void SendIntList(void);
void SendLibList(void);
void SendLockList(void);
void SendMemList(void);
void SendMountList(void);
void SendPortList(void);
void SendResList(void);
void SendResiList(void);
void SendSemList(void);
void SendTaskList(void);
void SendVectorList(void);
void SendWinList(void);
void SendLowMemory(void);
void SendCxList(void);
void SendClassList(void);
void SendSModeList(void);
void SendSystemList(void);
void SendTimerList(void);

void PrintNetHelp (void);
long SendResultString (char *fmt, ...);
int MyRequest (char *gadgets, char *fmt, ...);

#define NAMELEN      32
#define LINELEN      82
#define TRENNCHAR    '\0'

#define aprintf      Printf

char  *CMD_BEGIN = "BEGIN";
char  *CMD_USER = "USER";
char  *CMD_PASSWORD = "PASSWORD";
char  *CMD_END = "END";
char  *CMD_DONE = "DONE";
char  *CMD_ERROR = "ERROR";
char  *CMD_CONNECTED = "CONNECTED";

static struct  RDArgs      *rdargs;
static struct  RDArgs      *myrdargs;

int     server_socket = -1;
int     client_socket = -1;
int     connected = FALSE;
static long    type = SOCK_STREAM;

static long    sinlen;
static struct  sockaddr_in sin;

BOOL clientstate = FALSE;
BOOL serverstate = FALSE;
BOOL shellstate = FALSE;

char username[10+1];
char password[_PASSWORD_LEN+1];


/*
int logprint (char *fmt, ...) {
   int   rc = TRUE;
   char  buf[100];
   char  *logfilename = "basket:scout.log";
   BPTR  handle;

   va_list args;
   va_start(args,fmt);
   _sprintf (buf, fmt, args);

   if (((handle = Open (logfilename, MODE_OLDFILE)) \
     && (Seek (handle, 0, OFFSET_END) != -1)) \
     || (handle = Open (logfilename, MODE_NEWFILE))) {
      if (Write (handle, buf, strlen (buf)) == -1) {
         rc = FALSE;
      }
      Close (handle);
   }
   va_end (args);
   return (rc);
}
*/

void failtcp (void) {
   if (client_socket >= 0) {
      if (connected) {
         shutdown (client_socket, 2);
      }
      CloseSocket (client_socket);
   }

//   _STD_500_stdio_term();

   if (UserGroupBase) CloseLibrary (UserGroupBase);

//   _STD_200_closeTimer();

   if (SocketBase) CloseLibrary (SocketBase);
}

int inittcp (void) {
   SocketBase = OpenLibrary ("bsdsocket.library", 4L);
   if (! SocketBase)
      return (FALSE);

//   _STI_200_openTimer();

   UserGroupBase = OpenLibrary ("usergroup.library", 4);
   if (! UserGroupBase)
      return (FALSE);

//   _STI_500_stdio_init();
//   _STI_510_install_AmiTCP_callback();

   return (TRUE);
}

static int sgetc (int sock) {
   unsigned char c;
   fd_set rd,ex;
   ULONG flgs;
   int n;

   struct timeval t;
   t.tv_sec = 10L;
   t.tv_usec = 0;

   FD_ZERO(&rd);
   FD_ZERO(&ex);

   FD_SET(sock,&rd);
   FD_SET(sock,&ex);

   if (clientstate) {
      flgs = 0;
      WaitSelect(16,&rd,0L,&ex,&t,&flgs);
   } else {
      flgs = SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D;
      WaitSelect(16,&rd,0L,&ex,NULL,&flgs);
   }

   if (FD_ISSET(sock,&rd)) {
      n = recv(sock, &c, 1, 0);
      if (n == 1) {
         return c;
      } else {
         return -1;
      }
   } else {
      return -1;
   }
}

int sgets (int sock, char *string, int maxchars) {
   int   nchars = 0;
   char  c;

   while ((nchars < maxchars) && ((c = sgetc(sock)) != EOF) \
     && (c != TRENNCHAR)) {
      if ((!clientstate) && (c == '\n')) {
//         break;
      }
      string[nchars] = c;
      nchars++;
   }
   string[nchars] = '\0';
   return (nchars);
}

#if 0
static char *GetDate (void) {
   time_t   clock = time (NULL);
   return (ctime (&clock));
}
#endif

/*
static int StripCRLF (char *buffer) {
   int len = strlen (buffer);
   
   while (len && ((buffer[len-1] == '\n') \
     || (len && (buffer[len-1] == '\r')))) {
      buffer[--len] = '\0';
   }
   return (len);
}
*/

static char    NetHelpText[] = "\n" \
   " Available lists:\n" \
   " ----------------\n" \
   " Allocations     Assigns         BoopsiClasses   Commands\n" \
   " Commodities     Devices         Expansions      Fonts\n" \
   " InputHandlers   Interrupts      Libraries       Locks\n" \
   " LowMemory       Memory          Mounts          Ports\n" \
   " Residents       Resources       ScreenMode      Semaphores\n" \
   " System          Tasks           Timer           Vectors\n" \
   " Windows\n\n" \
   " Other commands:\n" \
   " ---------------\n" \
   " Help";

void PrintNetHelp (void)
{
   if (serverstate || shellstate) {
      int i = 0;
      UBYTE *tmpstr;

      if (tmpstr = AllocVec(256, MEMF_ANY)) {
         SendResultString (NetHelpText);

         while (arexx_list[i].mc_Name) {
            strcpy (tmpstr, " ");
            strcat (tmpstr, arexx_list[i].mc_Name);
            if (arexx_list[i].mc_Parameters) {
               strcat (tmpstr, " ");
               strcat (tmpstr, arexx_list[i].mc_Template);
            }
            SendResultString (tmpstr);
            i++;
         }

         FreeVec(tmpstr);
      }
   }
}

/*
** Dieser Hook wird benötigt um die ARexx-Command-Liste für
**  die TCP_Befehle nutzen zu können!
*/
struct NetHook {
   struct   MinNode  h_egal1;
   __asm    ULONG    (*h_Entry) (register __a1 ULONG *);
   ULONG    (*h_egal2)();
   APTR     h_egal3;
};

struct Net_Command {
   char  *nc_Name;
   void  (*nc_func) (char *);
};

struct Net_Command net_list[] = {
   "Allocations",    PrintAllocations,
   "Assigns",        PrintAssigns,
   "BoopsiClasses",  PrintClass,
   "Commands",       PrintCommands,
   "Commodities",    PrintCx,
   "Devices",        PrintDevices,
   "Expansions",     PrintExpansions,
   "Fonts",          PrintFonts,
   "InputHandlers",  PrintInputHandlers,
   "Interrupts",     PrintInterrupts,
   "Libraries",      PrintLibraries,
   "LowMemory",      PrintLowMemory,
   "Memory",         PrintMemory,
   "Mounts",         PrintMounts,
   "Locks",          PrintLocks,
   "Ports",          PrintPorts,
   "Residents",      PrintResidents,
   "ScreenMode",     PrintSMode,
   "Semaphores",     PrintSemaphores,
   "System",         PrintSystem,
   "Tasks",          PrintTasks,
   "Timer",          PrintTimer,
   "Resources",      PrintResources,
   "Vectors",        PrintVectors,
   "Windows",        PrintWindows,
   "Help",           (void (* ) (char *)) PrintNetHelp,
   "a",              PrintAllocations,
   "b",              PrintClass,
   "c",              PrintCommands,
   "d",              PrintDevices,
   "e",              PrintTimer,
   "f",              PrintFonts,
   "g",              PrintAssigns,
   "h",              PrintInputHandlers,
   "i",              PrintInterrupts,
   "j",              PrintLowMemory,
   "k",              PrintCx,
   "l",              PrintLibraries,
   "m",              PrintMemory,
   "n",              PrintMounts,
   "o",              PrintLocks,
   "p",              PrintPorts,
   "r",              PrintResidents,
   "s",              PrintSemaphores,
   "t",              PrintTasks,
   "u",              PrintResources,
   "v",              PrintVectors,
   "w",              PrintWindows,
   "x",              PrintExpansions,
   "y",              PrintSystem,
   "z",              PrintSMode,
   "GetAlcList",     (void (* ) (char *)) SendAlcList,
   "GetAssList",     (void (* ) (char *)) SendAssList,
   "GetClassList",   (void (* ) (char *)) SendClassList,
   "GetComList",     (void (* ) (char *)) SendComList,
   "GetCxList",      (void (* ) (char *)) SendCxList,
   "GetDevList",     (void (* ) (char *)) SendDevList,
   "GetExpList",     (void (* ) (char *)) SendExpList,
   "GetFontList",    (void (* ) (char *)) SendFontList,
   "GetInputList",   (void (* ) (char *)) SendInputList,
   "GetIntList",     (void (* ) (char *)) SendIntList,
   "GetLibList",     (void (* ) (char *)) SendLibList,
   "GetLockList",    (void (* ) (char *)) SendLockList,
   "GetLowMemList",  (void (* ) (char *)) SendLowMemory,
   "GetMemList",     (void (* ) (char *)) SendMemList,
   "GetMountList",   (void (* ) (char *)) SendMountList,
   "GetPortList",    (void (* ) (char *)) SendPortList,
   "GetResList",     (void (* ) (char *)) SendResList,
   "GetResiList",    (void (* ) (char *)) SendResiList,
   "GetSemList",     (void (* ) (char *)) SendSemList,
   "GetSModeList",   (void (* ) (char *)) SendSModeList,
   "GetSysList",     (void (* ) (char *)) SendSystemList,
   "GetTaskList",    (void (* ) (char *)) SendTaskList,
   "GetTimerList",   (void (* ) (char *)) SendTimerList,
   "GetVectorList",  (void (* ) (char *)) SendVectorList,
   "GetWinList",     (void (* ) (char *)) SendWinList,
   NULL
};

/********************************************************************/
/*                              Client                              */
/********************************************************************/

int SendDaemon (char *fmt, ...) {
   int   result = FALSE;
   char  buf[LINELEN];

   va_list args;
   va_start (args,fmt);
   RawDoFmt(fmt, args, (void (*)())"\x16\xc0\x4e\x75", buf);

   if (send (client_socket, buf, strlen (buf) + 1, 0) == strlen (buf) + 1) { 
      result = TRUE;
   }
   va_end (args);
   return (result);
}

static ULONG NetCommand (char *text) {
   ULONG rc = RETURN_FAIL;
   char  buf[LINELEN];

   if (send (client_socket, text, strlen (text) + 1, 0) == strlen (text) + 1) { 
// aprintf ("COMMAND '%s' send\n", text);
      while (sgets (client_socket, buf, LINELEN)) {
         if (strcmp (buf, CMD_DONE) == 0) {
            rc = RETURN_OK;
// aprintf ("DONE\n");
            break;
         } else if (strcmp (buf, CMD_ERROR) == 0) {
// aprintf ("ERROR\n");
            break;
         }
      }
   }
   return (rc);
}

int ReceiveDecodedEntry (UBYTE *structure, int length) {
   int   rc = FALSE, i = 8;

   if (sgets (client_socket, structure + 8, length - 8)) {
      if ((strcmp (structure + 8, CMD_ERROR)) \
        && (strcmp (structure + 8, CMD_DONE))) {
         while (i < length - 1) {
            if (structure[i] == '\1')
               structure[i] = '\0';
            i++;
         }
         rc = TRUE;
      }
   }
   return (rc);
}

int ConnectToServer (void) {
   char     hostname[NAMELEN];
   char     buffer[LINELEN+2];
//   struct   servent     *service;
   struct   hostent     *host;
   struct   sockaddr_in server;

   if (! inittcp()) {
      aprintf ("Failed to initialize AmiTCP version 4!\n");
      return (FALSE);
   }

   if (opts.User) {
      strncpy (username, opts.User, 10);
   } else {
      strncpy (username, getlogin(), 10);
   }

   if (opts.Password) {
      strncpy (password, opts.Password, _PASSWORD_LEN);
   } else {
      strncpy (password, getpass ("Password:"), _PASSWORD_LEN);
      if (password[0] == '\0') {
         aprintf ("Failed to get password!\n");
         return (FALSE);
      }
   }

   gethostname (hostname, sizeof (hostname));
//   service = getservbyname ("scout","tcp");

   if (host = gethostbyname (opts.Host)) {

      bzero ((char *) &server, sizeof(server));
//      server.sin_port = service->s_port;
      server.sin_port = 6543;
      bcopy (host->h_addr, (char *) &server.sin_addr, host->h_length);
      server.sin_family = host->h_addrtype;

      client_socket = socket (AF_INET, type, 0);
      if (client_socket >= 0) {
         if (connect (client_socket, (struct sockaddr *) &server, sizeof (server)) != -1) {
            connected = TRUE;
            if (SendDaemon (CMD_BEGIN)) {
               if ((SendDaemon ("%s %s", CMD_USER, username)) \
                 && (SendDaemon ("%s %s", CMD_PASSWORD, password))) {

                  if ((sgets (client_socket, buffer, LINELEN)) \
                    && (strcmp (buffer, CMD_CONNECTED))) {
                     aprintf (buffer);
                  } else {
                     return (TRUE);
                  }
               }
            }
         } else {
            aprintf ("Couldn't connect to server\n");
         }
      } else {
         aprintf ("Couldn't create socket\n");
      }
   } else {
      aprintf ("Host is not available\n");
   }
   return (FALSE);
}

ULONG netshellclient (VOID) {
   ULONG    rc = RETURN_FAIL;
   char     recvbuffer[LINELEN+2];

   if (clientstate = ConnectToServer()) {
      if (SendDaemon ("%s ", opts.Command)) {

         while ((sgets (client_socket, recvbuffer, LINELEN)) \
           && (strcmp (recvbuffer, CMD_ERROR)) \
           && (strcmp (recvbuffer, CMD_DONE))) {
            aprintf (recvbuffer);
         }
         if (strcmp (recvbuffer, CMD_DONE) == 0) {
            rc = RETURN_OK;
         } else {
            rc = RETURN_ERROR;
         }
         SendDaemon ("END");
      }
   }
   return (rc);
}

/********************************************************************/
/*                              Daemon                              */
/********************************************************************/

int isNetCall (void) {
   int   rc = TRUE;

   if (! inittcp()) {
      return (FALSE);
   }

   server_socket = init_inet_daemon();
   if (server_socket >= 0) {
      set_socket_stdio (server_socket);
      sinlen = sizeof (sin);
      if (getpeername (0, (struct sockaddr *) &sin, &sinlen) == -1) {
//         logprint ("scout: getpeername() failed\n");
         rc = FALSE;
      }
   } else {
//      logprint ("scout: init_inet_daemon() failed\n");
      rc = FALSE;
   }
   return (rc);
}

int isCommand (char *buffer, char *command) {
   int len = strlen (command);
   int rc = FALSE;

   if ((strnicmp (buffer, command, len) == 0) \
     && ((isspace (buffer[len])) || (buffer[len] == '\0'))) {
      rc = len + 1;
   }
   return (rc);
}

int SendClient (char *fmt, ...) {
   int   result = FALSE;
   char  buf[2048];

   va_list args;
   va_start(args,fmt);
   RawDoFmt(fmt, args, (void (*)())"\x16\xc0\x4e\x75", buf);

   if (send (0, buf, strlen (buf) + 1, 0) == strlen (buf) + 1) { 
      result = TRUE;
   }
   va_end (args);
   return (result);
}

int SendEncodedEntry (UBYTE *structure, int length) {
   int   i = 8;

   while (i < length - 1) {
      if (structure[i] == '\0')
         structure[i] = '\1';
      i++;
   }
   if (SendClient (structure + 8)) { 
      return (FALSE);
   }
   return (TRUE);
}

long SendResultString (char *fmt, ...) {
   char  buf[2048];

   va_list args;
   va_start (args,fmt);
   RawDoFmt(fmt, args, (void (*)())"\x16\xc0\x4e\x75", buf);

   if (serverstate) {
      strcat (buf, "\n");
      SendClient (buf);
   } else if (shellstate) {
      strcat (buf, "\n");
      Printf (buf);
   } else {
      set (AP_Scout, MUIA_Application_RexxString, buf);
   }
   va_end (args);
   return (TRUE);
}

int PrintOneLine (BPTR hd, char *text) {
   if (serverstate) {
      return (SendClient (text));
   } else {
      return (Write (hd, text, strlen (text)) != -1);
   }
}

ULONG ExecuteCommand (char *text) {
   ULONG    rc = RETURN_OK;
   char     buffer[256];
   int      i, len;

   #define  CMDOPT_TEMPLATE "CMD1/A"
   #define  CMDOPT_COUNT    4
   LONG     cmdopts[CMDOPT_COUNT];

   _sprintf (buffer, "%s ", text);

   i = 0;
   while (arexx_list[i].mc_Name) {
      if (len = isCommand (buffer, arexx_list[i].mc_Name)) {

         if (myrdargs = AllocDosObject (DOS_RDARGS,NULL)) {
            myrdargs->RDA_Source.CS_Buffer = buffer + len;
            myrdargs->RDA_Source.CS_Length = strlen (buffer) - len;

            if ((arexx_list[i].mc_Parameters && (rdargs = ReadArgs (arexx_list[i].mc_Template, (LONG *) &cmdopts, myrdargs))) \
              || (!arexx_list[i].mc_Parameters)) {

               rc = (*((struct NetHook *) arexx_list[i].mc_Hook)->h_Entry) ((ULONG *) &cmdopts[0]);
               FreeArgs (rdargs);
            } else {

               Fault (IoErr(), NULL, (char *) buffer, LINELEN);
               SendResultString (buffer);
               rc = RETURN_FAIL;
            }
            FreeDosObject (DOS_RDARGS,myrdargs);
         }
         break;
      }
      i++;
   }

   if (! (arexx_list[i].mc_Name)) {
      i = 0;
      while (net_list[i].nc_Name) {
         if (len = isCommand (buffer, net_list[i].nc_Name)) {

            if (myrdargs = AllocDosObject (DOS_RDARGS,NULL)) {
               myrdargs->RDA_Source.CS_Buffer = buffer;
               myrdargs->RDA_Source.CS_Length = strlen (buffer);

               if (rdargs = ReadArgs (CMDOPT_TEMPLATE, (LONG *) &cmdopts, myrdargs)) {

                  (*net_list[i].nc_func) (NULL);
                  FreeArgs (rdargs);
               } else {

                  Fault (IoErr(), NULL, (char *) buffer, LINELEN);
                  SendResultString (buffer);
                  rc = RETURN_FAIL;
               }
               FreeDosObject (DOS_RDARGS,myrdargs);
            }
            break;
         }
         i++;
      }
      if (! (net_list[i].nc_Name)) {
         SendResultString ("Unknown option");
         rc = RETURN_FAIL;
      }
   }
   va_end (args);
   return (rc);
}

ULONG netdaemon (VOID) {
   ULONG    rc = TRUE;
   char     buffer[LINELEN+2];
   int      len, done = FALSE;

   struct   passwd   *pw, *rootpw;
   long     rootid = 0;

/*
** inittcp() wurde bereits durch isNetCall() aufgerufen!
*/

   serverstate = TRUE;

   if (rootpw = getpwnam ("root"))
      rootid = rootpw->pw_gid;

   if ((sgets (0, buffer, LINELEN)) && (strcmp (buffer, CMD_BEGIN) == 0)) {
//logprint ("BEGIN\n");
      if ((sgets (0, buffer, LINELEN)) && (len = isCommand (buffer, CMD_USER)) \
        && (pw = getpwnam (buffer + len))) {
//logprint ("USER %s\n", buffer + len);

         if (((rootpw) && (rootid == pw->pw_gid)) || (! rootpw)) {

            if ((sgets (0, buffer, LINELEN)) && (len = isCommand (buffer, CMD_PASSWORD))
              && (strcmp (crypt (buffer + len, pw->pw_passwd), pw->pw_passwd) == 0)) {
//logprint ("PASSWORD %s\n", buffer + len);

               if (SendClient (CMD_CONNECTED)) {
//logprint ("'CONNECTED' send!\n");
                  while (sgets (0, buffer, LINELEN)) {
//logprint ("COMMAND '%s'\n", buffer);
                     if (strcmp (buffer, CMD_END) == 0) {
                        break;
                     }

                     if (ExecuteCommand (buffer)) {
                        SendClient (CMD_ERROR);
                     } else {
                        SendClient (CMD_DONE);
                     }
                     done = TRUE;
                  }
               }
            } else {
               SendClient ("Wrong password\n");
            }
         } else {
            SendClient ("No root privileges\n");
         }
      } else {
         SendClient ("Unknown user\n");
      }
   } else {
      SendClient ("No 'BEGIN' received\n");
   }
   if (! done)
      SendClient (CMD_ERROR);

   serverstate = FALSE;
   return (rc);
}

int MyDoCommand (char *fmt, ...) {
   int   result = FALSE;
   char  buf[LINELEN];

   va_list args;
   va_start(args,fmt);
   RawDoFmt(fmt, args, (void (*)())"\x16\xc0\x4e\x75", buf);

   if (clientstate) {
      if (! NetCommand (buf))
         result = TRUE;
   } else {
      if (! ExecuteCommand (buf))
         result = TRUE;
   }
   if (! result) {
      MyRequest ("Continue", "Error occured!");
   }
   va_end (args);
   return (result);
}

