ldmud-3.3.719/
ldmud-3.3.719/doc/
ldmud-3.3.719/doc/efun.de/
ldmud-3.3.719/doc/efun/
ldmud-3.3.719/doc/man/
ldmud-3.3.719/doc/other/
ldmud-3.3.719/mud/
ldmud-3.3.719/mud/heaven7/
ldmud-3.3.719/mud/lp-245/
ldmud-3.3.719/mud/lp-245/banish/
ldmud-3.3.719/mud/lp-245/doc/
ldmud-3.3.719/mud/lp-245/doc/examples/
ldmud-3.3.719/mud/lp-245/doc/sefun/
ldmud-3.3.719/mud/lp-245/log/
ldmud-3.3.719/mud/lp-245/obj/Go/
ldmud-3.3.719/mud/lp-245/players/lars/
ldmud-3.3.719/mud/lp-245/room/death/
ldmud-3.3.719/mud/lp-245/room/maze1/
ldmud-3.3.719/mud/lp-245/room/sub/
ldmud-3.3.719/mud/lp-245/secure/
ldmud-3.3.719/mud/sticklib/
ldmud-3.3.719/mud/sticklib/src/
ldmud-3.3.719/mudlib/deprecated/
ldmud-3.3.719/mudlib/uni-crasher/
ldmud-3.3.719/pkg/
ldmud-3.3.719/pkg/debugger/
ldmud-3.3.719/pkg/diff/
ldmud-3.3.719/pkg/misc/
ldmud-3.3.719/src/
ldmud-3.3.719/src/autoconf/
ldmud-3.3.719/src/ptmalloc/
ldmud-3.3.719/src/util/
ldmud-3.3.719/src/util/erq/
ldmud-3.3.719/src/util/indent/hosts/next/
ldmud-3.3.719/src/util/xerq/
ldmud-3.3.719/src/util/xerq/lpc/
ldmud-3.3.719/src/util/xerq/lpc/www/
ldmud-3.3.719/test/generic/
ldmud-3.3.719/test/inc/
ldmud-3.3.719/test/t-0000398/
ldmud-3.3.719/test/t-0000548/
ldmud-3.3.719/test/t-030925/
ldmud-3.3.719/test/t-040413/
ldmud-3.3.719/test/t-041124/
ldmud-3.3.719/test/t-language/
/*---------------------------------------------------------------------------
 * 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() */

/*-------------------------------------------------------------------------*/
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];
    unsigned char buf[256];
    size_t len;
    Bool retval = MY_TRUE;
    
    if (!ip->out_compress)
        return MY_TRUE;
   
    ip->out_compress->avail_in = 0;
    ip->out_compress->next_in = dummy;
    
    ip->out_compress->next_out = buf;
    ip->out_compress->avail_out = sizeof(buf);

    /* 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;
   
    len = ip->out_compress->next_out - buf;
    
    /* first 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;

    /* try to send any residual data */
    comm_socket_write((char*) buf, len, ip, WB_NONDISCARDABLE);
   
    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

    retval = end_compress(ip, MY_FALSE);

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

/*************************************************************************/