/***************************************************************************\
[*] ___ ____ ____ __ __ ____ [*] ROGUE: ROM With Attitude [*]
[*] /#/ ) /#/ ) /#/ ) /#/ /#/ /#/ [*] All rights reserved [*]
[*] /#/ < /#/ / /#/ _ /#/ /#/ /#/-- [*] Copyright(C) 2000-2001 [*]
[*] /#/ \(#(__/ (#(__/ (#(__/#/ (#(___ [*] Kenneth Conley (Mendanbar) [*]
[*] Expression of Digital Creativity.. [*] scmud@mad.scientist.com [*]
[-]---------------------------------------+-+-----------------------------[-]
[*] File: mudproto.cpp [*]
[*] Usage: MUD Client Protocols (MSP, MXP, MCCP) [*]
\***************************************************************************/
/*
* Uh.. something something..
* - Mendanbar
*/
#include "merc.h"
#include "telnet.h"
#include "clientopt.h"
bool processCompress args( (DESCRIPTOR_DATA *desc) );
bool write_to_descriptor args( (DESCRIPTOR_DATA *d, char *txt, int length) );
static char mcp_start[] = { IAC, SB, TELOPT_MCP, IAC, SE, '\0' };
static char mxp_start[] = { IAC, SB, TELOPT_MXP, IAC, SE, '\0' };
void *zlib_alloc(void *opaque, UInt32 items, UInt32 size) {
return calloc(items, size);
}
void zlib_free(void *opaque, void *address) {
free(address);
}
bool compressStart(DESCRIPTOR_DATA *desc) {
z_stream *s;
if (desc->out_compress)
return TRUE;
s = (z_stream *)alloc_mem(sizeof(*s));
desc->out_compress_buf = (unsigned char *)alloc_mem(COMPRESS_BUF_SIZE);
if (!s) {
mudlogf(BRF, LVL_STAFF, TRUE, "SYSERR: compressStart: Couldn't find a socket to compress.");
close_socket(desc);
return FALSE;
}
s->next_in = NULL;
s->avail_in = 0;
s->next_out = desc->out_compress_buf;
s->avail_out= COMPRESS_BUF_SIZE;
s->zalloc = zlib_alloc;
s->zfree = zlib_free;
s->opaque = NULL;
if (deflateInit(s, 9) != Z_OK) {
mudlogf(BRF, LVL_STAFF, TRUE, "SYSERR: problems with zlib, trying to clean up.");
free_mem(desc->out_compress_buf, COMPRESS_BUF_SIZE);
free_mem(s, sizeof(z_stream));
return FALSE;
}
SET_BIT(DESC_FLAGS(desc), DESC_MCPON);
write_to_descriptor(desc, mcp_start, strlen(mcp_start));
desc->out_compress = s;
return TRUE;
}
bool compressEnd(DESCRIPTOR_DATA *desc) {
unsigned char dummy[1];
if (!desc->out_compress)
return TRUE;
desc->out_compress->avail_in = 0;
desc->out_compress->next_in = dummy;
if (deflate(desc->out_compress, Z_FINISH) != Z_STREAM_END)
return FALSE;
if (!processCompressed(desc))
return FALSE;
deflateEnd(desc->out_compress);
free_mem(desc->out_compress_buf, COMPRESS_BUF_SIZE);
free_mem(desc->out_compress, sizeof(z_stream));
desc->out_compress = NULL;
desc->out_compress_buf = NULL;
REMOVE_BIT(DESC_FLAGS(desc), DESC_MCPON);
return TRUE;
}
bool processCompressed(DESCRIPTOR_DATA *desc) {
int iStart, nBlock, nWrite, len;
if (!desc->out_compress)
return TRUE;
len = desc->out_compress->next_out - desc->out_compress_buf;
if (len > 0) {
for (iStart = 0; iStart < len; iStart += nWrite) {
nBlock = UMIN(len - iStart, 4096);
if ((nWrite = write(desc->descriptor, desc->out_compress_buf + iStart, nBlock)) < 0) {
if (errno == EAGAIN || errno == ENOSR)
break;
return FALSE;
}
if (nWrite <= 0)
break;
}
if (iStart) {
if (iStart < len)
memmove(desc->out_compress_buf, desc->out_compress_buf + iStart, len - iStart);
desc->out_compress->next_out = desc->out_compress_buf + len - iStart;
}
}
return TRUE;
}
bool writeCompressed(DESCRIPTOR_DATA *desc, char *txt, int length) {
z_stream *s = desc->out_compress;
s->next_in = (unsigned char *)txt;
s->avail_in = length;
while (s->avail_in) {
s->avail_out = COMPRESS_BUF_SIZE - (s->next_out - desc->out_compress_buf);
if (s->avail_out) {
int status = deflate(s, Z_SYNC_FLUSH);
if (status != Z_OK)
return FALSE; // Boom?
}
if (!processCompressed(desc))
return FALSE;
}
return TRUE;
}
void toggle_mxp(DESCRIPTOR_DATA *d) {
if (!DESC_FLAGGED(d, DESC_MXPON)) {
// Initialize and setup elements...
SET_BIT(DESC_FLAGS(d), DESC_MXPON);
write_to_buffer(d, mxp_start, strlen(mxp_start));
write_to_buffer(d, MXPMODE(MXP_PERM_SECURE), 0);
write_to_buffer(d, MXP_TAG("!-- Setup MXP elements --"), 0);
write_to_buffer(d, MXP_TAG("!ELEMENT Ex '<send>' FLAG=RoomExit"), 0);
write_to_buffer(d, MXP_TAG("!ELEMENT Exit '<send exits>'"), 0);
} else {
// turn it off
REMOVE_BIT(DESC_FLAGS(d), DESC_MXPON);
write_to_buffer(d, MXPMODE(MXP_PERM_LOCKED), 0);
}
return;
}
void toggle_compression(DESCRIPTOR_DATA *d) {
if (d->out_compress)
compressEnd(d);
else
compressStart(d);
return;
}
ACMD(do_compress) {
if (!ch->desc) {
ch->Send("What descriptor?\n\r");
return;
}
if (!ch->desc->out_compress) {
if (!compressStart(ch->desc)) {
ch->Send("Failed.\n\r");
return;
}
ch->Send("Okay, compression enabled.\n\r");
} else {
if (!compressEnd(ch->desc)) {
ch->Send("Failed.\n\r");
return;
}
ch->Send("Okay, compression disabled.\n\r");
}
return;
}
void will_telopt(DESCRIPTOR_DATA *d, unsigned char opt) {
unsigned char will_option[] = { IAC, WILL, opt, '\0' };
write_to_buffer(d, (char *)will_option, 0);
}
void negotiate_telopt(DESCRIPTOR_DATA *d) {
// Extention Protocal
will_telopt(d, TELOPT_MXP);
// Sound Protocal
will_telopt(d, TELOPT_MSP);
if (!d->out_compress)
will_telopt(d, TELOPT_MCP);
}
int process_telopt(DESCRIPTOR_DATA *d, int first_iac) {
bool incomplete = FALSE;
int l, iac_sb_index, i = first_iac;
unsigned char *in = (unsigned char *)d->inbuf;
bool mxp_start = FALSE, mxp_stop = FALSE, mccp_stop = FALSE;
for (l = 0; in[i] == IAC && !incomplete && l < 20; l++) {
switch (in[i+1]) {
case IAC:
case '\0':
incomplete = TRUE;
break;
case DO:
switch (in[i+2]) {
case '\0':
i -= 3;
incomplete = TRUE;
break;
case TELOPT_MCP:
SET_BIT(DESC_FLAGS(d), DESC_CANZLIB);
toggle_compression(d);
break;
case TELOPT_MSP:
SET_BIT(DESC_FLAGS(d), DESC_HASMSP | DESC_MSPON);
break;
case TELOPT_MXP:
SET_BIT(DESC_FLAGS(d), DESC_HASMXP);
toggle_mxp(d);
break;
default:
// mudlogf(BRF, LVL_STAFF, TRUE, "process_telopt: unknown DO code (%s)", in[i+2]);
break;
}
i += 3;
break;
case DONT:
switch (in[i+2]) {
case '\0':
i -= 3;
incomplete = true;
break;
case TELOPT_MCP:
case TELOPT_MSP:
case TELOPT_MXP:
// do nothing
break;
default:
break;
}
i += 3;
break;
case SB:
iac_sb_index = i;
i += 2;
while (in[i] && in[i] != SE)
i++;
if (in[i] == SE) {
i++;
// subopt_check(d, iac_sb_index);
} else {
i = iac_sb_index;
incomplete = TRUE;
}
break;
default:
if (in[+2] == '\0')
incomplete = TRUE;
else {
mudlogf(BRF, LVL_STAFF, TRUE, "process_telopt: ignoring IAC %d %d", in[i+1], in[i+2]);
i += 3;
}
break;
}
}
memmove(&d->inbuf[first_iac], &d->inbuf[i], strlen(&d->inbuf[i])+1);
if (l == 20)
mudlogf(BRF, LVL_STAFF, TRUE, "process_telopt: More than 20 telnet options");
return 0;
}
void set_desc_flags(DESCRIPTOR_DATA *d) {
CHAR_DATA *ch = Original(d);
if (!ch)
return;
REMOVE_BIT(PLR_FLAGS(ch), PLR_PUEBLO | PLR_MSP );
if (DESC_FLAGGED(d, DESC_PUEBLO)) SET_BIT(PLR_FLAGS(ch), PLR_PUEBLO);
}
int count_mxp_tags(int mxp, const char *txt, int length) {
char c;
const char *p;
int count = 0, InTag = 0, InEntity = 0;
for (p = txt, count = 0; length > 0; p++, length--) {
c = *p;
if (InTag) {
if (!mxp)
count--;
if (c == MXP_ENDc)
InTag = FALSE;
} else if (InEntity) {
if (!mxp)
count--;
if (c == ';')
InEntity = FALSE;
} else {
switch (c) {
case MXP_BEGc:
InTag = TRUE;
if (!mxp)
count--;
break;
case MXP_ENDc:
if (!mxp)
count--;
break;
case MXP_AMPc:
InEntity = TRUE;
if (!mxp)
count--;
break;
default:
if (mxp) {
switch (c) {
case '<':
case '>':
count += 3;
break;
case '&':
count += 4;
break;
case '"':
count += 5;
break;
}
}
break;
}
}
}
return count;
}
void convert_mxp_tags(int mxp, char *dest, const char *src, int len) {
char *pd, c;
const char *ps;
bool InTag = FALSE, InEntity = FALSE;
for (ps = src, pd = dest; len > 0; ps++, len--) {
c = *ps;
if (InTag) {
if (c == MXP_ENDc) {
InTag = FALSE;
if (mxp)
*pd++ = '>';
} else if (mxp)
*pd++ = c;
} else if (InEntity) {
if (mxp)
*pd++ = c;
if (c == ';')
InEntity = FALSE;
} else {
switch (c) {
case MXP_BEGc:
InTag = TRUE;
if (mxp)
*pd++ = '<';
break;
case MXP_ENDc:
if (mxp)
*pd++ = '>';
break;
case MXP_AMPc:
InEntity = TRUE;
if (mxp)
*pd++ = '&';
break;
default:
if (mxp) {
switch (c) {
case '<':
memcpy(pd, "<", 4);
pd += 4;
break;
case '>':
memcpy(pd, ">", 4);
pd += 4;
break;
case '&':
memcpy(pd, "&", 5);
pd += 5;
break;
case '"':
memcpy(pd, """, 6);
pd += 6;
break;
default:
*pd++ = c;
break;
}
} else
*pd++ = c;
break;
}
}
}
}