psyclpc/src/pkg-mccp.c

454 lines
12 KiB
C

/*---------------------------------------------------------------------------
* MCCP package.
*
* Efuns written and donated 2003 by Bastian Hoyer.
*---------------------------------------------------------------------------
* Support functions are based on code from the mccp project,
* see http://www.randomly.org/projects/MCCP/
*
* Copyright (c) 1999, Oliver Jowett <oliver@randomly.org>
*
* This code may be freely distributed and used if this copyright
* notice is retained intact.
*---------------------------------------------------------------------------
*/
#include "driver.h"
#ifdef USE_MCCP
#include "typedefs.h"
#include <errno.h>
#include <stdio.h>
#include <zlib.h>
#ifdef SOCKET_INC
# include SOCKET_INC
#endif
/* When no special networking code is needed, define the
* socket function to their normal Unix names.
*/
#if !defined (SOCKET_LIB) && !defined(SOCKET_INC)
# define socket_number(s) (s)
# define socket_ioctl ioctl
# ifndef hpux
# define socket_select select
# else
# define socket_select(n,r,w,x,t) select(n, (int *)r, (int *)w, (int *)x, t)
/* Silences the compiler */
# endif
# define socket_read read
# define socket_write write
# define socket_close close
#endif /* SOCKET_LIB */
#include "pkg-mccp.h"
#include "array.h"
#include "comm.h"
#include "mstrings.h"
#include "object.h"
#include "svalue.h"
#include "xalloc.h"
#include "../mudlib/sys/telnet.h"
/*=========================================================================*/
/* Support functions */
/*-------------------------------------------------------------------------*/
#define UMIN(a,b) ((a) < (b) ? (a) : (b))
/*-------------------------------------------------------------------------*/
void *
zlib_alloc (void *opaque UNUSED, unsigned int items, unsigned int size)
/* Callback function for the zlib to allocate an zeroed block of
* memory.
*/
{
#ifdef __MWERKS__
# pragma unused(opaque)
#endif
return calloc (items, size);
} /* zlib_alloc() */
/*-------------------------------------------------------------------------*/
void
zlib_free (void *opaque UNUSED, void *address)
/* Callback function for the zlib to free a block of memory allocated with
* zlib_alloc().
*/
{
#ifdef __MWERKS__
# pragma unused(opaque)
#endif
free (address);
} /* zlib_free() */
/*-------------------------------------------------------------------------*/
Bool
start_compress (interactive_t * ip, unsigned char telopt)
/* Enable MCCP compression on interactive <ip>, using <telopt> for the
* negotiations.
*
* Return TRUE on success.
*/
{
z_stream *s;
/* already compressing */
if (ip->out_compress)
return MY_TRUE;
/* allocate and init stream, buffer */
s = xalloc(sizeof (*s));
ip->out_compress_buf = xalloc(COMPRESS_BUF_SIZE);
s->next_in = NULL;
s->avail_in = 0;
s->next_out = ip->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)
{
xfree(ip->out_compress_buf);
xfree(s);
return MY_FALSE;
}
if (ip->tn_enabled)
{
/* version 1 or 2 support */
if (telopt == TELOPT_COMPRESS)
{
DTF (("%s TDEBUG: send IAC SB %02x WILL SE\n", time_stamp (), telopt));
SEND_TELNET_COMMAND (add_message ("%c", IAC);
add_message ("%c%c%c%c", SB, telopt, WILL, SE);
add_message (message_flush););
}
else if (telopt == TELOPT_COMPRESS2)
{
DTF (("%s TDEBUG: send IAC SB %02x WILL SE\n", time_stamp (), telopt));
SEND_TELNET_COMMAND (add_message ("%c", IAC);
add_message ("%c%c%c%c", SB, telopt, IAC, SE);
add_message (message_flush););
}
else
{
printf("Bad teloption %d passed", telopt);
xfree(ip->out_compress_buf);
xfree(s);
return MY_FALSE;
}
}
ip->compressing = telopt;
ip->out_compress = s;
printf("%s MCCP-DEBUG: '%s' mccp started (%d)\n"
, time_stamp(), get_txt(ip->ob->name), telopt);
/* success */
return MY_TRUE;
} /* start_compress() */
/*-------------------------------------------------------------------------*/
static Bool
process_compressed (interactive_t * ip)
/* Try to send any pending compressed-but-not-sent data in for <ip>.
* Return TRUE on success.
*/
{
int iStart, nBlock, nWrite, len;
if (!ip->out_compress)
return MY_TRUE;
len = ip->out_compress->next_out - ip->out_compress_buf;
if (len > 0)
{
for (iStart = 0; iStart < len; iStart += nWrite)
{
nBlock = UMIN (len - iStart, 4096);
if ((nWrite =
socket_write(ip->socket, ip->out_compress_buf + iStart, nBlock)) < 0)
{
if (errno == EAGAIN)
break;
#ifdef ENOSR
if (errno == ENOSR)
break;
#endif /* ENOSR */
/* write error */
return MY_FALSE;
}
if (nWrite <= 0)
break;
}
if (iStart)
{
if (iStart < len)
memmove (ip->out_compress_buf, ip->out_compress_buf + iStart,
len - iStart);
ip->out_compress->next_out = ip->out_compress_buf + len - iStart;
}
}
/* success */
return MY_TRUE;
} /* process_compressed() */
/*-------------------------------------------------------------------------*/
Bool
end_compress (interactive_t * ip, Bool force)
/* Cleanly shut down compression on <ip>. If <force> is TRUE, the compression
* will be forcefully shut down even if this incurs a data loss.
* Return TRUE on success.
*/
{
unsigned char dummy[1];
Bool retval = MY_TRUE;
if (!ip->out_compress)
return MY_TRUE;
ip->out_compress->avail_in = 0;
ip->out_compress->next_in = dummy;
/* No terminating signature is needed - receiver will get Z_STREAM_END */
if (deflate (ip->out_compress, Z_FINISH) != Z_STREAM_END && !force)
return MY_FALSE;
/* try to send any residual data */
if (!process_compressed (ip) && !force)
{
printf("%s MCCP-DEBUG: '%s' mccp had error while ending\n"
, time_stamp(), get_txt(ip->ob->name));
retval = MY_FALSE;
/* This is a write error - no sense in trying it again, so
* get rid of all the buffers anyway.
*/
}
/* reset compression values */
deflateEnd(ip->out_compress);
xfree(ip->out_compress_buf);
xfree(ip->out_compress);
ip->compressing = 0;
ip->out_compress = NULL;
ip->out_compress_buf = NULL;
printf("%s MCCP-DEBUG: '%s' mccp ended\n"
, time_stamp(), get_txt(ip->ob->name));
/* Finished */
return retval;
} /* end_compress() */
/*=========================================================================*/
/* EFUNS */
/*-------------------------------------------------------------------------*/
svalue_t *
f_start_mccp_compress (svalue_t * sp)
/* EFUN start_mccp_compress()
*
* int start_mccp_compress(int telopt)
*
* This efun must be called inside an interactive player and starts
* compression of the driver -> client traffic immediatly.
*
* <telopt> denotes the MCCP version and must be either TELOPT_COMPRESS2
* or TELOPT_COMRESS from <telnet.h>.
*
* Return non-zero on success, and 0 on failure.
*/
{
interactive_t *ip;
p_int mccpver;
int retval;
if (!O_SET_INTERACTIVE(ip, current_object))
{
errorf("start_mccp_compress() called for non-interactive object.\n");
return sp;
}
if (!ip->tn_enabled)
mccpver = -1;
else
{
mccpver = sp->u.number;
if ((mccpver != TELOPT_COMPRESS)
&& (mccpver != TELOPT_COMPRESS2)
)
{
errorf("Illegal value to arg 1 of start_mccp_compress(): %ld, "
"expected TELOPT_COMPRESS (%d) or TELOPT_COMPRESS2 (%d).\n"
, (long)mccpver, TELOPT_COMPRESS, TELOPT_COMPRESS2
);
/* NOTREACHED */
return sp;
}
}
#if 0
if (!ip->tn_enabled)
{
errorf("start_mccp_compress() called for object with telnet disabled.\n");
return sp;
}
#endif
free_svalue(sp);
retval = start_compress(ip, mccpver);
put_number(sp, retval);
return sp;
} /* start_mccp_compress() */
/*-------------------------------------------------------------------------*/
svalue_t *
f_end_mccp_compress (svalue_t * sp)
/* EFUN end_mccp_compress()
*
* int end_mccp_compress()
*
* This efun must be called inside an interactive player and stops
* the compression of the driver -> client traffic.
*
* Returns non-zero on success, and zero on a failure.
*/
{
interactive_t *ip;
int retval;
if (!O_SET_INTERACTIVE(ip, current_object))
{
errorf("end_mccp_compress() called for non-interactive object.\n");
/* NOTREACHED */
return sp;
}
#if 0
if (!ip->tn_enabled)
{
errorf("end_mccp_compress() called for object with telnet disabled.\n");
return sp;
}
#endif
#ifdef USE_PTHREADS
ip->compressing = 0; /* Signal the writer to stop compressing */
retval = 0;
#else
retval = end_compress(ip, MY_FALSE);
#endif
sp++;
put_number(sp,retval);
return sp;
} /* end_mccp_compress() */
/*-------------------------------------------------------------------------*/
svalue_t *
f_query_mccp (svalue_t * sp)
/* EFUN query_mccp()
*
* int query_mccp(object ob)
*
* this efun returns 0 if no mccp is used for interactive ob.
* if ob|this_player uses mccp it returns TELOPT_COMPRESS or
* TELOPT_COMPRESS2
*/
{
interactive_t *ip;
/* Make sure the object is interactive */
if (!(O_SET_INTERACTIVE (ip, sp->u.ob)) || ip->closing)
{
errorf("Bad arg 1 to query_mccp(): object not interactive.\n");
return sp;
}
free_svalue (sp);
put_number (sp, ip->compressing);
return sp;
} /* query_mccp() */
/*-------------------------------------------------------------------------*/
svalue_t *
f_query_mccp_stats (svalue_t * sp)
/* EFUN query_mccp_stats()
*
* int *query_mccp_stats(object ob)
*
* if the connection of interactive ob ( or this_object() if ob==0 )
* is compressed it returns an array with zlib statistics
* ({ total_in, total_out }) ( uncompressed/compressed)
*/
{
interactive_t *ip;
vector_t *mccp_stats;
/* Make sure the object is interactive */
if (!(O_SET_INTERACTIVE (ip, sp->u.ob)) || ip->closing)
{
errorf("Bad arg 1 to query_mccp_stats(): object not interactive.\n");
return sp;
}
free_svalue (sp);
if (ip->compressing > 0)
{
mccp_stats = allocate_uninit_array (2);
put_number (mccp_stats->item, ip->out_compress->total_in);
put_number (mccp_stats->item + 1, ip->out_compress->total_out);
put_array (sp, mccp_stats);
}
else
{
put_number (sp, 0);
}
return sp;
} /* query_mccp_stats() */
/*-------------------------------------------------------------------------*/
#endif /* USE_MCCP */
/*************************************************************************/