/*
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