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