/
roa/
roa/lib/boards/
roa/lib/config/
roa/lib/edits/
roa/lib/help/
roa/lib/misc/
roa/lib/plrobjs/
roa/lib/quests/
roa/lib/socials/
roa/lib/www/
roa/lib/www/LEDSign/
roa/lib/www/LEDSign/fonts/
roa/lib/www/LEDSign/scripts/
roa/src/s_inc/
roa/src/sclient/
roa/src/sclient/binary/
roa/src/sclient/text/
roa/src/util/
/************************************************************************
	Realms of Aurealis 		James Rhone aka Vall of RoA

rclient.c				RoAClient GAMMA 0.9.
					Original project started 5/5/97.
					RoA Specific client capable of
					receiving server sent binaries
					(including sound files) and
					manipulating them via user defined
					techniques (sound players).
					
		******** 100% Completely Original Code ********
		*** BE AWARE OF ALL RIGHTS AND RESERVATIONS ***
		******** 100% Completely Original Code ********
		        All rights reserved henceforth. 

    Please note that no guarantees are associated with any code from
Realms of Aurealis.  All code which has been released to the general
public has been done so with an 'as is' pretense.  RoAClient is a 
completely original program with the exception of the readline suite
mentioned below.  Some of the original design was derived from Tintin++,
though no code was directly utilized.  Credit is given where credit is
due.

*** Read, Learn, Understand, Improve ***

*************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

#include <signal.h>
#include <netdb.h>
#include <netinet/in.h>

#include "../defines.h"
#include "../structures.h"
#include "../utils.h"
#include "../clicomm.h"
#include "rclient.h"

// Global variables
char *cversion = "RoAClient v1.0 GAMMA";
int chook = -1;                 /* global client<->mud socket */
int mudport;			/* port number to connect to*/
BOOL hide_text = 0;             /* echo or no echo? global */
char trans[MAX_STRING_LENGTH];
int  sound_pid = -1;		/* child playing sound file's pid */
int  file_dl_pid = -1;		/* large download child pid */

char ipaddy[MAX_INPUT_LENGTH];
char ipport[MAX_INPUT_LENGTH];

// prototypes...
void rclient_login(char *name, char *pword);
int doio(int sok);

int main(int argc, char **argv)
{
  char buf[MAX_STRING_LENGTH];
  char name[MAX_INPUT_LENGTH];
  char pword[MAX_INPUT_LENGTH];

  if (argc != 2 && argc != 3)
  {
    printf("Usage: %s <RoAServer IP [port]> \n", argv[0]);
    exit(-1);
  }

  csay(cversion);

  // bounce them over...
  strcpy(ipaddy, argv[1]);
  if (argc == 3)
    mudport = atoi(argv[2]);
  else
    mudport = 4000;

  sprintf(buf, "Attempting %s, port %d.", ipaddy, mudport);
  csay(buf);

  // we need the username and password now...
  printf("Username: ");
  fgets(name, 80, stdin);
  name[strlen(name) - 1] = '\0';

  printf("Password: ");
  fgets(pword, 80, stdin);
  pword[strlen(pword) - 1] = '\0';

  signal(SIGINT, (void *) goodnight);
  signal(SIGTERM, (void *) goodnight);
  signal(SIGCHLD, (void *) reaper);

  // ok, get a client connection...
  chook = get_roa_socket(ipaddy, mudport+1); 
  if (chook >= 0)
    csay("Connected to RoAServer.");
  else
  {
    csay("Unable to connect to RoAServer.  Exiting.");
    exit(-1);
  }

  // ok, we're connected, send name and passwd...
  rclient_login(name, pword);

  // we'll sit in here forever, until it's over...
  doio(chook);

  client_exit();
}

// write it over the wire...
int send_dblock(dblock *blk)
{
  int cnt, size, bufsize, retval = -1, nbytes = 0;
  char *sndbuf;
  void (*func)();

#ifdef DEBUG
  switch(blk->type) {
    case LOGIN_REQUEST:
      csay("Sending login request.");
      break;
    case SOUND_NOGO:
      csay("Sending sound nogo.");
      break;
    case SOUND_GO:
      csay("Sending sound go.");
      break;
    case SOUND_STOP:
      csay("Sending sound stop.");
      break;
    case PIC_NOGO:
      csay("Sending pic nogo.");
      break;
    case PIC_GO:
      csay("Sending pic go.");
      break;
    case PIC_STOP:
      csay("Sending pic stop.");
      break;
    case EDIT_REPLY:
      csay("Sending edit reply.");
      break;
  }
#endif

  func = signal(SIGPIPE, SIG_IGN);

  size    = sizeof(dblock) + blk->streamlen;
  bufsize = size + sizeof(int);

  CREATE(sndbuf, char, bufsize);
  if (!sndbuf)
  {
    csay("OUT OF MEMORY!!");
    client_exit();
  }

  // assemble the three pieces... header, block, stream
  memcpy(sndbuf, (void *) &size, sizeof(int));
  memcpy(&(sndbuf[sizeof(int)]), blk, sizeof(dblock));
  memcpy(&(sndbuf[sizeof(int)+sizeof(dblock)]), blk->stream, blk->streamlen);

  nbytes = mywrite(chook, sndbuf, bufsize);
  if (nbytes != bufsize)
  {
    FREENULL(sndbuf);
    printf("ERROR: %d bytes out of %d write on chook: %d\n", nbytes, bufsize, chook);
    csay("Write error: send_dblock");
    perror("send_dblock:");
    client_exit();
  }
  else
    retval = bufsize;

  FREENULL(sndbuf);

  signal(SIGPIPE, func);
  return retval;
}

