calisto-20000323/
calisto-20000323/lib/
calisto-20000323/lib/etc/
calisto-20000323/lib/players/
calisto-20000323/lib/text/
calisto-20000323/log/
/*
 Calisto (c) 1998-2000 Peter Howkins, Matthew Howkins, Simon Howkins

 $Id: library.c,v 1.15 2000/03/07 21:04:21 peter Exp $

 $Log: library.c,v $
 Revision 1.15  2000/03/07 21:04:21  peter
 buffer safed change_line_endings(), send_to_descriptor() now uses all buffer
 safe funcs

 Revision 1.14  2000/03/06 17:41:35  peter
 added snprint() and vsnprintf(), also colour codes handled with a larger
 buffer, see note in code.

 Revision 1.13  2000/02/07 19:59:44  peter
 *** empty log message ***

 Revision 1.12  2000/02/04 21:45:24  peter
 fixed get_from_descriptor() to truncate very long lines

 Revision 1.11  2000/01/15 00:06:22  peter
 remmed out some of the debug lines, from telnet code handling

 Revision 1.10  2000/01/11 19:40:45  peter
 Now uses STRNCOPY and STRNAPPEND from strplus.h

 Revision 1.9  1999/12/19 17:27:50  peter
 bug fixing seg fault on bar2 func

 Revision 1.8  1999/12/14 20:51:13  peter
 Added character_from_name_exact() for checking if someone is logged in

 Revision 1.7  1999/12/11 23:09:26  peter
 *** empty log message ***

 Revision 1.6  1999/12/03 22:55:31  peter
 changed bar2 to use terminal width

 Revision 1.5  1999/12/03 22:33:37  peter
 got sb handling to copy term width + height to descriptor struct

 Revision 1.4  1999/12/03 22:01:48  peter
 Added new Telnet Code handling
 Terminal Type and Window Size

 Revision 1.3  1999/11/30 23:01:11  peter
 *** empty log message ***

 Revision 1.2  1999/11/29 22:02:10  peter
 More telnet

 Revision 1.1  1999/11/23 22:12:21  peter
 Initial revision


 */
static char rcsid[] = "$Id: library.c,v 1.15 2000/03/07 21:04:21 peter Exp $";

#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* attempt to activate certain parts of arpa/telnet.h */
#define TELOPTS
#define TELCMDS

#include "arpa/telnet.h"

#include "colours.h"
#include "dllist.h"
#include "globals.h"
#include "library.h"
#include "msnprintf.h"
#include "socks.h"
#include "strplus.h"
#include "structs.h"

character *character_from_name_exact(const char *sName)
{
  listnode *pNode = AllPlayers.head.next;

  while (LIST_NODE_IS_REAL(pNode)) {
    character *pChar = LIST_GET_DATA(pNode, character *, characterlink);

    if (STRIEQ(pChar->name, sName))
      return pChar;

    pNode = pNode->next;
  }
  return NULL;
}

character *character_from_name(const char *sName)
{
  unsigned int count = 0;
  character *temp = 0;
  listnode *pNode = AllPlayers.head.next;

  while (LIST_NODE_IS_REAL(pNode)) {
    character *pChar = LIST_GET_DATA(pNode, character *, characterlink);

    if (STRIEQ(pChar->name, sName)) {
      /* exact match */
      return pChar;
    } else if (STRINEQ(pChar->name, sName, strlen(sName))) {
      count ++;
      temp = pChar;
    }
    pNode = pNode->next;
  }
  switch (count) {
    case 0:
      /* did not find a match at all */
      return NULL;
      break;
    case 1:
      /* part matched just one */
      return temp;
      break;
    default:
      /* part matched more than one */
      return NULL;
      break;
  }
}

