#include <stdio.h>
#include <malloc.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <crypt.h>
#define MAX_CONNECT 256
#define AUTH '0'
#define MUDLIST '1'
#define TELL '3'
#define CHAT '4'
#define WHO '5'
#define RWHO '6'
#define INFOMSG '7'
#define MSG '8'
#define KICK '9'
#define AVERSION ':'
#define FILEPAGE ';'
#define TO_MUD '0'
#define TO_ALL '1'
#define SEP '\007'
#define D "\007"
#define WRITELEN 4096
#define SERVER_VERSION "AberChat Server 5.1"
#define SERVER_NAME "BetaServer"
int listener_fd = -1;
class conn {
public:
struct sockaddr_in addr;
int fd;
FILE *f;
const char *get_name() { return name; };
const char *get_lib() { return lib; };
const char *get_host() { return host; };
const char *get_ip() { return ip; };
const char *get_port() { return port; };
void set_name(const char *n) { name = strdup(n); };
void set_port(const char *n) { port = strdup(n); };
void set_host(const char *n) { host = strdup(n); };
void set_lib(const char *n) { lib = strdup(n); };
void set_ip(const char *n) { ip = strdup(n); };
conn() {
name = 0;
lib = 0;
host = 0;
port = 0;
ip = 0;
buffer = 0;
buflen = 0;
bufsize = 0;
auth = 0;
f = 0;
fd = -1;
}
void assocfd() {
f = fdopen(fd, "r");
}
~conn() {
if (name) free(name);
if (lib) free(lib);
if (host) free(host);
if (port) free(port);
if (ip) free(ip);
if (buffer) free(buffer);
if (f) fclose(f);
if (fd != -1) close(fd);
}
void queue(char *what, int len) {
if ((len+buflen)>bufsize) {
bufsize = (len+buflen)*2;
if (buffer)
buffer = (char *)realloc(buffer, bufsize);
else
buffer = (char *)malloc(bufsize);
}
memcpy(buffer+buflen, what, len);
buflen+=len;
}
void finished() {
char thing[] = {0};
queue(thing, 1);
}
void dequeue(int fd) {
write(fd, buffer, buflen);
buflen = 0;
}
bool auth;
private:
char *name, *lib, *host, *port, *ip, *buffer;
int buflen, bufsize;
};
conn *conns[MAX_CONNECT] = {0};
conn *alloc_conn() {
int i;
for (i=0;i<MAX_CONNECT;i++) {
if (!conns[i]) {
conns[i] = new conn();
return conns[i];
}
}
return 0;
}
void get_connection() {
struct sockaddr_in addr;
socklen_t what = sizeof addr;
int fd = accept(listener_fd, (struct sockaddr*)&addr, &what);
if (fd!=-1) {
conn *c = alloc_conn();
c->addr = addr;
c->fd = fd;
c->set_ip(inet_ntoa(c->addr.sin_addr));
c->set_host(inet_ntoa(c->addr.sin_addr));
}
}
char *get_arg(char *txt) {
static char c = '0';
char *ptr;
if ((ptr = strchr(txt, SEP))) {
*ptr = '\0';
return(++ptr);
}
else
return(&c);
}
void cprintf(conn *what, const char *format, ...)
__attribute__ ((format(printf, 2, 3)));
void cprintf(conn *what, const char *format, ...)
{
va_list pvar;
static char buffer[WRITELEN];
va_start(pvar, format);
vsprintf(buffer, format, pvar);
va_end(pvar);
what->queue(buffer, strlen(buffer));
}
void authenticate(char *packet, conn **what) {
char *name = packet, *lib, *port, *pass;
lib = get_arg(name);
port = get_arg(lib);
pass = get_arg(port);
bool found = false;
FILE * f = fopen("muds", "r");
if (!f) {
fprintf(stderr, "cant open mudsfile.\n");
exit(1);
}
do {
char mudline[1024];
char a_port[128], a_ip[128], a_pass[128], c_pass[128];
fgets(mudline, 1024, f);
if (feof(f)) break;
sscanf(mudline, "%s %s %s", a_ip, a_port, a_pass);
strcpy(c_pass, crypt(a_pass, "Ac"));
if (feof(f)) break;
if (!strcmp(a_ip, (*what)->get_ip()) &&
!strcmp(a_port, port) &&
!strcmp(c_pass, pass)) {
found = 1;
break;
}
} while(1);
if (found || 1) {
// fprintf(stderr, "Yes.\n");
for (int i=0;i<MAX_CONNECT;i++)
if (conns[i] && conns[i]->auth) {
cprintf(conns[i], "%c" D "%s" D "%s",
INFOMSG, name, "Connected!");
conns[i]->finished();
}
(*what)->set_name(name);
(*what)->set_lib(lib);
(*what)->set_port(port);
(*what)->auth = true;
cprintf(*what, "%c" D "Y", AUTH);
(*what)->finished();
} else {
// fprintf(stderr, "No.\n");
cprintf(*what, "%c" D "N", AUTH);
(*what)->finished();
delete *what;
*what = 0;
}
}
int cmp_conn(const void *a, const void *b) {
if (a > b) return 1;
if (b < a) return -1;
return 0;
}
void mudlist(char *player, conn **what) {
conn *muds[MAX_CONNECT];
int len, i;
for (i = 0, len = 0 ; i < MAX_CONNECT ; i++)
if (conns[i] && conns[i]->auth)
muds[len++] = conns[i];
qsort(muds, len, sizeof(conn *), cmp_conn);
cprintf(*what, "%c" D "%s", MUDLIST, player);
for (i = 0; i<len; i++) {
cprintf(*what, D "%s" D "%s" D "%s" D "%s" D "%s",
muds[i]->get_name(),
muds[i]->get_host(),
muds[i]->get_ip(),
muds[i]->get_port(),
muds[i]->get_lib());
}
(*what)->finished();
}
inline int min(int a, int b) {
if (a < b) return a;
return b;
}
bool eqmud(const char *mudindex, const char *mudname) {
if (!strncasecmp(mudindex, "The ", 4)) mudindex+=4;
if (!strncasecmp(mudname, "The ", 4)) mudname+=4;
if (!strncasecmp(mudindex, mudname, min(strlen(mudindex), strlen(mudname))))
return true;
return false;
}
struct conn *find_mud(const char *text) {
for (int i=0;i<MAX_CONNECT;i++)
if (conns[i] && conns[i]->auth &&
eqmud(conns[i]->get_name(), text))
return conns[i];
return NULL;
}
void version(char *buff, conn **what) {
char *player, *cli_ver;
player = buff;
cli_ver = get_arg(player);
cprintf(*what, "%c" D "%s" D "%s" D "%s",
AVERSION, player, cli_ver, SERVER_VERSION);
(*what)->finished();
}
bool has_line(const char *ln) {
FILE *f = fopen("pager-files", "r");
if (!f) return false;
char buffer[4096];
while (!feof(f)) {
fgets(buffer, 4095, f);
if (char *p=strchr(buffer, '\n')) *p=0;
if (!strcmp(buffer, ln)) {
fclose(f);
return true;
}
}
fclose(f);
return false;
}
void filepage(char *to_plr, conn **what) {
// I despair. Whoever designed this should be shot.
char *filename = get_arg(to_plr);
char *lt = get_arg(filename);
if (!lt) return;
if (!has_line(filename)) {
cprintf(*what, "%c" D "%s" D "Learn to type, muppet.\n" D "%c",
FILEPAGE, to_plr, 0);
(*what)->finished();
return;
}
FILE *f = fopen(filename, "r");
if (!f) {
cprintf(*what, "%c" D "%s" D "No such file.\n" D "%c",
FILEPAGE, to_plr, 0);
(*what)->finished();
return;
}
int linenum = (atoi(lt)) * 22;
char buffer[4096];
cprintf(*what, "%c" D "%s" D, FILEPAGE, to_plr);
while (!feof(f) && linenum--)
fgets(buffer, 4095, f);
if (!feof(f))
for (linenum = 23; linenum > 0 ; linenum--) {
if (linenum == 5) {
linenum = 5;
}
fgets(buffer, 4095, f);
if (feof(f)) break;
cprintf(*what, "%s", buffer);
}
cprintf(*what, D "%c", feof(f) ? '0' : '1');
(*what)->finished();
fclose(f);
}
void tomud(char *packet, conn **what) {
char *data = get_arg(packet), type, *args;
args = data + 2;
type = *data;
char *mudname = packet;
struct conn *c = mudname ? find_mud(mudname) : 0;
// fprintf(stderr, "got tomud packet type %i\n", type);
if (type != AUTH) {
if (!c) {
char *player = get_arg(data);
if (type == WHO) {
player = get_arg(player);
}
if (char *p=strchr(player, SEP)) *p=0;
cprintf(*what, "%c" D "%s" D "%s" D "%s",
MSG, player, SERVER_NAME, "No such mud connected.");
(*what)->finished();
return;
}
}
switch (type) {
case AUTH:
authenticate(args, what);
break;
case MUDLIST:
mudlist(args, what);
break;
case AVERSION:
version(args, what);
break;
case FILEPAGE:
filepage(args, what);
break;
default:
if (c) {
cprintf(c, "%s", data);
c->finished();
}
}
}
void toall(char *data, conn **) {
for (int i=0;i<MAX_CONNECT;i++)
if (conns[i] && conns[i]->auth) {
cprintf(conns[i], "%s", data);
conns[i]->finished();
}
}
void printpacket(const char *a) {
char f[4096];
strcpy(f, a);
char *p = f;
while (*p) {
if (*p==SEP)
*p = '|';
p++;
}
}
void do_read(conn **what) {
char buffer[4096];
int len;
memset(buffer, 0, sizeof buffer);
len = recv((*what)->fd, buffer, sizeof buffer, 0);
if (len < 1) {
for (int i=0;i<MAX_CONNECT;i++)
if (conns[i] && conns[i]->auth && conns[i] != *what) {
cprintf(conns[i], "%c" D "%s" D "%s",
INFOMSG, (*what)->get_name(), "Disconnected!");
conns[i]->finished();
}
delete *what;
*what=0;
} else {
switch (buffer[0]) {
case TO_MUD:
tomud(buffer+2, what);
break;
case TO_ALL:
toall(buffer+2, what);
break;
}
}
}
int main() {
struct sockaddr_in addr = { AF_INET, htons(6715), { 0, } };
int yes = 1;
listener_fd = socket(AF_INET, SOCK_STREAM, 0);
setsockopt(listener_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes);
bind(listener_fd, (struct sockaddr *)&addr, sizeof addr);
listen(listener_fd, 2);
while (1) {
fd_set rd, ex;
int maxfd = listener_fd, i;
FD_ZERO(&rd);
FD_ZERO(&ex);
FD_SET(listener_fd, &rd);
FD_SET(listener_fd, &ex);
for (i=0;i<MAX_CONNECT;i++) if (conns[i]) {
FD_SET(conns[i]->fd, &rd);
FD_SET(conns[i]->fd, &ex);
if (maxfd < conns[i]->fd) maxfd = conns[i]->fd;
}
for (i=0;i<MAX_CONNECT;i++)
if (conns[i])
conns[i]->dequeue(conns[i]->fd);
select(maxfd+1, &rd, 0, &ex, 0);
if (FD_ISSET(listener_fd, &rd)) get_connection();
for (i=0;i<MAX_CONNECT;i++) {
if (conns[i] && FD_ISSET(conns[i]->fd, &rd))
do_read(&conns[i]);
}
}
}