/************************************************************************
* IRC - Internet Relay Chat, ircd/s_zip.c
* Copyright (C) 1996 Christophe Kalt
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef lint
static char rcsid[] = "@(#)$Id: s_zip.c,v 1.7 1998/12/24 16:29:17 kalt Exp $";
#endif
#include "os.h"
#include "s_defines.h"
#define S_ZIP_C
#include "s_externs.h"
#undef S_ZIP_C
#ifdef ZIP_LINKS
/*
** Important note:
** The provided buffers for compression *MUST* be big
** enough for any operation to complete.
**
** s_bsd.c current settings are that the biggest packet size is 16k
** (but socket buffers are set to 8k..)
*/
/*
** size of the buffer holding compressed data
**
** outgoing data:
** must be enough to hold compressed data resulting of the compression
** of up to ZIP_MAXIMUM bytes
** incoming data:
** must be enough to hold what was just read
** (cptr->zip->inbuf should never hold more than ONE compression block.
** The biggest block allowed for compression is ZIP_MAXIMUM bytes)
*/
#define ZIP_BUFFER_SIZE (MAX(ZIP_MAXIMUM, READBUF_SIZE))
/*
** size of the buffer where zlib puts compressed data
** should be enough to hold uncompressed data resulting of the
** uncompression of zipbuffer
**
** tests show that an average ratio is around 40%,
** in some very particular cases, ratio can be VERY low, BUT:
**
** s_bsd.c/read_packet() is now smart enough to detect when uncompression
** stopped because the buffer is too small, and calls dopacket() again
** to finish the work (as many times as needed).
*/
#define UNZIP_BUFFER_SIZE 4*ZIP_BUFFER_SIZE
/* buffers */
static char unzipbuf[UNZIP_BUFFER_SIZE];
static char zipbuf[ZIP_BUFFER_SIZE];
/*
** zip_init
** Initialize compression structures for a server.
** If failed, zip_free() has to be called.
*/
int zip_init(cptr)
aClient *cptr;
{
cptr->zip = (aZdata *) MyMalloc(sizeof(aZdata));
cptr->zip->outcount = 0;
cptr->zip->in = (z_stream *) MyMalloc(sizeof(z_stream));
cptr->zip->in->avail_in = 0;
cptr->zip->in->total_in = 0;
cptr->zip->in->total_out = 0;
cptr->zip->in->zalloc = (alloc_func)0;
cptr->zip->in->zfree = (free_func)0;
cptr->zip->in->data_type = Z_ASCII;
if (inflateInit(cptr->zip->in) != Z_OK)
{
cptr->zip->out = NULL;
return -1;
}
cptr->zip->out = (z_stream *) MyMalloc(sizeof(z_stream));
cptr->zip->out->total_in = 0;
cptr->zip->out->total_out = 0;
cptr->zip->out->zalloc = (alloc_func)0;
cptr->zip->out->zfree = (free_func)0;
cptr->zip->out->data_type = Z_ASCII;
if (deflateInit(cptr->zip->out, ZIP_LEVEL) != Z_OK)
return -1;
return 0;
}
/*
** zip_free
*/
void zip_free(cptr)
aClient *cptr;
{
cptr->flags &= ~FLAGS_ZIP;
if (cptr->zip)
{
if (cptr->zip->in)
inflateEnd(cptr->zip->in);
MyFree(cptr->zip->in);
if (cptr->zip->out)
deflateEnd(cptr->zip->out);
MyFree(cptr->zip->out);
MyFree(cptr->zip);
cptr->zip = NULL;
}
}
/*
** unzip_packet
** Unzip the content of the buffer, don't worry about any leftover.
**
** will return the uncompressed buffer, length will be updated.
** if a fatal error occurs, length will be set to -1
*/
char * unzip_packet(cptr, buffer, length)
aClient *cptr;
char *buffer;
int *length;
{
Reg z_stream *zin = cptr->zip->in;
int r;
if (*length != 0 && zin->avail_in != 0)
{
sendto_flag(SCH_ERROR,
"assertion failed in unzip_packet(): %d %d",
*length, zin->avail_in);
sendto_flag(SCH_ERROR, "Please report to ircd-bugs@irc.org");
*length = -1;
return NULL;
}
if (*length)
{
zin->next_in = buffer;
zin->avail_in = *length;
}
zin->next_out = unzipbuf;
zin->avail_out = UNZIP_BUFFER_SIZE;
switch (r = inflate(zin, Z_PARTIAL_FLUSH))
{
case Z_OK:
cptr->flags &= ~FLAGS_ZIPRQ;
*length = UNZIP_BUFFER_SIZE - zin->avail_out;
return unzipbuf;
case Z_BUF_ERROR: /*no progress possible or output buffer too small*/
if (zin->avail_out == 0)
{
sendto_flag(SCH_ERROR,
"inflate() returned Z_BUF_ERROR: %s",
(zin->msg) ? zin->msg : "?");
*length = -1;
}
break;
case Z_DATA_ERROR: /* the buffer might not be compressed.. */
if ((cptr->flags & FLAGS_ZIPRQ) &&
!strncmp("ERROR ", buffer, 6))
{
cptr->flags &= ~(FLAGS_ZIP | FLAGS_ZIPRQ);
/*
* This is not sane at all. But if other server
* has sent an error now, it is probably closing
* the link as well.
*/
return buffer;
}
/* no break */
default: /* error ! */
/* should probably mark link as dead or something... */
sendto_flag(SCH_ERROR, "inflate() error(%d): %s", r,
(zin->msg) ? zin->msg : "?");
*length = -1; /* report error condition */
break;
}
return NULL;
}
/*
** zip_buffer
** Zip the content of cptr->zip->outbuf and of the buffer,
** put anything left in cptr->zip->outbuf, update cptr->zip->outcount
**
** if flush is set, then all available data will be compressed,
** otherwise, compression only occurs if there's enough to compress,
** or if we are reaching the maximum allowed size during a connect burst.
**
** will return the uncompressed buffer, length will be updated.
** if a fatal error occurs, length will be set to -1
*/
char * zip_buffer(cptr, buffer, length, flush)
aClient *cptr;
char *buffer;
int *length, flush;
{
Reg z_stream *zout = cptr->zip->out;
int r;
if (buffer)
{
/* concatenate buffer in cptr->zip->outbuf */
bcopy(buffer, cptr->zip->outbuf + cptr->zip->outcount,*length);
cptr->zip->outcount += *length;
}
*length = 0;
if (!flush && ((cptr->zip->outcount < ZIP_MINIMUM) ||
((cptr->zip->outcount < (ZIP_MAXIMUM - BUFSIZE)) &&
CBurst(cptr))))
return NULL;
zout->next_in = cptr->zip->outbuf;
zout->avail_in = cptr->zip->outcount;
zout->next_out = zipbuf;
zout->avail_out = ZIP_BUFFER_SIZE;
switch (r = deflate(zout, Z_PARTIAL_FLUSH))
{
case Z_OK:
if (zout->avail_in)
{
/* can this occur?? I hope not... */
sendto_flag(SCH_ERROR,
"deflate() didn't process all available data!");
}
cptr->zip->outcount = 0;
*length = ZIP_BUFFER_SIZE - zout->avail_out;
return zipbuf;
default: /* error ! */
sendto_flag(SCH_ERROR, "deflate() error(%d): %s", r,
(zout->msg) ? zout->msg : "?");
*length = -1;
break;
}
return NULL;
}
#endif /* ZIP_LINKS */
syntax highlighted by Code2HTML, v. 0.9.1