/* Searches for \n in the src string, and replaces it with 13,10 aka CRLF */
static void change_line_endings(char *dest, size_t size, const char *src)
{
  int count = size;

  while (*src && count > 0) {
    if (*src == '\n') {
      if (count >= 2) {
        *dest++ = 13;
        *dest++ = 10;
      }
      src++;
      count -= 2;
    } else { 
      *dest++ = *src++;
      count--;
    }
  }
  *dest = '\0';
}

void send_to_descriptor(descriptor *des, const char *string, ...)
{
  char buffer[OUTPUT_BUFFER];
  char buffer2[OUTPUT_BUFFER];
  va_list ptr;
  int check;

  va_start(ptr, string);
  mvsnprintf(buffer, sizeof(buffer), string, ptr);
  va_end(ptr);

  /* change line endings (from buffer -> buffer2) */
  change_line_endings(buffer2, sizeof(buffer2), buffer);

  /* process ansi colours (from buffer2 -> buffer) */
  csnprintf(buffer, sizeof(buffer), "%s", buffer2);

  /* is it safe to send stuff ? */
  if (des->safe == TRUE) {
    check = send_to_socket(des->sfd, "%s", buffer);
    if(check != 0) {
      des->state = STATE_CLOSING;
      des->safe = FALSE;
    }
  }
}

void send_to_char(const character *player, const char *string, ...)
{
  char buffer[OUTPUT_BUFFER];
  va_list ptr;

  va_start(ptr, string);
  mvsnprintf(buffer, sizeof(buffer), string, ptr);
  va_end(ptr);
  send_to_descriptor(LIST_GET_DATA(player,descriptor*,player), "%s", buffer);
}

void send_to_all_except(const character *c, const char *string, ...)
{
  listnode *pNode = AllPlayers.head.next;
  char buffer[OUTPUT_BUFFER];
  va_list ptr;

  va_start(ptr, string);
  mvsnprintf(buffer, sizeof(buffer), string, ptr);
  va_end(ptr);

  while (LIST_NODE_IS_REAL(pNode)) {
    character *pChar = LIST_GET_DATA(pNode,character*,characterlink);
    if (pChar != c)
      send_to_char(pChar, buffer);
    pNode = pNode->next;
  }
}

void send_to_all(const char *string, ...)
{
  listnode *pNode = AllPlayers.head.next;
  char buffer[OUTPUT_BUFFER];
  va_list ptr;

  va_start(ptr, string);
  mvsnprintf(buffer, sizeof(buffer), string, ptr);
  va_end(ptr);

  while (LIST_NODE_IS_REAL(pNode)) {
    character *pChar = LIST_GET_DATA(pNode,character*,characterlink);
    send_to_char(pChar, buffer);
    pNode = pNode->next;
  }
}

void send_to_room(const char *group, const char *string, ...)
{
  listnode *pNode = AllPlayers.head.next;
  char buffer[OUTPUT_BUFFER];
  va_list ptr;

  va_start(ptr, string);
  mvsnprintf(buffer, sizeof(buffer), string, ptr);
  va_end(ptr);
  
  while (LIST_NODE_IS_REAL(pNode)) {
    character *pChar = LIST_GET_DATA(pNode,character*,characterlink);
    if(STREQ(group, pChar->group)) {
      send_to_char(pChar, buffer);
    }
    pNode = pNode->next;
  }
}

void send_to_room_except(const char *group, const character *c, const char *string, ...)
{
  listnode *pNode = AllPlayers.head.next;
  char buffer[OUTPUT_BUFFER];
  va_list ptr;

  va_start(ptr, string);
  mvsnprintf(buffer, sizeof(buffer), string, ptr);
  va_end(ptr);

  while (LIST_NODE_IS_REAL(pNode)) {
    character *pChar = LIST_GET_DATA(pNode,character*,characterlink);
    if(pChar != c) {
      if(STREQ(group, pChar->group)) {
        send_to_char(pChar, buffer);
      }
    }
    pNode = pNode->next;
  }
}

