diff options
author | Alexey Shchepin <alexey@process-one.net> | 2006-01-19 02:17:31 +0000 |
---|---|---|
committer | Alexey Shchepin <alexey@process-one.net> | 2006-01-19 02:17:31 +0000 |
commit | 568909d5bb454fab2023e7ab1a7cf62eff8dcd39 (patch) | |
tree | 562acb2d39d04393e19bda367791a2d8f04e6289 /src/ejabberd_zlib | |
parent | * src/odbc/ejabberd_odbc.erl: Added a way to retry database (diff) |
* src/aclocal.m4: Updated for zlib support
* src/configure.ac: Likewise
* src/mod_muc/mod_muc_room.erl: Weakened presence filtering, added
warning in non-anonymous rooms, room destroying updated to latest
JEP-0045, added a number of occupants and room name in room's
disco#info reply, miscellaneous internal changes (thanks to Sergei
Golovan)
* src/mod_muc/mod_muc.erl: Better support for nick unregistration
(thanks to Sergei Golovan)
* src/ejabberd_zlib/ejabberd_zlib.erl: Zlib support (thanks to
Sergei Golovan)
* src/ejabberd_zlib/ejabberd_zlib_drv.c: Likewise
* src/ejabberd_zlib/Makefile.in: Likewise
* src/ejabberd_c2s.erl: Stream compression support (JEP-0138)
* src/ejabberd_receiver.erl: Likewise
* src/mod_disco.erl: Don't split node name before calling hooks
(thanks to Sergei Golovan)
* src/mod_configure.erl: Support for configuration using ad-hoc
commands (thanks to Sergei Golovan)
* src/mod_announce.erl: Support for sending announce messages
using ad-hoc commands (thanks to Sergei Golovan)
* src/mod_adhoc.erl: Ad-hoc support (JEP-0050) (thanks to Magnus
Henoch)
* src/adhoc.erl: Likewise
* src/adhoc.hrl: Likewise
* src/jlib.hrl: Updated (thanks to Sergei Golovan)
* src/gen_mod.erl: Added function is_loaded/2 (thanks to Sergei
Golovan)
* src/ejabberd_service.erl: Changed error message on handshake
error (thanks to Sergei Golovan)
* src/ejabberd.cfg.example: Updated (thanks to Sergei Golovan)
SVN Revision: 486
Diffstat (limited to 'src/ejabberd_zlib')
-rw-r--r-- | src/ejabberd_zlib/Makefile.in | 38 | ||||
-rw-r--r-- | src/ejabberd_zlib/ejabberd_zlib.erl | 134 | ||||
-rw-r--r-- | src/ejabberd_zlib/ejabberd_zlib_drv.c | 171 |
3 files changed, 343 insertions, 0 deletions
diff --git a/src/ejabberd_zlib/Makefile.in b/src/ejabberd_zlib/Makefile.in new file mode 100644 index 000000000..1b4b5a021 --- /dev/null +++ b/src/ejabberd_zlib/Makefile.in @@ -0,0 +1,38 @@ +# $Id$ + +CC = @CC@ +CFLAGS = @CFLAGS@ @ZLIB_CFLAGS@ @ERLANG_CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ @ZLIB_LIBS@ @ERLANG_LIBS@ + +SUBDIRS = + +ERLSHLIBS = ../ejabberd_zlib_drv.so + +OUTDIR = .. +EFLAGS = -I .. -pz .. +OBJS = \ + $(OUTDIR)/ejabberd_zlib.beam + +all: $(OBJS) $(ERLSHLIBS) + +$(OUTDIR)/%.beam: %.erl + @ERLC@ -W $(EFLAGS) -o $(OUTDIR) $< + +#all: $(ERLSHLIBS) +# erl -s make all report "{outdir, \"..\"}" -noinput -s erlang halt + +$(ERLSHLIBS): ../%.so: %.c + $(CC) -Wall $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) \ + $(subst ../,,$(subst .so,.c,$@)) $(LIBS) \ + -o $@ -fpic -shared + +clean: + rm -f $(OBJS) $(ERLSHLIBS) + +distclean: clean + rm -f Makefile + +TAGS: + etags *.erl diff --git a/src/ejabberd_zlib/ejabberd_zlib.erl b/src/ejabberd_zlib/ejabberd_zlib.erl new file mode 100644 index 000000000..2f74108df --- /dev/null +++ b/src/ejabberd_zlib/ejabberd_zlib.erl @@ -0,0 +1,134 @@ +%%%---------------------------------------------------------------------- +%%% File : ejabberd_zlib.erl +%%% Author : Alexey Shchepin <alexey@sevcom.net> +%%% Purpose : Interface to zlib +%%% Created : 19 Jan 2006 by Alexey Shchepin <alexey@sevcom.net> +%%% Id : $Id$ +%%%---------------------------------------------------------------------- + +-module(ejabberd_zlib). +-author('alexey@sevcom.net'). +-vsn('$Revision$ '). + +-behaviour(gen_server). + +-export([start/0, start_link/0, + enable_zlib/2, disable_zlib/1, + send/2, + recv/2, recv/3, recv_data/2, + setopts/2, + controlling_process/2, + close/1]). + +%% Internal exports, call-back functions. +-export([init/1, + handle_call/3, + handle_cast/2, + handle_info/2, + code_change/3, + terminate/2]). + +-define(DEFLATE, 1). +-define(INFLATE, 2). + +-record(zlibsock, {sockmod, socket, zlibport}). + +start() -> + gen_server:start({local, ?MODULE}, ?MODULE, [], []). + +start_link() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). + +init([]) -> + case erl_ddll:load_driver(ejabberd:get_so_path(), ejabberd_zlib_drv) of + ok -> ok; + {error, already_loaded} -> ok + end, + Port = open_port({spawn, ejabberd_zlib_drv}, [binary]), + {ok, Port}. + + +%%% -------------------------------------------------------- +%%% The call-back functions. +%%% -------------------------------------------------------- + +handle_call(_, _, State) -> + {noreply, State}. + +handle_cast(_, State) -> + {noreply, State}. + +handle_info({'EXIT', Port, Reason}, Port) -> + {stop, {port_died, Reason}, Port}; + +handle_info({'EXIT', _Pid, _Reason}, Port) -> + {noreply, Port}; + +handle_info(_, State) -> + {noreply, State}. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +terminate(_Reason, Port) -> + Port ! {self, close}, + ok. + + +enable_zlib(SockMod, Socket) -> + case erl_ddll:load_driver(ejabberd:get_so_path(), ejabberd_zlib_drv) of + ok -> ok; + {error, already_loaded} -> ok + end, + Port = open_port({spawn, ejabberd_zlib_drv}, [binary]), + {ok, #zlibsock{sockmod = SockMod, socket = Socket, zlibport = Port}}. + +disable_zlib(#zlibsock{sockmod = SockMod, socket = Socket, zlibport = Port}) -> + port_close(Port), + {SockMod, Socket}. + +recv(Socket, Length) -> + recv(Socket, Length, infinity). +recv(#zlibsock{sockmod = SockMod, socket = Socket} = ZlibSock, + Length, Timeout) -> + case SockMod:recv(Socket, Length, Timeout) of + {ok, Packet} -> + recv_data(ZlibSock, Packet); + {error, _Reason} = Error -> + Error + end. + +recv_data(#zlibsock{zlibport = Port} = _ZlibSock, Packet) -> + case port_control(Port, ?INFLATE, Packet) of + <<0, In/binary>> -> + {ok, In}; + <<1, Error/binary>> -> + {error, binary_to_list(Error)} + end. + +send(#zlibsock{sockmod = SockMod, socket = Socket, zlibport = Port}, + Packet) -> + case port_control(Port, ?DEFLATE, Packet) of + <<0, Out/binary>> -> + SockMod:send(Socket, Out); + <<1, Error/binary>> -> + {error, binary_to_list(Error)} + end. + + +setopts(#zlibsock{sockmod = SockMod, socket = Socket}, Opts) -> + case SockMod of + gen_tcp -> + inet:setopts(Socket, Opts); + _ -> + SockMod:setopts(Socket, Opts) + end. + +controlling_process(#zlibsock{sockmod = SockMod, socket = Socket}, Pid) -> + SockMod:controlling_process(Socket, Pid). + +close(#zlibsock{sockmod = SockMod, socket = Socket, zlibport = Port}) -> + SockMod:close(Socket), + port_close(Port). + + diff --git a/src/ejabberd_zlib/ejabberd_zlib_drv.c b/src/ejabberd_zlib/ejabberd_zlib_drv.c new file mode 100644 index 000000000..075e31176 --- /dev/null +++ b/src/ejabberd_zlib/ejabberd_zlib_drv.c @@ -0,0 +1,171 @@ +/* $Id$ */ + +#include <stdio.h> +#include <string.h> +#include <erl_driver.h> +#include <zlib.h> + + +#define BUF_SIZE 1024 + +typedef struct { + ErlDrvPort port; + z_stream *d_stream; + z_stream *i_stream; +} ejabberd_zlib_data; + + +static ErlDrvData ejabberd_zlib_drv_start(ErlDrvPort port, char *buff) +{ + ejabberd_zlib_data *d = + (ejabberd_zlib_data *)driver_alloc(sizeof(ejabberd_zlib_data)); + d->port = port; + + d->d_stream = (z_stream *)malloc(sizeof(z_stream)); + + d->d_stream->zalloc = (alloc_func)0; + d->d_stream->zfree = (free_func)0; + d->d_stream->opaque = (voidpf)0; + + deflateInit(d->d_stream, Z_DEFAULT_COMPRESSION); + + d->i_stream = (z_stream *)malloc(sizeof(z_stream)); + + d->i_stream->zalloc = (alloc_func)0; + d->i_stream->zfree = (free_func)0; + d->i_stream->opaque = (voidpf)0; + + inflateInit(d->i_stream); + + set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY); + + return (ErlDrvData)d; +} + +static void ejabberd_zlib_drv_stop(ErlDrvData handle) +{ + ejabberd_zlib_data *d = (ejabberd_zlib_data *)handle; + + deflateEnd(d->d_stream); + free(d->d_stream); + + inflateEnd(d->i_stream); + free(d->i_stream); + + driver_free((char *)handle); +} + + +#define DEFLATE 1 +#define INFLATE 2 + +#define die_unless(cond, errstr) \ + if (!(cond)) \ + { \ + rlen = strlen(errstr) + 1; \ + b = driver_realloc_binary(b, rlen); \ + b->orig_bytes[0] = 1; \ + strncpy(b->orig_bytes + 1, errstr, rlen - 1); \ + *rbuf = (char *)b; \ + return rlen; \ + } + + +static int ejabberd_zlib_drv_control(ErlDrvData handle, + unsigned int command, + char *buf, int len, + char **rbuf, int rlen) +{ + ejabberd_zlib_data *d = (ejabberd_zlib_data *)handle; + int err; + int size; + ErlDrvBinary *b; + + switch (command) + { + case DEFLATE: + size = BUF_SIZE + 1; + rlen = 1; + b = driver_alloc_binary(size); + b->orig_bytes[0] = 0; + + d->d_stream->next_in = buf; + d->d_stream->avail_in = len; + d->d_stream->avail_out = 0; + err = Z_OK; + + while (err == Z_OK && d->d_stream->avail_out == 0) + { + d->d_stream->next_out = b->orig_bytes + rlen; + d->d_stream->avail_out = BUF_SIZE; + + err = deflate(d->d_stream, Z_SYNC_FLUSH); + die_unless((err == Z_OK) || (err == Z_STREAM_END), + "Deflate error"); + + rlen += (BUF_SIZE - d->d_stream->avail_out); + size += (BUF_SIZE - d->d_stream->avail_out); + b = driver_realloc_binary(b, size); + } + b = driver_realloc_binary(b, rlen); + *rbuf = (char *)b; + return rlen; + case INFLATE: + size = BUF_SIZE + 1; + rlen = 1; + b = driver_alloc_binary(size); + b->orig_bytes[0] = 0; + + if (len > 0) { + d->i_stream->next_in = buf; + d->i_stream->avail_in = len; + d->i_stream->avail_out = 0; + err = Z_OK; + + while (err == Z_OK && d->i_stream->avail_out == 0) + { + d->i_stream->next_out = b->orig_bytes + rlen; + d->i_stream->avail_out = BUF_SIZE; + + err = inflate(d->i_stream, Z_SYNC_FLUSH); + die_unless((err == Z_OK) || (err == Z_STREAM_END), + "Inflate error"); + + rlen += (BUF_SIZE - d->i_stream->avail_out); + size += (BUF_SIZE - d->i_stream->avail_out); + b = driver_realloc_binary(b, size); + } + } + b = driver_realloc_binary(b, rlen); + *rbuf = (char *)b; + return rlen; + } + + b = driver_alloc_binary(1); + b->orig_bytes[0] = 0; + *rbuf = (char *)b; + return 1; +} + + +ErlDrvEntry ejabberd_zlib_driver_entry = { + NULL, /* F_PTR init, N/A */ + ejabberd_zlib_drv_start, /* L_PTR start, called when port is opened */ + ejabberd_zlib_drv_stop, /* F_PTR stop, called when port is closed */ + NULL, /* F_PTR output, called when erlang has sent */ + NULL, /* F_PTR ready_input, called when input descriptor ready */ + NULL, /* F_PTR ready_output, called when output descriptor ready */ + "ejabberd_zlib_drv", /* char *driver_name, the argument to open_port */ + NULL, /* F_PTR finish, called when unloaded */ + NULL, /* handle */ + ejabberd_zlib_drv_control, /* F_PTR control, port_command callback */ + NULL, /* F_PTR timeout, reserved */ + NULL /* F_PTR outputv, reserved */ +}; + +DRIVER_INIT(ejabberd_zlib_drv) /* must match name in driver_entry */ +{ + return &ejabberd_zlib_driver_entry; +} + + |