int myread(int sok, char *trans, int length)
{
  int cnt, sofar;

  sofar = 0;
  while (sofar < length)
  {
    cnt = read(sok, trans + sofar, length - sofar);
    if (cnt < 0)
    {
      if (errno == EAGAIN) 
      {
        printf("\r%d out of %d bytes read...", sofar, length);
        continue;
      }
      else 
        return cnt;
    }
    else
    if (cnt == 0)
    {
      csay("Connection closed by server...leaving.");
      client_exit();
    }
    sofar += cnt;
    printf("\r%d out of %d bytes read...", sofar, length);
  }

  printf("\n");
  return sofar;
}

int mywrite(int sok, char *trans, int length)
{
  int cnt, sofar;

  sofar = 0;
  while (sofar < length)
  {
    cnt = write(sok, trans + sofar, length - sofar);
    if (cnt < 0)
    {
      if (errno == EAGAIN)
      {
        printf("\r%d out of %d bytes wrote...", sofar, length);
        continue;
      }
      else
        return cnt;
    }
    if (cnt == 0)
    {
      csay("Connection closed by server...leaving.");
      client_exit();
    }
    sofar += cnt;
    printf("\r%d out of %d bytes wrote...", sofar, length);
  }

  printf("\n");
  return sofar;
}

// read in a data block from this descriptor
// this allocs memory for the data block, BTW
int read_dblock(void **blk_ptr)
{
  int size;
  int nbytes;
  char *msg;
  void (*func)();

  func = signal(SIGPIPE, SIG_IGN);
  errno = 0;

  // error check here...
  nbytes = myread(chook, (char *)&size, sizeof(int));
  if (nbytes != sizeof(int))
  {
    printf("ERROR: %d bytes out of %d read on chook: %d\n", nbytes, sizeof(int), chook);
    perror("hmm:");
    csay("Client communication error, exiting.");
    csay("Client communication error, exiting.");
    csay("Client communication error, exiting.");
    client_exit();
  }

  printf("Allocating for %d size...\n", size);
  msg = (char *)malloc(size);
  if (!msg)
  {
    csay("Read error in read_dblock...out of memory.");
    return -1;
  }

  nbytes = myread(chook, msg, size);
  if (nbytes != size)
  {
    printf("ERROR: %d bytes out of %d read on chook: %d\n", nbytes, size, chook);
    perror("hmm:");
    csay("Client communication error, exiting.");
    csay("Client communication error, exiting.");
    csay("Client communication error, exiting.");
    client_exit();
  }

  // else... we read the right size...
  *blk_ptr = msg;

  signal(SIGPIPE, func);
  return nbytes + sizeof(int);;
}

// send our name and passwd...
void rclient_login(char *name, char *pword)
{
  dblock blk;

  blk.type      = LOGIN_REQUEST;
  blk.version   = 2;
  strcpy(blk.str1, name);
  strcpy(blk.str2, pword);
  blk.streamlen = 0;
  blk.stream    = NULL;

  send_dblock(&blk);
}

int reaper()
{
  int status;
  int pid;

  pid = wait3(&status, WNOHANG, (struct rusage *)0);
#ifdef DEBUG
  printf("Child %d exited.\n\r", pid);
#endif
  signal(SIGCHLD, (void *) reaper);
}

int goodnight()
{
  csay("Signal received.  Closing connection.\n");
  client_exit();
}

void client_exit(void)
{
  csay("RoAClient exiting.");

  /* don't check for errors, we don't care right now */
  if (sound_pid > 0)
    kill(sound_pid, SIGKILL); 

  close(chook);
  audi();
}

void audi()
{
  system("stty sane");
  exit(1);
}

void csay(char *txt)
{
  printf("RoAC**> %s\n\r",txt);
  fflush(stdout);
}