/* Receives subnegotiation string.
   Everything between IAC SB and IAC SE
*/
static void process_telnet_subneg(descriptor *des, const unsigned char *buf, size_t size)
{
  if (size == 0)
    return;
  switch (*buf) {
    case TELOPT_TTYPE:
      buf++;
      if (size <= 2)
        return;
      if (*buf != TELQUAL_IS) {
        log(debug, "Telnet Subnegotiation TELOPT_TTYPE subfunction unknown: %d", *buf);
        return;
      }
      buf++;
      if (size <= 3)
        return;
/*      log(debug, "Telnet Subnegotiation TELOPT_TTYPE: %.*s", size - 2, buf); */
      STRNCOPY(des->termtype, (const char *) buf, sizeof(des->termtype));
      break;
    case TELOPT_NAWS:
      {
        unsigned w = 0, h = 0;

        buf++;
        if (size < 5)
          return;
        w = *buf++;
        w = (w << 8) | *buf++;
        h = *buf++;
        h = (h << 8) | *buf++;
/*        log(debug, "Telnet Subnegotiation TELOPT_NAWS: %u %u", w, h); */
        des->term_width  = (w != 0) ? w : 80;
        des->term_height = (h != 0) ? h : 24;
      }
      break;
    default:
      log(debug, "Telnet Subnegotiation unknown: %s", TELOPT(*buf));
      break;
  }
}

static void process_telnet_commands(descriptor *des)
{

  const unsigned char *src = (unsigned char *) &des->inbuf[0];
  unsigned char *dest = (unsigned char *) src;
  int pos;

  pos = 0;
  while (pos < des->inbuf_used) {
    if (*src == IAC) {
      src++;
      switch (*src) {
        case WILL:
        case WONT:
        case DO:
        case DONT:
/*          log(debug, "Telnet (sfd %d): IAC %s %s", des->sfd, TELCMD(*src),
              TELOPT(*(src+1))); */
          if (*src == WILL) {
            switch (*(src + 1)) {
              case TELOPT_TTYPE:
                request_termtype(des);
                break;
            }
          }
          src += 2;
          des->inbuf_used -= 3;
          break;
        case SB:
          {
            const unsigned char *subnegstart;
            size_t subnegcount = 0;
            unsigned char subneg[MAX_RAW_INPUT_BUFFER];

/*            log(debug, "Telnet Subnegotiation started"); */
            subnegstart = src + 1;
            des->inbuf_used--;
            do {
              src++;
              des->inbuf_used--;
              while (*src != IAC) {
                subnegcount++;
                src++;
                des->inbuf_used--;
              }
            } while (*(src + 1) != SE);
/*            log(debug, "Telnet Subnegotiation end (length = %d)", subnegcount); */
            src += 2;
            des->inbuf_used -= 2;

            memcpy(subneg, subnegstart, subnegcount);
            subneg[subnegcount] = '\0';
            process_telnet_subneg(des, subneg, subnegcount);
          }
          break;
        case 255:
          *dest++ = 255;
          src++;
          des->inbuf_used--;
          break;
        case GA:
        case EL:
        case EC:
        case AYT:
        case AO:
        case IP:
        case BREAK:
        case DM:
        case NOP:
        case EOR:
        case ABORT:
        case SUSP:
        case xEOF:
          src++;
          break;
      }
    } else {
      *dest++ = *src++;
    }
    pos++;
  }
  *dest++ = 0;
}

