aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.in7
-rw-r--r--src/configure.ac9
-rw-r--r--src/ejabberd_app.erl1
-rw-r--r--src/ejabberd_c2s.erl8
-rw-r--r--src/ejabberd_s2s_in.erl4
-rw-r--r--src/ejabberd_s2s_out.erl2
-rw-r--r--src/ejabberd_service.erl4
-rw-r--r--src/mod_muc/mod_muc_room.erl11
-rw-r--r--src/mod_offline_odbc.erl3
-rw-r--r--src/web/ejabberd_http_bind.erl16
-rw-r--r--src/xml.c230
-rw-r--r--src/xml.erl19
12 files changed, 290 insertions, 24 deletions
diff --git a/src/Makefile.in b/src/Makefile.in
index 94fa8b1ac..174084f64 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -55,6 +55,11 @@ ifeq (@full_xml@, true)
EFLAGS+=-DFULL_XML_SUPPORT
endif
+ifeq (@nif@, true)
+ EFLAGS+=-DNIF
+ ERLSHLIBS=xml.so
+endif
+
ifeq (@transient_supervisors@, false)
EFLAGS+=-DNO_TRANSIENT_SUPERVISORS
endif
@@ -68,7 +73,7 @@ prefix = @prefix@
exec_prefix = @exec_prefix@
SUBDIRS = @mod_irc@ @mod_pubsub@ @mod_muc@ @mod_proxy65@ @eldap@ @pam@ @web@ stringprep stun @tls@ @odbc@ @ejabberd_zlib@
-ERLSHLIBS = expat_erl.so
+ERLSHLIBS += expat_erl.so
ERLBEHAVS = cyrsasl.erl gen_mod.erl p1_fsm.erl
SOURCES_ALL = $(wildcard *.erl)
SOURCES_MISC = $(ERLBEHAVS) $(DEBUGTOOLS)
diff --git a/src/configure.ac b/src/configure.ac
index 20bfdf6f1..70a6fb43c 100644
--- a/src/configure.ac
+++ b/src/configure.ac
@@ -94,6 +94,15 @@ AC_ARG_ENABLE(full_xml,
esac],[full_xml=false])
AC_SUBST(full_xml)
+AC_ARG_ENABLE(nif,
+[AC_HELP_STRING([--enable-nif], [replace some functions with C equivalents. You should have Erlang R13B04 or higher (default: no)])],
+[case "${enableval}" in
+ yes) nif=true ;;
+ no) nif=false ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-nif) ;;
+esac],[nif=false])
+AC_SUBST(nif)
+
AC_CONFIG_FILES([Makefile
$make_mod_irc
$make_mod_muc
diff --git a/src/ejabberd_app.erl b/src/ejabberd_app.erl
index 7a54fcc37..97ae7fedd 100644
--- a/src/ejabberd_app.erl
+++ b/src/ejabberd_app.erl
@@ -46,6 +46,7 @@ start(normal, _Args) ->
db_init(),
sha:start(),
stringprep_sup:start_link(),
+ xml:start(),
start(),
translate:start(),
acl:start(),
diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl
index 6238cdae5..d4b68096e 100644
--- a/src/ejabberd_c2s.erl
+++ b/src/ejabberd_c2s.erl
@@ -627,7 +627,7 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
Socket = StateData#state.socket,
TLSSocket = (StateData#state.sockmod):starttls(
Socket, TLSOpts,
- xml:element_to_string(
+ xml:element_to_binary(
{xmlelement, "proceed", [{"xmlns", ?NS_TLS}], []})),
fsm_next_state(wait_for_stream,
StateData#state{socket = TLSSocket,
@@ -650,7 +650,7 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
Socket = StateData#state.socket,
ZlibSocket = (StateData#state.sockmod):compress(
Socket,
- xml:element_to_string(
+ xml:element_to_binary(
{xmlelement, "compressed",
[{"xmlns", ?NS_COMPRESS}], []})),
fsm_next_state(wait_for_stream,
@@ -1453,14 +1453,14 @@ change_shaper(StateData, JID) ->
(StateData#state.sockmod):change_shaper(StateData#state.socket, Shaper).
send_text(StateData, Text) ->
- ?DEBUG("Send XML on stream = ~p", [lists:flatten(Text)]),
+ ?DEBUG("Send XML on stream = ~p", [Text]),
(StateData#state.sockmod):send(StateData#state.socket, Text).
send_element(StateData, El) when StateData#state.xml_socket ->
(StateData#state.sockmod):send_xml(StateData#state.socket,
{xmlstreamelement, El});
send_element(StateData, El) ->
- send_text(StateData, xml:element_to_string(El)).
+ send_text(StateData, xml:element_to_binary(El)).
send_header(StateData, Server, Version, Lang)
when StateData#state.xml_socket ->
diff --git a/src/ejabberd_s2s_in.erl b/src/ejabberd_s2s_in.erl
index 9b378338c..179dc08f1 100644
--- a/src/ejabberd_s2s_in.erl
+++ b/src/ejabberd_s2s_in.erl
@@ -263,7 +263,7 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
TLSOpts = StateData#state.tls_options,
TLSSocket = (StateData#state.sockmod):starttls(
Socket, TLSOpts,
- xml:element_to_string(
+ xml:element_to_binary(
{xmlelement, "proceed", [{"xmlns", ?NS_TLS}], []})),
{next_state, wait_for_stream,
StateData#state{socket = TLSSocket,
@@ -618,7 +618,7 @@ send_text(StateData, Text) ->
(StateData#state.sockmod):send(StateData#state.socket, Text).
send_element(StateData, El) ->
- send_text(StateData, xml:element_to_string(El)).
+ send_text(StateData, xml:element_to_binary(El)).
change_shaper(StateData, Host, JID) ->
diff --git a/src/ejabberd_s2s_out.erl b/src/ejabberd_s2s_out.erl
index 69cbfdd22..01ab8d8aa 100644
--- a/src/ejabberd_s2s_out.erl
+++ b/src/ejabberd_s2s_out.erl
@@ -892,7 +892,7 @@ send_text(StateData, Text) ->
ejabberd_socket:send(StateData#state.socket, Text).
send_element(StateData, El) ->
- send_text(StateData, xml:element_to_string(El)).
+ send_text(StateData, xml:element_to_binary(El)).
send_queue(StateData, Q) ->
case queue:out(Q) of
diff --git a/src/ejabberd_service.erl b/src/ejabberd_service.erl
index 44bca0b9e..bd07bfea8 100644
--- a/src/ejabberd_service.erl
+++ b/src/ejabberd_service.erl
@@ -347,7 +347,7 @@ handle_info({route, From, To, Packet}, StateName, StateData) ->
Attrs2 = jlib:replace_from_to_attrs(jlib:jid_to_string(From),
jlib:jid_to_string(To),
Attrs),
- Text = xml:element_to_string({xmlelement, Name, Attrs2, Els}),
+ Text = xml:element_to_binary({xmlelement, Name, Attrs2, Els}),
send_text(StateData, Text);
deny ->
Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
@@ -391,7 +391,7 @@ send_text(StateData, Text) ->
(StateData#state.sockmod):send(StateData#state.socket, Text).
send_element(StateData, El) ->
- send_text(StateData, xml:element_to_string(El)).
+ send_text(StateData, xml:element_to_binary(El)).
new_id() ->
randoms:get_string().
diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl
index 8798ace2d..b0a07178b 100644
--- a/src/mod_muc/mod_muc_room.erl
+++ b/src/mod_muc/mod_muc_room.erl
@@ -162,7 +162,7 @@ normal_state({route, From, "",
trunc(gen_mod:get_module_opt(
StateData#state.server_host,
mod_muc, min_message_interval, 0) * 1000000),
- Size = iolist_size(xml:element_to_string(Packet)),
+ Size = element_size(Packet),
{MessageShaper, MessageShaperInterval} =
shaper:update(Activity#activity.message_shaper, Size),
if
@@ -1394,7 +1394,7 @@ prepare_room_queue(StateData) ->
{{value, {message, From}}, _RoomQueue} ->
Activity = get_user_activity(From, StateData),
Packet = Activity#activity.message,
- Size = iolist_size(xml:element_to_string(Packet)),
+ Size = element_size(Packet),
{RoomShaper, RoomShaperInterval} =
shaper:update(StateData#state.room_shaper, Size),
erlang:send_after(
@@ -1405,7 +1405,7 @@ prepare_room_queue(StateData) ->
{{value, {presence, From}}, _RoomQueue} ->
Activity = get_user_activity(From, StateData),
{_Nick, Packet} = Activity#activity.presence,
- Size = iolist_size(xml:element_to_string(Packet)),
+ Size = element_size(Packet),
{RoomShaper, RoomShaperInterval} =
shaper:update(StateData#state.room_shaper, Size),
erlang:send_after(
@@ -2068,7 +2068,7 @@ add_message_to_history(FromNick, FromJID, Packet, StateData) ->
jlib:jid_replace_resource(StateData#state.jid, FromNick),
StateData#state.jid,
TSPacket),
- Size = iolist_size(xml:element_to_string(SPacket)),
+ Size = element_size(SPacket),
Q1 = lqueue_in({FromNick, TSPacket, HaveSubject, TimeStamp, Size},
StateData#state.history),
add_to_log(text, {FromNick, Packet}, StateData),
@@ -3582,3 +3582,6 @@ tab_count_user(JID) ->
_ ->
0
end.
+
+element_size(El) ->
+ size(xml:element_to_binary(El)).
diff --git a/src/mod_offline_odbc.erl b/src/mod_offline_odbc.erl
index c89beda4e..063ea709e 100644
--- a/src/mod_offline_odbc.erl
+++ b/src/mod_offline_odbc.erl
@@ -123,8 +123,7 @@ loop(Host, AccessMaxOfflineMsgs) ->
M#offline_msg.timestamp))]},
XML =
ejabberd_odbc:escape(
- lists:flatten(
- xml:element_to_string(Packet))),
+ xml:element_to_binary(Packet)),
odbc_queries:add_spool_sql(Username, XML)
end, Msgs),
case catch odbc_queries:add_spool(Host, Query) of
diff --git a/src/web/ejabberd_http_bind.erl b/src/web/ejabberd_http_bind.erl
index 80ddaf3ea..27ea3c089 100644
--- a/src/web/ejabberd_http_bind.erl
+++ b/src/web/ejabberd_http_bind.erl
@@ -790,7 +790,7 @@ handle_http_put(Sid, Rid, Attrs, Payload, PayloadSize, StreamStart, IP) ->
?DEBUG("Trafic Shaper: Delaying request ~p", [Rid]),
timer:sleep(Pause),
%{200, ?HEADER,
- % xml:element_to_string(
+ % xml:element_to_binary(
% {xmlelement, "body",
% [{"xmlns", ?NS_HTTP_BIND},
% {"type", "error"}], []})};
@@ -827,21 +827,21 @@ handle_http_put_error(Reason, #http_bind{pid=FsmRef, version=Version})
case Reason of
not_exists ->
{200, ?HEADER,
- xml:element_to_string(
+ xml:element_to_binary(
{xmlelement, "body",
[{"xmlns", ?NS_HTTP_BIND},
{"type", "terminate"},
{"condition", "item-not-found"}], []})};
bad_key ->
{200, ?HEADER,
- xml:element_to_string(
+ xml:element_to_binary(
{xmlelement, "body",
[{"xmlns", ?NS_HTTP_BIND},
{"type", "terminate"},
{"condition", "item-not-found"}], []})};
polling_too_frequently ->
{200, ?HEADER,
- xml:element_to_string(
+ xml:element_to_binary(
{xmlelement, "body",
[{"xmlns", ?NS_HTTP_BIND},
{"type", "terminate"},
@@ -986,7 +986,7 @@ prepare_outpacket_response(#http_bind{id=Sid, wait=Wait,
MaxInactivity = get_max_inactivity(To, ?MAX_INACTIVITY),
MaxPause = get_max_pause(To),
{200, ?HEADER,
- xml:element_to_string(
+ xml:element_to_binary(
{xmlelement,"body",
[{"xmlns",
?NS_HTTP_BIND},
@@ -1041,7 +1041,7 @@ send_outpacket(#http_bind{pid = FsmRef}, OutPacket) ->
true ->
TypedEls = [check_default_xmlns(OEl) ||
{xmlstreamelement, OEl} <- OutPacket],
- Body = xml:element_to_string(
+ Body = xml:element_to_binary(
{xmlelement,"body",
[{"xmlns",
?NS_HTTP_BIND}],
@@ -1075,7 +1075,7 @@ send_outpacket(#http_bind{pid = FsmRef}, OutPacket) ->
StreamTail]
end,
{200, ?HEADER,
- xml:element_to_string(
+ xml:element_to_binary(
{xmlelement,"body",
[{"xmlns",
?NS_HTTP_BIND}],
@@ -1191,7 +1191,7 @@ set_inactivity_timer(_Pause, MaxInactivity) ->
elements_to_string([]) ->
[];
elements_to_string([El | Els]) ->
- xml:element_to_string(El) ++ elements_to_string(Els).
+ [xml:element_to_binary(El)|elements_to_string(Els)].
%% @spec (To, Default::integer()) -> integer()
%% where To = [] | {Host::string(), Version::string()}
diff --git a/src/xml.c b/src/xml.c
new file mode 100644
index 000000000..d1b2b7f44
--- /dev/null
+++ b/src/xml.c
@@ -0,0 +1,230 @@
+#include <erl_nif.h>
+#include <string.h>
+#include <stdio.h>
+
+struct buf {
+ int limit;
+ int len;
+ unsigned char *b;
+};
+
+static int make_element(ErlNifEnv* env, struct buf *rbuf, ERL_NIF_TERM el);
+
+static struct buf *init_buf(ErlNifEnv* env)
+{
+ struct buf *rbuf = enif_alloc(env, sizeof(struct buf));
+ rbuf->limit = 1024;
+ rbuf->len = 0;
+ rbuf->b = enif_alloc(env, rbuf->limit);
+ return rbuf;
+}
+
+static void destroy_buf(ErlNifEnv* env, struct buf *rbuf)
+{
+ if (rbuf) {
+ if (rbuf->b) {
+ enif_free(env, rbuf->b);
+ };
+ enif_free(env, rbuf);
+ };
+}
+
+inline void resize_buf(ErlNifEnv* env, struct buf *rbuf, int len_to_add)
+{
+ int new_len = rbuf->len + len_to_add;
+
+ if (new_len >= rbuf->limit) {
+ rbuf->limit = ((new_len / 1024) + 1) * 1024;
+ rbuf->b = enif_realloc(env, rbuf->b, rbuf->limit);
+ };
+}
+
+static void buf_add_char(ErlNifEnv* env, struct buf *rbuf, unsigned char c)
+{
+ resize_buf(env, rbuf, 1);
+ (rbuf->b)[rbuf->len] = c;
+ rbuf->len += 1;
+}
+
+static void buf_add_str(ErlNifEnv* env, struct buf *rbuf, char *data, int len)
+{
+ resize_buf(env, rbuf, len);
+ memcpy(rbuf->b + rbuf->len, data, len);
+ rbuf->len += len;
+}
+
+inline void crypt(ErlNifEnv* env, struct buf *rbuf, unsigned char *data, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ switch (data[i]) {
+ case '&':
+ buf_add_str(env, rbuf, "&amp;", 5);
+ break;
+ case '<':
+ buf_add_str(env, rbuf, "&lt;", 4);
+ break;
+ case '>':
+ buf_add_str(env, rbuf, "&gt;", 4);
+ break;
+ case '"':
+ buf_add_str(env, rbuf, "&quot;", 6);
+ break;
+ case '\'':
+ buf_add_str(env, rbuf, "&apos;", 6);
+ break;
+ default:
+ buf_add_char(env, rbuf, data[i]);
+ break;
+ };
+ };
+}
+
+static int make_elements(ErlNifEnv* env, struct buf *rbuf, ERL_NIF_TERM els)
+{
+ ERL_NIF_TERM head, tail;
+ int ret = 0;
+
+ while (enif_get_list_cell(env, els, &head, &tail)) {
+ ret = make_element(env, rbuf, head);
+ if (ret) {
+ els = tail;
+ } else {
+ break;
+ };
+ };
+
+ return ret;
+}
+
+static int make_attrs(ErlNifEnv* env, struct buf *rbuf, ERL_NIF_TERM attrs)
+{
+ ErlNifBinary name, data;
+ ERL_NIF_TERM head, tail;
+ const ERL_NIF_TERM *tuple;
+ int arity, ret = 1;
+
+ while (enif_get_list_cell(env, attrs, &head, &tail)) {
+ if (enif_get_tuple(env, head, &arity, &tuple)) {
+ if (arity == 2) {
+ if (enif_inspect_iolist_as_binary(env, tuple[0], &name) &&
+ enif_inspect_iolist_as_binary(env, tuple[1], &data)) {
+ buf_add_char(env, rbuf, ' ');
+ buf_add_str(env, rbuf, (char *)name.data, name.size);
+ buf_add_str(env, rbuf, "='", 2);
+ crypt(env, rbuf, data.data, data.size);
+ buf_add_char(env, rbuf, '\'');
+ attrs = tail;
+ } else {
+ ret = 0;
+ break;
+ };
+ } else {
+ ret = 0;
+ break;
+ };
+ } else {
+ ret = 0;
+ break;
+ };
+ };
+
+ return ret;
+}
+
+static int make_element(ErlNifEnv* env, struct buf *rbuf, ERL_NIF_TERM el)
+{
+ ErlNifBinary cdata, name;
+ const ERL_NIF_TERM *tuple;
+ int arity, ret = 0;
+
+ if (enif_get_tuple(env, el, &arity, &tuple)) {
+ if (arity == 2) {
+ if (!enif_compare(env, tuple[0], enif_make_atom(env, "xmlcdata"))) {
+ if (enif_inspect_iolist_as_binary(env, tuple[1], &cdata)) {
+ crypt(env, rbuf, cdata.data, cdata.size);
+ ret = 1;
+ };
+ };
+ };
+ if (arity == 4) {
+ if (!enif_compare(env, tuple[0], enif_make_atom(env, "xmlelement"))) {
+ if (enif_inspect_iolist_as_binary(env, tuple[1], &name)) {
+ buf_add_char(env, rbuf, '<');
+ buf_add_str(env, rbuf, (char *)name.data, name.size);
+ ret = make_attrs(env, rbuf, tuple[2]);
+ if (ret) {
+ if (enif_is_empty_list(env, tuple[3])) {
+ buf_add_str(env, rbuf, "/>", 2);
+ } else {
+ buf_add_char(env, rbuf, '>');
+ ret = make_elements(env, rbuf, tuple[3]);
+ if (ret) {
+ buf_add_str(env, rbuf, "</", 2);
+ buf_add_str(env, rbuf, (char*)name.data, name.size);
+ buf_add_char(env, rbuf, '>');
+ };
+ };
+ };
+ };
+ };
+ };
+ };
+
+ return ret;
+}
+
+static ERL_NIF_TERM element_to(ErlNifEnv* env, int argc,
+ const ERL_NIF_TERM argv[],
+ int as_string)
+{
+ ErlNifBinary output;
+ ERL_NIF_TERM result;
+ struct buf *rbuf;
+
+ if (argc == 1) {
+ rbuf = init_buf(env);
+ if (make_element(env, rbuf, argv[0])) {
+ if (as_string) {
+ (rbuf->b)[rbuf->len] = 0;
+ result = enif_make_string(env, (char *) rbuf->b, ERL_NIF_LATIN1);
+ destroy_buf(env, rbuf);
+ return result;
+ } else {
+ if (enif_alloc_binary(env, rbuf->len, &output)) {
+ memcpy(output.data, rbuf->b, rbuf->len);
+ result = enif_make_binary(env, &output);
+ destroy_buf(env, rbuf);
+ return result;
+ };
+ };
+ };
+ destroy_buf(env, rbuf);
+ };
+
+ return enif_make_badarg(env);
+}
+
+/* static ERL_NIF_TERM element_to_string(ErlNifEnv* env, int argc, */
+/* const ERL_NIF_TERM argv[]) */
+/* { */
+/* return element_to(env, argc, argv, 1); */
+/* } */
+
+static ERL_NIF_TERM element_to_binary(ErlNifEnv* env, int argc,
+ const ERL_NIF_TERM argv[])
+{
+ return element_to(env, argc, argv, 0);
+}
+
+static ErlNifFunc nif_funcs[] =
+ {
+ /* Stupid Erlang bug with enif_make_string() is fixed
+ in R14A only (OTP-8685), so we can't use
+ element_to_string yet.*/
+ /* {"element_to_string", 1, element_to_string}, */
+ {"element_to_binary", 1, element_to_binary}
+ };
+
+ERL_NIF_INIT(xml, nif_funcs, NULL, NULL, NULL, NULL)
diff --git a/src/xml.erl b/src/xml.erl
index 9a9a7f833..22746b10a 100644
--- a/src/xml.erl
+++ b/src/xml.erl
@@ -37,8 +37,11 @@
get_subtag/2, get_subtag_cdata/2,
append_subtags/2,
get_path_s/2,
+ start/0,
replace_tag_attr/3]).
+-include("ejabberd.hrl").
+
%% Select at compile time how to escape characters in binary text
%% nodes.
%% Can be choosen with ./configure --enable-full-xml
@@ -48,6 +51,22 @@
-define(ESCAPE_BINARY(CData), crypt(CData)).
-endif.
+%% Replace element_to_binary/1 with NIF
+%% Can be choosen with ./configure --enable-nif
+-ifdef(NIF).
+start() ->
+ SOPath = filename:join(ejabberd:get_so_path(), "xml"),
+ case catch erlang:load_nif(SOPath, 0) of
+ ok ->
+ ok;
+ Err ->
+ ?WARNING_MSG("unable to load xml NIF: ~p", [Err])
+ end.
+-else.
+start() ->
+ ok.
+-endif.
+
element_to_binary(El) ->
iolist_to_binary(element_to_string(El)).