// open up a client connection ...
int get_roa_socket(char *ipname, int port)
{
  struct sockaddr_in srvadr_in;
  struct hostent *sp;
  int addrlen,sok,cnt,opt;
  char buf[MAX_STRING_LENGTH];

  addrlen = sizeof(struct sockaddr_in);
  memset((char *)&srvadr_in, 0, addrlen);
  srvadr_in.sin_family = AF_INET;
  sp = gethostbyname(ipname);
  if (!sp)
  {
    csay("Unable to gethostbyname.");
    exit(1);
  }

  srvadr_in.sin_addr.s_addr = ((struct in_addr *)(sp->h_addr))->s_addr;
  srvadr_in.sin_port = htons(port);

  sok = socket(PF_INET, SOCK_STREAM, 0);
  if (sok < 0) 
  {
    perror("Init-mud-socket");
    exit(0);
  }

#if defined(SO_SNDBUF)
  opt = MAX_STRING_LENGTH + 128;
  if (setsockopt(sok, SOL_SOCKET, SO_SNDBUF, (char *) &opt, sizeof(opt)) < 0) 
  {
    perror("setsockopt SNDBUF");
    exit(1);
  }
#endif

  opt = 1;
  if (setsockopt(sok, SOL_SOCKET, SO_REUSEADDR, (char *) & opt, sizeof (opt)) < 0) 
  {
    perror ("setsockopt REUSEADDR");
    exit (0);
  }

#if defined(SO_LINGER)
  {
    struct linger ld;
    ld.l_onoff = 0; ld.l_linger = 0;
    if (setsockopt(sok, SOL_SOCKET, SO_LINGER, (char *) &ld, sizeof(ld)) < 0) 
    {
      perror("setsockopt LINGER");
      exit(1);
    }
  }
#endif

  if (connect(sok, (struct sockaddr *)&srvadr_in, addrlen) < 0)
  {
    sprintf(buf, "Unable to connect to port %d.",port); 
    csay(buf);
    return -1;
  }

  // nonblock(sok);
  return sok;
}

/* display header info from TYPE_TEXT_MESSAGE from server */
void display_server_text_message(char *mesg)
{
  csay("Message from server:");
  csay(mesg);
}

int send_sound_GO(char *fname)
{
  dblock blk;
  blk.type = SOUND_GO;
  blk.version = 2;
  strcpy(blk.str1, fname);
  strcpy(blk.str2, "");
  blk.streamlen = 0;
  blk.stream    = NULL;
  send_dblock(&blk);
  return 1;
}

int send_sound_NOGO(char *fname)
{
  dblock blk;
  blk.type = SOUND_NOGO;
  blk.version = 2;
  strcpy(blk.str1, fname);
  strcpy(blk.str2, "");
  blk.streamlen = 0;
  blk.stream    = NULL;
  send_dblock(&blk);
  return 1;
}

/* read incoming message, decode it, and process as required */
/* call appropriate functions depending on message type */
int read_socket(int sok)
{
  int cnt, size, i, sofar = 0;
  int type, length, togo, chunk;
  char fname[MAX_INPUT_LENGTH];
  char *str;
  FILE *fp;
  char buf[MAX_STRING_LENGTH];
  char trans[MAX_STRING_LENGTH];
  void *sfile;
  dblock *blk;

  if (read_dblock((void*)&blk) < 0)
  {
    close(sok);
    csay("Unable to read_dblock in read_socket, leaving...");
    client_exit();
    return -1;
  }

  // if we have a stream, offset the pointer correctly...
  if (blk->streamlen > 0)
    blk->stream = (void *) &blk[1];

#ifdef DEBUG
  sprintf(buf, "Client received header version %d.", blk->version);
  csay(buf);
#endif

  /* act on type */
  switch (blk->type) 
  {
    /* read message length, then read length bytes and send */
    case TEXT_MESG:
      display_server_text_message(blk->str1);
      FREENULL(blk);
      break;

    case TEXT_FILE:
      /* check for fname here, see if have to dl or not */
      /* right now assume we have to dl */
      
      sprintf(trans, "text/%s", blk->str1); 
      if (!(fp = fopen(trans, "wt")))
      {
	sprintf(buf, "Unable to open %s for write.", trans);
	csay(buf);
        FREENULL(blk);
        return -1;
      }
      fprintf(fp, "%s", (char *)blk->stream);
      fclose(fp);

      sprintf(buf, "Text file: %s written successfully.", blk->str1);
      csay(buf);
      FREENULL(blk);
      break;

    case SOUND_QUERY:
      csay("Query received.");
      sprintf(trans, "binary/%s", blk->str1); 
      if ((fp = fopen(trans, "r")))
      {
	fclose(fp);
	send_sound_NOGO(blk->str1);

        sprintf(buf, "(%d) Playing sound file.", getpid());
        csay(buf);
	play_sound(trans);
      }
      else
	send_sound_GO(blk->str1);

      FREENULL(blk);
      break;

    case SOUND_FILE:
      csay("Sound_File received.");
      sprintf(trans, "binary/%s", blk->str1); 
      if (!(fp = fopen(trans, "wb")))
      {
	sprintf(buf, "Unable to open %s for write.", trans);
	csay(buf);
	FREENULL(blk);
        return 1;
      }

      if (fwrite(blk->stream, 1, blk->streamlen, fp) < blk->streamlen)
      {
	csay("TYPE BINARY SOUNDFILE: sound fwrite error.");
      }
      fclose(fp);

      FREENULL(blk);

      sprintf(buf, "Sound file: %s written successfully.", trans);
      csay(buf);

      sprintf(buf, "(%d) Playing sound file.", getpid());
      csay(buf);
      play_sound(trans);
      break;

    case EDIT_REQUEST:
      csay("Edit request received....sleeping (debug).");
      sleep(300);
      blk->type = EDIT_REPLY;
      send_dblock(blk);
      FREENULL(blk);
      break;

    default: 
      break;
  }
  return 1;
}

