aboutsummaryrefslogtreecommitdiff
path: root/src/ejabberd_zlib
diff options
context:
space:
mode:
authorAlexey Shchepin <alexey@process-one.net>2006-01-19 02:17:31 +0000
committerAlexey Shchepin <alexey@process-one.net>2006-01-19 02:17:31 +0000
commit568909d5bb454fab2023e7ab1a7cf62eff8dcd39 (patch)
tree562acb2d39d04393e19bda367791a2d8f04e6289 /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.in38
-rw-r--r--src/ejabberd_zlib/ejabberd_zlib.erl134
-rw-r--r--src/ejabberd_zlib/ejabberd_zlib_drv.c171
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;
+}
+
+