/* Gets lines from descriptor.
   Able to handle no lines available,
                  part lines available, 
                  multiple lines available, including fractions.
   
   Places in the inbuf[] the lines so far, separated only by '\0's

   Returns the number of *whole* lines available.
   May therefore return 0, if only part of line received.
   Returns <0 for error

   To handle fractions, keeps a note of amount of buffer used.

   Does not care about what line endings are received. Able to receive any of 
   10, 13, 10&13, 13&10, and filters these out appropriately
*/
int get_from_descriptor(descriptor *des)
{
  int count;

  /* Shift the contents of the buffer left over the whole lines that will
     already have been processed */
  if (des->inbuf_used_lines > 0) {
/*    log(debug, "inbuf: %d", des->inbuf_used - des->inbuf_used_lines);*/
    /* Only move lines if parts of lines available, because moving
       otherwise is not necessary */
    if (des->inbuf_used != des->inbuf_used_lines)
      memmove(&des->inbuf[0], &des->inbuf[des->inbuf_used_lines],
              des->inbuf_used - des->inbuf_used_lines);
    des->inbuf_used -= des->inbuf_used_lines;
    des->inbuf_used_lines = 0;
  }

  /* Read some more into the buffer, after whatever may already be in there */
  count = get_some_from_socket(des->sfd, &des->inbuf[des->inbuf_used],
                               MAX_RAW_INPUT_BUFFER - des->inbuf_used);
  if (count < 0)
    return -1;
  des->inbuf_used += count;

  /* Process telnet commands */
  process_telnet_commands(des);

  /* Process the des->inbuf, upto des->inbuf_used bytes.
     Search for newline characters, replacing with '\0's
     Keep a count of lines found.
     Update des->inbuf_used_lines with
       the number of bytes that make up whole lines.
     (This may be zero) */
  {
    int pos = 0;
    int line_count = 0;
    int bytes_to_move;

    des->inbuf_used_lines = 0;

    while (pos < des->inbuf_used) {
      switch (des->inbuf[pos]) {
        case 10:
          if (pos + 1 < des->inbuf_used) {
            if (des->inbuf[pos + 1] == 13) {
              /* Found a 10,13 line-ending - treat as one line */
              des->inbuf[pos] = '\0';
              line_count++;
              des->inbuf_used_lines = pos + 1;
              bytes_to_move = des->inbuf_used - pos - 2;
              if (bytes_to_move > 0) {
                memmove(&des->inbuf[pos + 1], &des->inbuf[pos + 2],
                        bytes_to_move);
              }
              des->inbuf_used--;
            } else {
              /* Found a 10 line-ending */
              des->inbuf[pos] = '\0';
              line_count++;
              des->inbuf_used_lines = pos + 1;
            }
          } else {
            /* Found a 10 line-ending */
            des->inbuf[pos] = '\0';
            line_count++;
            des->inbuf_used_lines = pos + 1;
          }
          break;
        case 13:
          if (pos + 1 < des->inbuf_used) {
            if (des->inbuf[pos + 1] == 10) {
              /* Found a 13,10 line-ending - treat as one line */
              des->inbuf[pos] = '\0';
              line_count++;
              des->inbuf_used_lines = pos + 1;
              bytes_to_move = des->inbuf_used - pos - 2;
              if (bytes_to_move > 0) {
                memmove(&des->inbuf[pos + 1], &des->inbuf[pos + 2],
                        bytes_to_move);
              }
              des->inbuf_used--;
            } else {
              /* Found a 13 line-ending */
              des->inbuf[pos] = '\0';
              line_count++;
              des->inbuf_used_lines = pos + 1;
            }
          } else {
            /* Found a 13 line-ending */
            des->inbuf[pos] = '\0';
            line_count++;
            des->inbuf_used_lines = pos + 1;
          }
          break;
      }
      pos++;
    }

    /* if someone has input a huge line, truncate it and throw away the rest of 
       the data in the tcpip buffer  */
    if (des->inbuf_used == MAX_RAW_INPUT_BUFFER && line_count == 0) {
      char temp_buf[MAX_RAW_INPUT_BUFFER];

      des->inbuf[MAX_RAW_INPUT_BUFFER - 1] = '\0';
      des->inbuf_used_lines = des->inbuf_used;
      line_count = 1;
    
      /* scrap remaining data on socket stack from this connection */
      while (get_some_from_socket(des->sfd, temp_buf, sizeof(temp_buf)))
        ;
    }

    return line_count;
  }
}