/* the main I/O loop  -roa */
int doio(int sok)
{
  fd_set          readfds;
  int 		  rv, maxdesc;

  while (TRUE) 
  {
    FD_ZERO(&readfds);
    FD_SET(sok, &readfds);   /* mud connection */

    // select forever 
    rv = select(sok +1, &readfds, NULL, NULL, 0);
    if (rv == 0)
      continue; 
    else 
    if (rv < 0)
    {
      if (errno != EINTR)
        csay("select error");
      continue;
    }

    /* if sok has something to say, lets grab it */
    if (FD_ISSET(sok, &readfds))
      if (read_socket(sok) < 0)
      {
        perror("read error...");
        csay("READ ERROR");
        close(sok);
        audi();
      }
  }
}

/* stop the socket from blocking */
void	nonblock(int s)
{
   int	flags;
   flags = fcntl(s, F_GETFL);
   flags |= O_NONBLOCK;
   if (fcntl(s, F_SETFL, flags) < 0) {
      perror("Fatal error executing nonblock (comm.c)");
      exit(1);
   }
}

/* is it a number, ripped from circle */
int is_number(char *str)
{
   int  look_at;

   if (*str == '\0')
      return(0);

   for (look_at = 0; *(str + look_at) != '\0'; look_at++)
      if ((*(str + look_at) < '0') || (*(str + look_at) > '9'))
         return(0);
   return(1);
}
 
/* this function forks off a child, closes its stdin/out and execs
   the sound player with fname as an argument 
   parent simply returns 1 */
int play_sound(char *fname)
{
  int midi = FALSE;
  int mod = FALSE;
  int au = FALSE;
  int wav = FALSE;
  char buf[512];

  /* if already playing, killit and play this */
  /* don't check for errors, we don't care right now */
  if (sound_pid > 0)
    if (kill(sound_pid, SIGKILL) < 0)
      csay("Error killing prior sound pid.");

  if (strstr(fname, ".mid") || strstr(fname, ".MID"))
    midi = TRUE;
  else
  if (strstr(fname, ".wav") || strstr(fname, ".WAV"))
    wav = TRUE;
  else
  if (strstr(fname, ".au") || strstr(fname, ".AU"))
    au = TRUE;
  else
  if (strstr(fname, ".mod") || strstr(fname, ".MOD"))
    mod = TRUE;

  fflush(NULL);

  sound_pid = fork();
  if (!sound_pid)	/* child mon */
  {
     close(0);  close(1);	/* reroute these to syslog at some point */
    
    /* close up the chook descriptor */
    close(chook);

    if (midi)
      execl(MIDIPLAYER, "rclient_midi", fname, "-g", (char*)0);

    if (mod)
      execl(MODPLAYER, "rclient_mod", fname, (char*)0);

    if (au)
      execl(AUPLAYER, "rclient_au", fname, (char*)0);

    if (wav)
    {
    //  execl(WAVPLAYER, "rclient_wav", fname, "-t", ".wav", (char*)0);
      sprintf(buf, "%s %s -t .wav /dev/audio", WAVPLAYER, fname);
      system(buf);
      exit(1);
    }
   
    csay("EXEC ERROR.");
    client_exit();  
  }
  else
  if (sound_pid < 0)	/* error mon */
  {
    csay("Forking error play_sound.");
    client_exit();
  }
  
  /* parent, just return */
  return 1;
}