/* Telnet functions ***************************************************** */

void inquire_termtype(descriptor *d)
{
  unsigned char request[] = {
    IAC, DO, TELOPT_TTYPE, 0
  };
  send_to_descriptor(d, "%s", request);
}

void inquire_windowsize(descriptor *d)
{
  unsigned char request[] = {
    IAC, DO, TELOPT_NAWS, 0
  };
  send_to_descriptor(d, "%s", request);
}

void request_termtype(descriptor *d)
{
  unsigned char request[] = {
    IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE, 0
  };
  send_to_descriptor(d, "%s", request);
}

/* Telnet command code functions */
void echo_on(descriptor *d)
{
  unsigned char on_string[] = {
    IAC, WONT, TELOPT_ECHO,
    IAC, WONT, TELOPT_NAOFFD,
    IAC, WONT, TELOPT_NAOCRD,
    0
  };
  send_to_descriptor(d, "%s", on_string);
}

void echo_off(descriptor *d)
{
  unsigned char off_string[] = {
    IAC, WILL, TELOPT_ECHO,
    0
  };
  send_to_descriptor(d, "%s", off_string);
}


int send_file_to_descriptor(char *path, descriptor *des)
{
  static char buffer[OUTPUT_BUFFER];
  FILE *fp;
  int j;

  fp = fopen(path, "r");
  if (fp == NULL) {
    /* file not found */
    return -1;
  }

  while (fgets(buffer, OUTPUT_BUFFER, fp) != NULL) {
    j = strlen(buffer);
    if (buffer[j-1] == '\n') {
      buffer[j-1] = 13; buffer[j] = 10; buffer[j+1] = 0;
    }
    send_to_descriptor(des, "%s", buffer);
  }

  fclose(fp);
  return 0;
}

void bar2(descriptor *des, const char *text,
          const char *textform, const char *barform)
{
  char buff[OUTPUT_BUFFER];
  int i;

  if (!barform)
    barform = "^g";
  if (!textform)
    textform = "^n";

  if (text != NULL) {
    msnprintf(buff, sizeof(buff), "%s-[%s%s%s]",
              barform, textform, text, barform);
    /* add on (terminal width - strlen(text) - 3) dashes */
    if (strlen(text) + 3 < des->term_width) {
      for(i = strlen(text) + 3; i < des->term_width; i++) {
        STRNAPPEND(buff, "-", sizeof(buff));
      }
    }
  } else {
    STRNCOPY(buff, barform, sizeof(buff));
    for(i=0; i<des->term_width; i++) {
      STRNAPPEND(buff, "-", sizeof(buff));
    }
  }
  STRNAPPEND(buff, "^b^n\n", sizeof(buff));
  
  send_to_descriptor(des, "%s", buff);  
}

#if 0
void bar2(descriptor *des, const char *text, const char *textform,
          const char *barform)
{
  char buff[OUTPUT_BUFFER];
  char temp[OUTPUT_BUFFER];
  int count;

  if (!textform)
    textform = "^n";
  if (!barform)
    barform = "^g";

  if (text) {
    count = msnprintf(buff, sizeof(buff), "%s-[%s%s%s]",
                      barform, textform, text, barform);
    stripCodes(temp, buff);
    count -= strlen(temp);

    memset

    send_to_descriptor(des, "%.*s^b^n\n", des->term_width + count, buff);




  } else {
    msnprintf(buff, sizeof(buff), "%s%.*s", barform, des->term_width, dashes);
    len = des->term_width;
  }


  if (text) {
    count = msnprintf(buff, sizeof(buff), "%s-[%s%s%s]",
                      barform, textform, text, barform);
    memset(buff + count, '-', sizeof(buff) - count - 1);
  } else {
    count = msnprintf(buff, sizeof(buff), "%s", barform);


  send_to_descriptor(des, "%.*s^b^n\n", des->term_width, buff);
}
#endif