mirror of
https://github.com/processone/ejabberd.git
synced 2024-12-20 17:27:00 +01:00
Add xml compression to sql backend of mam
This commit is contained in:
parent
e37a1a73f1
commit
0b31aa490b
@ -1172,7 +1172,7 @@ mod_opt_type(O) when O == cache_life_time; O == cache_size ->
|
||||
fun (I) when is_integer(I), I > 0 -> I;
|
||||
(infinity) -> infinity
|
||||
end;
|
||||
mod_opt_type(O) when O == use_cache; O == cache_missed ->
|
||||
mod_opt_type(O) when O == use_cache; O == cache_missed; O == compress_xml ->
|
||||
fun (B) when is_boolean(B) -> B end;
|
||||
mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
|
||||
mod_opt_type(default) ->
|
||||
@ -1187,6 +1187,7 @@ mod_options(Host) ->
|
||||
[{assume_mam_usage, false},
|
||||
{default, never},
|
||||
{request_activates_archiving, false},
|
||||
{compress_xml, false},
|
||||
{db_type, ejabberd_config:default_db(Host, ?MODULE)},
|
||||
{use_cache, ejabberd_config:use_cache(Host)},
|
||||
{cache_size, ejabberd_config:cache_size(Host)},
|
||||
|
@ -102,9 +102,18 @@ store(Pkt, LServer, {LUser, LHost}, Type, Peer, Nick, _Dir, TS) ->
|
||||
jid:remove_resource(Peer))),
|
||||
LPeer = jid:encode(
|
||||
jid:tolower(Peer)),
|
||||
XML = fxml:element_to_binary(Pkt),
|
||||
Body = fxml:get_subtag_cdata(Pkt, <<"body">>),
|
||||
SType = misc:atom_to_binary(Type),
|
||||
XML = case gen_mod:get_module_opt(LServer, mod_mam, compress_xml) of
|
||||
true ->
|
||||
J1 = case Type of
|
||||
chat -> jid:encode({LUser, LHost, <<>>});
|
||||
groupchat -> SUser
|
||||
end,
|
||||
xml_compress:encode(Pkt, J1, LPeer);
|
||||
_ ->
|
||||
fxml:element_to_binary(Pkt)
|
||||
end,
|
||||
case ejabberd_sql:sql_query(
|
||||
LServer,
|
||||
?SQL_INSERT(
|
||||
@ -192,8 +201,8 @@ select(LServer, JidRequestor, #jid{luser = LUser} = JidArchive,
|
||||
{lists:flatmap(
|
||||
fun([TS, XML, PeerBin, Kind, Nick]) ->
|
||||
case make_archive_el(
|
||||
TS, XML, PeerBin, Kind, Nick,
|
||||
MsgType, JidRequestor, JidArchive) of
|
||||
jid:encode(JidArchive), TS, XML, PeerBin, Kind, Nick,
|
||||
MsgType, JidRequestor, JidArchive) of
|
||||
{ok, El} ->
|
||||
[{TS, binary_to_integer(TS), El}];
|
||||
{error, _} ->
|
||||
@ -399,13 +408,13 @@ get_max_direction_id(RSM) ->
|
||||
{undefined, undefined, <<>>}
|
||||
end.
|
||||
|
||||
-spec make_archive_el(binary(), binary(), binary(), binary(),
|
||||
-spec make_archive_el(binary(), binary(), binary(), binary(), binary(),
|
||||
binary(), _, jid(), jid()) ->
|
||||
{ok, xmpp_element()} | {error, invalid_jid |
|
||||
invalid_timestamp |
|
||||
invalid_xml}.
|
||||
make_archive_el(TS, XML, Peer, Kind, Nick, MsgType, JidRequestor, JidArchive) ->
|
||||
case fxml_stream:parse_element(XML) of
|
||||
make_archive_el(User, TS, XML, Peer, Kind, Nick, MsgType, JidRequestor, JidArchive) ->
|
||||
case xml_compress:decode(XML, User, Peer) of
|
||||
#xmlel{} = El ->
|
||||
try binary_to_integer(TS) of
|
||||
TSInt ->
|
||||
|
958
src/xml_compress.erl
Normal file
958
src/xml_compress.erl
Normal file
@ -0,0 +1,958 @@
|
||||
-module(xml_compress).
|
||||
-export([encode/3, decode/3]).
|
||||
|
||||
% This file was generated by xml_compress_gen
|
||||
%
|
||||
% Rules used:
|
||||
%
|
||||
% [{<<"eu.siacs.conversations.axolotl">>,<<"key">>,
|
||||
% [{<<"prekey">>,[<<"true">>]},{<<"rid">>,[]}],
|
||||
% []},
|
||||
% {<<"jabber:client">>,<<"message">>,
|
||||
% [{<<"from">>,[j2,{j1}]},
|
||||
% {<<"id">>,[]},
|
||||
% {<<"to">>,[j1,j2,{j1}]},
|
||||
% {<<"type">>,[<<"chat">>,<<"groupchat">>,<<"normal">>]},
|
||||
% {<<"xml:lang">>,[<<"en">>]}],
|
||||
% []},
|
||||
% {<<"urn:xmpp:hints">>,<<"store">>,[],[]},
|
||||
% {<<"jabber:client">>,<<"body">>,[],
|
||||
% [<<73,32,115,101,110,116,32,121,111,117,32,97,110,32,79,77,69,77,79,32,101,
|
||||
% 110,99,114,121,112,116,101,100,32,109,101,115,115,97,103,101,32,98,117,
|
||||
% 116,32,121,111,117,114,32,99,108,105,101,110,116,32,100,111,101,115,110,
|
||||
% 226,128,153,116,32,115,101,101,109,32,116,111,32,115,117,112,112,111,
|
||||
% 114,116,32,116,104,97,116,46,32,70,105,110,100,32,109,111,114,101,32,
|
||||
% 105,110,102,111,114,109,97,116,105,111,110,32,111,110,32,104,116,116,
|
||||
% 112,115,58,47,47,99,111,110,118,101,114,115,97,116,105,111,110,115,46,
|
||||
% 105,109,47,111,109,101,109,111>>]},
|
||||
% {<<"urn:xmpp:sid:0">>,<<"origin-id">>,[{<<"id">>,[]}],[]},
|
||||
% {<<"urn:xmpp:chat-markers:0">>,<<"markable">>,[],[]},
|
||||
% {<<"eu.siacs.conversations.axolotl">>,<<"encrypted">>,[],[]},
|
||||
% {<<"eu.siacs.conversations.axolotl">>,<<"header">>,[{<<"sid">>,[]}],[]},
|
||||
% {<<"eu.siacs.conversations.axolotl">>,<<"iv">>,[],[]},
|
||||
% {<<"eu.siacs.conversations.axolotl">>,<<"payload">>,[],[]},
|
||||
% {<<"urn:xmpp:eme:0">>,<<"encryption">>,
|
||||
% [{<<"name">>,[<<"OMEMO">>]},
|
||||
% {<<"namespace">>,[<<"eu.siacs.conversations.axolotl">>]}],
|
||||
% []},
|
||||
% {<<"urn:xmpp:delay">>,<<"delay">>,[{<<"from">>,[j1]},{<<"stamp">>,[]}],[]},
|
||||
% {<<"http://jabber.org/protocol/address">>,<<"address">>,
|
||||
% [{<<"jid">>,[{j1}]},{<<"type">>,[<<"ofrom">>]}],
|
||||
% []},
|
||||
% {<<"http://jabber.org/protocol/address">>,<<"addresses">>,[],[]},
|
||||
% {<<"urn:xmpp:chat-markers:0">>,<<"displayed">>,
|
||||
% [{<<"id">>,[]},{<<"sender">>,[{j1},{j2}]}],
|
||||
% []},
|
||||
% {<<"urn:xmpp:mam:tmp">>,<<"archived">>,[{<<"by">>,[]},{<<"id">>,[]}],[]},
|
||||
% {<<"urn:xmpp:sid:0">>,<<"stanza-id">>,[{<<"by">>,[]},{<<"id">>,[]}],[]},
|
||||
% {<<"urn:xmpp:receipts">>,<<"request">>,[],[]},
|
||||
% {<<"urn:xmpp:chat-markers:0">>,<<"received">>,[{<<"id">>,[]}],[]},
|
||||
% {<<"urn:xmpp:receipts">>,<<"received">>,[{<<"id">>,[]}],[]},
|
||||
% {<<"http://jabber.org/protocol/chatstates">>,<<"active">>,[],[]},
|
||||
% {<<"http://jabber.org/protocol/muc#user">>,<<"invite">>,
|
||||
% [{<<"from">>,[{j1}]}],
|
||||
% []},
|
||||
% {<<"http://jabber.org/protocol/muc#user">>,<<"reason">>,[],[]},
|
||||
% {<<"http://jabber.org/protocol/muc#user">>,<<"x">>,[],[]},
|
||||
% {<<"jabber:x:conference">>,<<"x">>,[{<<"jid">>,[j2]}],[]},
|
||||
% {<<"jabber:client">>,<<"subject">>,[],[]},
|
||||
% {<<"jabber:client">>,<<"thread">>,[],[]},
|
||||
% {<<"http://jabber.org/protocol/pubsub#event">>,<<"event">>,[],[]},
|
||||
% {<<"http://jabber.org/protocol/pubsub#event">>,<<"item">>,[{<<"id">>,[]}],[]},
|
||||
% {<<"http://jabber.org/protocol/pubsub#event">>,<<"items">>,
|
||||
% [{<<"node">>,[<<"urn:xmpp:mucsub:nodes:messages">>]}],
|
||||
% []},
|
||||
% {<<"p1:push:custom">>,<<"x">>,[{<<"key">>,[]},{<<"value">>,[]}],[]},
|
||||
% {<<"p1:pushed">>,<<"x">>,[],[]},
|
||||
% {<<"urn:xmpp:message-correct:0">>,<<"replace">>,[{<<"id">>,[]}],[]},
|
||||
% {<<"http://jabber.org/protocol/chatstates">>,<<"composing">>,[],[]}]
|
||||
|
||||
encode(El, J1, J2) ->
|
||||
encode_child(El, <<"jabber:client">>,
|
||||
J1, J2, byte_size(J1), byte_size(J2), <<1:8>>).
|
||||
|
||||
encode_attr({<<"xmlns">>, _}, Acc) ->
|
||||
Acc;
|
||||
encode_attr({N, V}, Acc) ->
|
||||
<<Acc/binary, 1:8, (encode_string(N))/binary,
|
||||
(encode_string(V))/binary>>.
|
||||
|
||||
encode_attrs(Attrs, Acc) ->
|
||||
lists:foldl(fun encode_attr/2, Acc, Attrs).
|
||||
|
||||
encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
|
||||
E1 = if
|
||||
PNs == Ns -> encode_attrs(Attrs, <<Pfx/binary, 2:8, (encode_string(Name))/binary>>);
|
||||
true -> encode_attrs(Attrs, <<Pfx/binary, 3:8, (encode_string(Ns))/binary, (encode_string(Name))/binary>>)
|
||||
end,
|
||||
E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E1/binary, 2:8>>),
|
||||
<<E2/binary, 4:8>>.
|
||||
|
||||
encode_child({xmlel, Name, Attrs, Children}, PNs, J1, J2, J1L, J2L, Pfx) ->
|
||||
case lists:keyfind(<<"xmlns">>, 1, Attrs) of
|
||||
false ->
|
||||
encode(PNs, PNs, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx);
|
||||
{_, Ns} ->
|
||||
encode(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx)
|
||||
end;
|
||||
encode_child({xmlcdata, Data}, _PNs, _J1, _J2, _J1L, _J2L, Pfx) ->
|
||||
<<Pfx/binary, 1:8, (encode_string(Data))/binary>>.
|
||||
|
||||
encode_children(Children, PNs, J1, J2, J1L, J2L, Pfx) ->
|
||||
lists:foldl(
|
||||
fun(Child, Acc) ->
|
||||
encode_child(Child, PNs, J1, J2, J1L, J2L, Acc)
|
||||
end, Pfx, Children).
|
||||
|
||||
encode_string(Data) ->
|
||||
<<V1:4, V2:6, V3:6>> = <<(byte_size(Data)):16/unsigned-big-integer>>,
|
||||
case {V1, V2, V3} of
|
||||
{0, 0, V3} ->
|
||||
<<V3:8, Data/binary>>;
|
||||
{0, V2, V3} ->
|
||||
<<(V3 bor 64):8, V2:8, Data/binary>>;
|
||||
_ ->
|
||||
<<(V3 bor 64):8, (V2 bor 64):8, V1:8, Data/binary>>
|
||||
end.
|
||||
|
||||
encode(PNs, <<"eu.siacs.conversations.axolotl">> = Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
|
||||
case Name of
|
||||
<<"key">> ->
|
||||
E = lists:foldl(fun
|
||||
({<<"prekey">>, AVal}, Acc) ->
|
||||
case AVal of
|
||||
<<"true">> -> <<Acc/binary, 3:8>>;
|
||||
_ -> <<Acc/binary, 4:8, (encode_string(AVal))/binary>>
|
||||
end;
|
||||
({<<"rid">>, AVal}, Acc) ->
|
||||
<<Acc/binary, 5:8, (encode_string(AVal))/binary>>;
|
||||
(Attr, Acc) -> encode_attr(Attr, Acc)
|
||||
end, <<Pfx/binary, 5:8>>, Attrs),
|
||||
E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
|
||||
<<E2/binary, 4:8>>;
|
||||
<<"encrypted">> ->
|
||||
E = encode_attrs(Attrs, <<Pfx/binary, 12:8>>),
|
||||
E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
|
||||
<<E2/binary, 4:8>>;
|
||||
<<"header">> ->
|
||||
E = lists:foldl(fun
|
||||
({<<"sid">>, AVal}, Acc) ->
|
||||
<<Acc/binary, 3:8, (encode_string(AVal))/binary>>;
|
||||
(Attr, Acc) -> encode_attr(Attr, Acc)
|
||||
end, <<Pfx/binary, 13:8>>, Attrs),
|
||||
E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
|
||||
<<E2/binary, 4:8>>;
|
||||
<<"iv">> ->
|
||||
E = encode_attrs(Attrs, <<Pfx/binary, 14:8>>),
|
||||
E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
|
||||
<<E2/binary, 4:8>>;
|
||||
<<"payload">> ->
|
||||
E = encode_attrs(Attrs, <<Pfx/binary, 15:8>>),
|
||||
E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
|
||||
<<E2/binary, 4:8>>;
|
||||
_ -> encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx)
|
||||
end;
|
||||
encode(PNs, <<"jabber:client">> = Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
|
||||
case Name of
|
||||
<<"message">> ->
|
||||
E = lists:foldl(fun
|
||||
({<<"from">>, AVal}, Acc) ->
|
||||
case AVal of
|
||||
J2 -> <<Acc/binary, 3:8>>;
|
||||
<<J1:J1L/binary, Rest/binary>> -> <<Acc/binary, 4:8, (encode_string(Rest))/binary>>;
|
||||
_ -> <<Acc/binary, 5:8, (encode_string(AVal))/binary>>
|
||||
end;
|
||||
({<<"id">>, AVal}, Acc) ->
|
||||
<<Acc/binary, 6:8, (encode_string(AVal))/binary>>;
|
||||
({<<"to">>, AVal}, Acc) ->
|
||||
case AVal of
|
||||
J1 -> <<Acc/binary, 7:8>>;
|
||||
J2 -> <<Acc/binary, 8:8>>;
|
||||
<<J1:J1L/binary, Rest/binary>> -> <<Acc/binary, 9:8, (encode_string(Rest))/binary>>;
|
||||
_ -> <<Acc/binary, 10:8, (encode_string(AVal))/binary>>
|
||||
end;
|
||||
({<<"type">>, AVal}, Acc) ->
|
||||
case AVal of
|
||||
<<"chat">> -> <<Acc/binary, 11:8>>;
|
||||
<<"groupchat">> -> <<Acc/binary, 12:8>>;
|
||||
<<"normal">> -> <<Acc/binary, 13:8>>;
|
||||
_ -> <<Acc/binary, 14:8, (encode_string(AVal))/binary>>
|
||||
end;
|
||||
({<<"xml:lang">>, AVal}, Acc) ->
|
||||
case AVal of
|
||||
<<"en">> -> <<Acc/binary, 15:8>>;
|
||||
_ -> <<Acc/binary, 16:8, (encode_string(AVal))/binary>>
|
||||
end;
|
||||
(Attr, Acc) -> encode_attr(Attr, Acc)
|
||||
end, <<Pfx/binary, 6:8>>, Attrs),
|
||||
E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
|
||||
<<E2/binary, 4:8>>;
|
||||
<<"body">> ->
|
||||
E = encode_attrs(Attrs, <<Pfx/binary, 8:8>>),
|
||||
E2 = lists:foldl(fun
|
||||
({xmlcdata, <<73,32,115,101,110,116,32,121,111,117,32,97,110,32,79,77,69,
|
||||
77,79,32,101,110,99,114,121,112,116,101,100,32,109,101,115,
|
||||
115,97,103,101,32,98,117,116,32,121,111,117,114,32,99,108,
|
||||
105,101,110,116,32,100,111,101,115,110,226,128,153,116,32,
|
||||
115,101,101,109,32,116,111,32,115,117,112,112,111,114,116,32,
|
||||
116,104,97,116,46,32,70,105,110,100,32,109,111,114,101,32,
|
||||
105,110,102,111,114,109,97,116,105,111,110,32,111,110,32,104,
|
||||
116,116,112,115,58,47,47,99,111,110,118,101,114,115,97,116,
|
||||
105,111,110,115,46,105,109,47,111,109,101,109,111>>}, Acc) -> <<Acc/binary, 9:8>>;
|
||||
(El, Acc) -> encode_child(El, Ns, J1, J2, J1L, J2L, Acc)
|
||||
end, <<E/binary, 2:8>>, Children),
|
||||
<<E2/binary, 4:8>>;
|
||||
<<"subject">> ->
|
||||
E = encode_attrs(Attrs, <<Pfx/binary, 31:8>>),
|
||||
E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
|
||||
<<E2/binary, 4:8>>;
|
||||
<<"thread">> ->
|
||||
E = encode_attrs(Attrs, <<Pfx/binary, 32:8>>),
|
||||
E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
|
||||
<<E2/binary, 4:8>>;
|
||||
_ -> encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx)
|
||||
end;
|
||||
encode(PNs, <<"urn:xmpp:hints">> = Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
|
||||
case Name of
|
||||
<<"store">> ->
|
||||
E = encode_attrs(Attrs, <<Pfx/binary, 7:8>>),
|
||||
E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
|
||||
<<E2/binary, 4:8>>;
|
||||
_ -> encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx)
|
||||
end;
|
||||
encode(PNs, <<"urn:xmpp:sid:0">> = Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
|
||||
case Name of
|
||||
<<"origin-id">> ->
|
||||
E = lists:foldl(fun
|
||||
({<<"id">>, AVal}, Acc) ->
|
||||
<<Acc/binary, 3:8, (encode_string(AVal))/binary>>;
|
||||
(Attr, Acc) -> encode_attr(Attr, Acc)
|
||||
end, <<Pfx/binary, 10:8>>, Attrs),
|
||||
E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
|
||||
<<E2/binary, 4:8>>;
|
||||
<<"stanza-id">> ->
|
||||
E = lists:foldl(fun
|
||||
({<<"by">>, AVal}, Acc) ->
|
||||
<<Acc/binary, 3:8, (encode_string(AVal))/binary>>;
|
||||
({<<"id">>, AVal}, Acc) ->
|
||||
<<Acc/binary, 4:8, (encode_string(AVal))/binary>>;
|
||||
(Attr, Acc) -> encode_attr(Attr, Acc)
|
||||
end, <<Pfx/binary, 22:8>>, Attrs),
|
||||
E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
|
||||
<<E2/binary, 4:8>>;
|
||||
_ -> encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx)
|
||||
end;
|
||||
encode(PNs, <<"urn:xmpp:chat-markers:0">> = Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
|
||||
case Name of
|
||||
<<"markable">> ->
|
||||
E = encode_attrs(Attrs, <<Pfx/binary, 11:8>>),
|
||||
E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
|
||||
<<E2/binary, 4:8>>;
|
||||
<<"displayed">> ->
|
||||
E = lists:foldl(fun
|
||||
({<<"id">>, AVal}, Acc) ->
|
||||
<<Acc/binary, 3:8, (encode_string(AVal))/binary>>;
|
||||
({<<"sender">>, AVal}, Acc) ->
|
||||
case AVal of
|
||||
<<J1:J1L/binary, Rest/binary>> -> <<Acc/binary, 4:8, (encode_string(Rest))/binary>>;
|
||||
<<J2:J2L/binary, Rest/binary>> -> <<Acc/binary, 5:8, (encode_string(Rest))/binary>>;
|
||||
_ -> <<Acc/binary, 6:8, (encode_string(AVal))/binary>>
|
||||
end;
|
||||
(Attr, Acc) -> encode_attr(Attr, Acc)
|
||||
end, <<Pfx/binary, 20:8>>, Attrs),
|
||||
E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
|
||||
<<E2/binary, 4:8>>;
|
||||
<<"received">> ->
|
||||
E = lists:foldl(fun
|
||||
({<<"id">>, AVal}, Acc) ->
|
||||
<<Acc/binary, 3:8, (encode_string(AVal))/binary>>;
|
||||
(Attr, Acc) -> encode_attr(Attr, Acc)
|
||||
end, <<Pfx/binary, 24:8>>, Attrs),
|
||||
E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
|
||||
<<E2/binary, 4:8>>;
|
||||
_ -> encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx)
|
||||
end;
|
||||
encode(PNs, <<"urn:xmpp:eme:0">> = Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
|
||||
case Name of
|
||||
<<"encryption">> ->
|
||||
E = lists:foldl(fun
|
||||
({<<"name">>, AVal}, Acc) ->
|
||||
case AVal of
|
||||
<<"OMEMO">> -> <<Acc/binary, 3:8>>;
|
||||
_ -> <<Acc/binary, 4:8, (encode_string(AVal))/binary>>
|
||||
end;
|
||||
({<<"namespace">>, AVal}, Acc) ->
|
||||
case AVal of
|
||||
<<"eu.siacs.conversations.axolotl">> -> <<Acc/binary, 5:8>>;
|
||||
_ -> <<Acc/binary, 6:8, (encode_string(AVal))/binary>>
|
||||
end;
|
||||
(Attr, Acc) -> encode_attr(Attr, Acc)
|
||||
end, <<Pfx/binary, 16:8>>, Attrs),
|
||||
E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
|
||||
<<E2/binary, 4:8>>;
|
||||
_ -> encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx)
|
||||
end;
|
||||
encode(PNs, <<"urn:xmpp:delay">> = Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
|
||||
case Name of
|
||||
<<"delay">> ->
|
||||
E = lists:foldl(fun
|
||||
({<<"from">>, AVal}, Acc) ->
|
||||
case AVal of
|
||||
J1 -> <<Acc/binary, 3:8>>;
|
||||
_ -> <<Acc/binary, 4:8, (encode_string(AVal))/binary>>
|
||||
end;
|
||||
({<<"stamp">>, AVal}, Acc) ->
|
||||
<<Acc/binary, 5:8, (encode_string(AVal))/binary>>;
|
||||
(Attr, Acc) -> encode_attr(Attr, Acc)
|
||||
end, <<Pfx/binary, 17:8>>, Attrs),
|
||||
E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
|
||||
<<E2/binary, 4:8>>;
|
||||
_ -> encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx)
|
||||
end;
|
||||
encode(PNs, <<"http://jabber.org/protocol/address">> = Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
|
||||
case Name of
|
||||
<<"address">> ->
|
||||
E = lists:foldl(fun
|
||||
({<<"jid">>, AVal}, Acc) ->
|
||||
case AVal of
|
||||
<<J1:J1L/binary, Rest/binary>> -> <<Acc/binary, 3:8, (encode_string(Rest))/binary>>;
|
||||
_ -> <<Acc/binary, 4:8, (encode_string(AVal))/binary>>
|
||||
end;
|
||||
({<<"type">>, AVal}, Acc) ->
|
||||
case AVal of
|
||||
<<"ofrom">> -> <<Acc/binary, 5:8>>;
|
||||
_ -> <<Acc/binary, 6:8, (encode_string(AVal))/binary>>
|
||||
end;
|
||||
(Attr, Acc) -> encode_attr(Attr, Acc)
|
||||
end, <<Pfx/binary, 18:8>>, Attrs),
|
||||
E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
|
||||
<<E2/binary, 4:8>>;
|
||||
<<"addresses">> ->
|
||||
E = encode_attrs(Attrs, <<Pfx/binary, 19:8>>),
|
||||
E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
|
||||
<<E2/binary, 4:8>>;
|
||||
_ -> encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx)
|
||||
end;
|
||||
encode(PNs, <<"urn:xmpp:mam:tmp">> = Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
|
||||
case Name of
|
||||
<<"archived">> ->
|
||||
E = lists:foldl(fun
|
||||
({<<"by">>, AVal}, Acc) ->
|
||||
<<Acc/binary, 3:8, (encode_string(AVal))/binary>>;
|
||||
({<<"id">>, AVal}, Acc) ->
|
||||
<<Acc/binary, 4:8, (encode_string(AVal))/binary>>;
|
||||
(Attr, Acc) -> encode_attr(Attr, Acc)
|
||||
end, <<Pfx/binary, 21:8>>, Attrs),
|
||||
E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
|
||||
<<E2/binary, 4:8>>;
|
||||
_ -> encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx)
|
||||
end;
|
||||
encode(PNs, <<"urn:xmpp:receipts">> = Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
|
||||
case Name of
|
||||
<<"request">> ->
|
||||
E = encode_attrs(Attrs, <<Pfx/binary, 23:8>>),
|
||||
E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
|
||||
<<E2/binary, 4:8>>;
|
||||
<<"received">> ->
|
||||
E = lists:foldl(fun
|
||||
({<<"id">>, AVal}, Acc) ->
|
||||
<<Acc/binary, 3:8, (encode_string(AVal))/binary>>;
|
||||
(Attr, Acc) -> encode_attr(Attr, Acc)
|
||||
end, <<Pfx/binary, 25:8>>, Attrs),
|
||||
E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
|
||||
<<E2/binary, 4:8>>;
|
||||
_ -> encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx)
|
||||
end;
|
||||
encode(PNs, <<"http://jabber.org/protocol/chatstates">> = Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
|
||||
case Name of
|
||||
<<"active">> ->
|
||||
E = encode_attrs(Attrs, <<Pfx/binary, 26:8>>),
|
||||
E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
|
||||
<<E2/binary, 4:8>>;
|
||||
<<"composing">> ->
|
||||
E = encode_attrs(Attrs, <<Pfx/binary, 39:8>>),
|
||||
E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
|
||||
<<E2/binary, 4:8>>;
|
||||
_ -> encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx)
|
||||
end;
|
||||
encode(PNs, <<"http://jabber.org/protocol/muc#user">> = Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
|
||||
case Name of
|
||||
<<"invite">> ->
|
||||
E = lists:foldl(fun
|
||||
({<<"from">>, AVal}, Acc) ->
|
||||
case AVal of
|
||||
<<J1:J1L/binary, Rest/binary>> -> <<Acc/binary, 3:8, (encode_string(Rest))/binary>>;
|
||||
_ -> <<Acc/binary, 4:8, (encode_string(AVal))/binary>>
|
||||
end;
|
||||
(Attr, Acc) -> encode_attr(Attr, Acc)
|
||||
end, <<Pfx/binary, 27:8>>, Attrs),
|
||||
E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
|
||||
<<E2/binary, 4:8>>;
|
||||
<<"reason">> ->
|
||||
E = encode_attrs(Attrs, <<Pfx/binary, 28:8>>),
|
||||
E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
|
||||
<<E2/binary, 4:8>>;
|
||||
<<"x">> ->
|
||||
E = encode_attrs(Attrs, <<Pfx/binary, 29:8>>),
|
||||
E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
|
||||
<<E2/binary, 4:8>>;
|
||||
_ -> encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx)
|
||||
end;
|
||||
encode(PNs, <<"jabber:x:conference">> = Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
|
||||
case Name of
|
||||
<<"x">> ->
|
||||
E = lists:foldl(fun
|
||||
({<<"jid">>, AVal}, Acc) ->
|
||||
case AVal of
|
||||
J2 -> <<Acc/binary, 3:8>>;
|
||||
_ -> <<Acc/binary, 4:8, (encode_string(AVal))/binary>>
|
||||
end;
|
||||
(Attr, Acc) -> encode_attr(Attr, Acc)
|
||||
end, <<Pfx/binary, 30:8>>, Attrs),
|
||||
E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
|
||||
<<E2/binary, 4:8>>;
|
||||
_ -> encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx)
|
||||
end;
|
||||
encode(PNs, <<"http://jabber.org/protocol/pubsub#event">> = Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
|
||||
case Name of
|
||||
<<"event">> ->
|
||||
E = encode_attrs(Attrs, <<Pfx/binary, 33:8>>),
|
||||
E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
|
||||
<<E2/binary, 4:8>>;
|
||||
<<"item">> ->
|
||||
E = lists:foldl(fun
|
||||
({<<"id">>, AVal}, Acc) ->
|
||||
<<Acc/binary, 3:8, (encode_string(AVal))/binary>>;
|
||||
(Attr, Acc) -> encode_attr(Attr, Acc)
|
||||
end, <<Pfx/binary, 34:8>>, Attrs),
|
||||
E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
|
||||
<<E2/binary, 4:8>>;
|
||||
<<"items">> ->
|
||||
E = lists:foldl(fun
|
||||
({<<"node">>, AVal}, Acc) ->
|
||||
case AVal of
|
||||
<<"urn:xmpp:mucsub:nodes:messages">> -> <<Acc/binary, 3:8>>;
|
||||
_ -> <<Acc/binary, 4:8, (encode_string(AVal))/binary>>
|
||||
end;
|
||||
(Attr, Acc) -> encode_attr(Attr, Acc)
|
||||
end, <<Pfx/binary, 35:8>>, Attrs),
|
||||
E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
|
||||
<<E2/binary, 4:8>>;
|
||||
_ -> encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx)
|
||||
end;
|
||||
encode(PNs, <<"p1:push:custom">> = Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
|
||||
case Name of
|
||||
<<"x">> ->
|
||||
E = lists:foldl(fun
|
||||
({<<"key">>, AVal}, Acc) ->
|
||||
<<Acc/binary, 3:8, (encode_string(AVal))/binary>>;
|
||||
({<<"value">>, AVal}, Acc) ->
|
||||
<<Acc/binary, 4:8, (encode_string(AVal))/binary>>;
|
||||
(Attr, Acc) -> encode_attr(Attr, Acc)
|
||||
end, <<Pfx/binary, 36:8>>, Attrs),
|
||||
E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
|
||||
<<E2/binary, 4:8>>;
|
||||
_ -> encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx)
|
||||
end;
|
||||
encode(PNs, <<"p1:pushed">> = Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
|
||||
case Name of
|
||||
<<"x">> ->
|
||||
E = encode_attrs(Attrs, <<Pfx/binary, 37:8>>),
|
||||
E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
|
||||
<<E2/binary, 4:8>>;
|
||||
_ -> encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx)
|
||||
end;
|
||||
encode(PNs, <<"urn:xmpp:message-correct:0">> = Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
|
||||
case Name of
|
||||
<<"replace">> ->
|
||||
E = lists:foldl(fun
|
||||
({<<"id">>, AVal}, Acc) ->
|
||||
<<Acc/binary, 3:8, (encode_string(AVal))/binary>>;
|
||||
(Attr, Acc) -> encode_attr(Attr, Acc)
|
||||
end, <<Pfx/binary, 38:8>>, Attrs),
|
||||
E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
|
||||
<<E2/binary, 4:8>>;
|
||||
_ -> encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx)
|
||||
end;
|
||||
encode(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
|
||||
encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx).
|
||||
|
||||
decode(<<$<, _/binary>> = Data, _J1, _J2) ->
|
||||
fxml_stream:parse_element(Data);
|
||||
decode(<<1:8, Rest/binary>>, J1, J2) ->
|
||||
{El, _} = decode(Rest, <<"jabber:client">>, J1, J2),
|
||||
El.
|
||||
|
||||
decode_string(Data) ->
|
||||
case Data of
|
||||
<<0:2, L:6, Str:L/binary, Rest/binary>> ->
|
||||
{Str, Rest};
|
||||
<<1:2, L1:6, 0:2, L2:6, Rest/binary>> ->
|
||||
L = L2*64 + L1,
|
||||
<<Str:L/binary, Rest2/binary>> = Rest,
|
||||
{Str, Rest2};
|
||||
<<1:2, L1:6, 1:2, L2:6, L3:8, Rest/binary>> ->
|
||||
L = (L3*64 + L2)*64 + L1,
|
||||
<<Str:L/binary, Rest2/binary>> = Rest,
|
||||
{Str, Rest2}
|
||||
end.
|
||||
|
||||
decode_child(<<1:8, Rest/binary>>, _PNs, _J1, _J2) ->
|
||||
{Text, Rest2} = decode_string(Rest),
|
||||
{{xmlcdata, Text}, Rest2};
|
||||
decode_child(<<2:8, Rest/binary>>, PNs, J1, J2) ->
|
||||
{Name, Rest2} = decode_string(Rest),
|
||||
{Attrs, Rest3} = decode_attrs(Rest2),
|
||||
{Children, Rest4} = decode_children(Rest3, PNs, J1, J2),
|
||||
{{xmlel, Name, Attrs, Children}, Rest4};
|
||||
decode_child(<<3:8, Rest/binary>>, PNs, J1, J2) ->
|
||||
{Name, Rest2} = decode_string(Rest),
|
||||
{Ns, Rest3} = decode_string(Rest2),
|
||||
{Attrs, Rest4} = decode_attrs(Rest3),
|
||||
{Children, Rest5} = decode_children(Rest4, Ns, J1, J2),
|
||||
{{xmlel, Name, add_ns(PNs, Ns, Attrs), Children}, Rest5};
|
||||
decode_child(<<4:8, Rest/binary>>, _PNs, _J1, _J2) ->
|
||||
{stop, Rest};
|
||||
decode_child(Other, PNs, J1, J2) ->
|
||||
decode(Other, PNs, J1, J2).
|
||||
|
||||
decode_children(Data, PNs, J1, J2) ->
|
||||
prefix_map(fun(Data2) -> decode(Data2, PNs, J1, J2) end, Data).
|
||||
|
||||
decode_attr(<<1:8, Rest/binary>>) ->
|
||||
{Name, Rest2} = decode_string(Rest),
|
||||
{Val, Rest3} = decode_string(Rest2),
|
||||
{{Name, Val}, Rest3};
|
||||
decode_attr(<<2:8, Rest/binary>>) ->
|
||||
{stop, Rest}.
|
||||
|
||||
decode_attrs(Data) ->
|
||||
prefix_map(fun decode_attr/1, Data).
|
||||
|
||||
prefix_map(F, Data) ->
|
||||
prefix_map(F, Data, []).
|
||||
|
||||
prefix_map(F, Data, Acc) ->
|
||||
case F(Data) of
|
||||
{stop, Rest} ->
|
||||
{lists:reverse(Acc), Rest};
|
||||
{Val, Rest} ->
|
||||
prefix_map(F, Rest, [Val | Acc])
|
||||
end.
|
||||
|
||||
add_ns(Ns, Ns, Attrs) ->
|
||||
Attrs;
|
||||
add_ns(_, Ns, Attrs) ->
|
||||
[{<<"xmlns">>, Ns} | Attrs].
|
||||
|
||||
decode(<<5:8, Rest/binary>>, PNs, J1, J2) ->
|
||||
Ns = <<"eu.siacs.conversations.axolotl">>,
|
||||
{Attrs, Rest2} = prefix_map(fun
|
||||
(<<3:8, Rest3/binary>>) ->
|
||||
{{<<"prekey">>, <<"true">>}, Rest3};
|
||||
(<<4:8, Rest3/binary>>) ->
|
||||
{AVal, Rest4} = decode_string(Rest3),
|
||||
{{<<"prekey">>, AVal}, Rest4};
|
||||
(<<5:8, Rest3/binary>>) ->
|
||||
{AVal, Rest4} = decode_string(Rest3),
|
||||
{{<<"rid">>, AVal}, Rest4};
|
||||
(<<2:8, Rest3/binary>>) ->
|
||||
{stop, Rest3};
|
||||
(Data) ->
|
||||
decode_attr(Data)
|
||||
end, Rest),
|
||||
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
|
||||
{{xmlel, <<"key">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
|
||||
decode(<<12:8, Rest/binary>>, PNs, J1, J2) ->
|
||||
Ns = <<"eu.siacs.conversations.axolotl">>,
|
||||
{Attrs, Rest2} = decode_attrs(Rest),
|
||||
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
|
||||
{{xmlel, <<"encrypted">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
|
||||
decode(<<13:8, Rest/binary>>, PNs, J1, J2) ->
|
||||
Ns = <<"eu.siacs.conversations.axolotl">>,
|
||||
{Attrs, Rest2} = prefix_map(fun
|
||||
(<<3:8, Rest3/binary>>) ->
|
||||
{AVal, Rest4} = decode_string(Rest3),
|
||||
{{<<"sid">>, AVal}, Rest4};
|
||||
(<<2:8, Rest3/binary>>) ->
|
||||
{stop, Rest3};
|
||||
(Data) ->
|
||||
decode_attr(Data)
|
||||
end, Rest),
|
||||
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
|
||||
{{xmlel, <<"header">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
|
||||
decode(<<14:8, Rest/binary>>, PNs, J1, J2) ->
|
||||
Ns = <<"eu.siacs.conversations.axolotl">>,
|
||||
{Attrs, Rest2} = decode_attrs(Rest),
|
||||
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
|
||||
{{xmlel, <<"iv">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
|
||||
decode(<<15:8, Rest/binary>>, PNs, J1, J2) ->
|
||||
Ns = <<"eu.siacs.conversations.axolotl">>,
|
||||
{Attrs, Rest2} = decode_attrs(Rest),
|
||||
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
|
||||
{{xmlel, <<"payload">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
|
||||
decode(<<6:8, Rest/binary>>, PNs, J1, J2) ->
|
||||
Ns = <<"jabber:client">>,
|
||||
{Attrs, Rest2} = prefix_map(fun
|
||||
(<<3:8, Rest3/binary>>) ->
|
||||
{{<<"from">>, J2}, Rest3};
|
||||
(<<4:8, Rest3/binary>>) ->
|
||||
{AVal, Rest4} = decode_string(Rest3),
|
||||
{{<<"from">>, <<J1/binary, AVal/binary>>}, Rest4};
|
||||
(<<5:8, Rest3/binary>>) ->
|
||||
{AVal, Rest4} = decode_string(Rest3),
|
||||
{{<<"from">>, AVal}, Rest4};
|
||||
(<<6:8, Rest3/binary>>) ->
|
||||
{AVal, Rest4} = decode_string(Rest3),
|
||||
{{<<"id">>, AVal}, Rest4};
|
||||
(<<7:8, Rest3/binary>>) ->
|
||||
{{<<"to">>, J1}, Rest3};
|
||||
(<<8:8, Rest3/binary>>) ->
|
||||
{{<<"to">>, J2}, Rest3};
|
||||
(<<9:8, Rest3/binary>>) ->
|
||||
{AVal, Rest4} = decode_string(Rest3),
|
||||
{{<<"to">>, <<J1/binary, AVal/binary>>}, Rest4};
|
||||
(<<10:8, Rest3/binary>>) ->
|
||||
{AVal, Rest4} = decode_string(Rest3),
|
||||
{{<<"to">>, AVal}, Rest4};
|
||||
(<<11:8, Rest3/binary>>) ->
|
||||
{{<<"type">>, <<"chat">>}, Rest3};
|
||||
(<<12:8, Rest3/binary>>) ->
|
||||
{{<<"type">>, <<"groupchat">>}, Rest3};
|
||||
(<<13:8, Rest3/binary>>) ->
|
||||
{{<<"type">>, <<"normal">>}, Rest3};
|
||||
(<<14:8, Rest3/binary>>) ->
|
||||
{AVal, Rest4} = decode_string(Rest3),
|
||||
{{<<"type">>, AVal}, Rest4};
|
||||
(<<15:8, Rest3/binary>>) ->
|
||||
{{<<"xml:lang">>, <<"en">>}, Rest3};
|
||||
(<<16:8, Rest3/binary>>) ->
|
||||
{AVal, Rest4} = decode_string(Rest3),
|
||||
{{<<"xml:lang">>, AVal}, Rest4};
|
||||
(<<2:8, Rest3/binary>>) ->
|
||||
{stop, Rest3};
|
||||
(Data) ->
|
||||
decode_attr(Data)
|
||||
end, Rest),
|
||||
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
|
||||
{{xmlel, <<"message">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
|
||||
decode(<<8:8, Rest/binary>>, PNs, J1, J2) ->
|
||||
Ns = <<"jabber:client">>,
|
||||
{Attrs, Rest2} = decode_attrs(Rest),
|
||||
{Children, Rest6} = prefix_map(fun (<<9:8, Rest5/binary>>) ->
|
||||
{{xmlcdata, <<73,32,115,101,110,116,32,121,111,117,32,97,110,32,79,77,69,
|
||||
77,79,32,101,110,99,114,121,112,116,101,100,32,109,101,115,
|
||||
115,97,103,101,32,98,117,116,32,121,111,117,114,32,99,108,
|
||||
105,101,110,116,32,100,111,101,115,110,226,128,153,116,32,
|
||||
115,101,101,109,32,116,111,32,115,117,112,112,111,114,116,
|
||||
32,116,104,97,116,46,32,70,105,110,100,32,109,111,114,101,
|
||||
32,105,110,102,111,114,109,97,116,105,111,110,32,111,110,
|
||||
32,104,116,116,112,115,58,47,47,99,111,110,118,101,114,115,
|
||||
97,116,105,111,110,115,46,105,109,47,111,109,101,109,111>>}, Rest5};
|
||||
(Other) ->
|
||||
decode_child(Other, Ns, J1, J2)
|
||||
end, Rest2),
|
||||
{{xmlel, <<"body">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
|
||||
decode(<<31:8, Rest/binary>>, PNs, J1, J2) ->
|
||||
Ns = <<"jabber:client">>,
|
||||
{Attrs, Rest2} = decode_attrs(Rest),
|
||||
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
|
||||
{{xmlel, <<"subject">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
|
||||
decode(<<32:8, Rest/binary>>, PNs, J1, J2) ->
|
||||
Ns = <<"jabber:client">>,
|
||||
{Attrs, Rest2} = decode_attrs(Rest),
|
||||
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
|
||||
{{xmlel, <<"thread">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
|
||||
decode(<<7:8, Rest/binary>>, PNs, J1, J2) ->
|
||||
Ns = <<"urn:xmpp:hints">>,
|
||||
{Attrs, Rest2} = decode_attrs(Rest),
|
||||
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
|
||||
{{xmlel, <<"store">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
|
||||
decode(<<10:8, Rest/binary>>, PNs, J1, J2) ->
|
||||
Ns = <<"urn:xmpp:sid:0">>,
|
||||
{Attrs, Rest2} = prefix_map(fun
|
||||
(<<3:8, Rest3/binary>>) ->
|
||||
{AVal, Rest4} = decode_string(Rest3),
|
||||
{{<<"id">>, AVal}, Rest4};
|
||||
(<<2:8, Rest3/binary>>) ->
|
||||
{stop, Rest3};
|
||||
(Data) ->
|
||||
decode_attr(Data)
|
||||
end, Rest),
|
||||
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
|
||||
{{xmlel, <<"origin-id">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
|
||||
decode(<<22:8, Rest/binary>>, PNs, J1, J2) ->
|
||||
Ns = <<"urn:xmpp:sid:0">>,
|
||||
{Attrs, Rest2} = prefix_map(fun
|
||||
(<<3:8, Rest3/binary>>) ->
|
||||
{AVal, Rest4} = decode_string(Rest3),
|
||||
{{<<"by">>, AVal}, Rest4};
|
||||
(<<4:8, Rest3/binary>>) ->
|
||||
{AVal, Rest4} = decode_string(Rest3),
|
||||
{{<<"id">>, AVal}, Rest4};
|
||||
(<<2:8, Rest3/binary>>) ->
|
||||
{stop, Rest3};
|
||||
(Data) ->
|
||||
decode_attr(Data)
|
||||
end, Rest),
|
||||
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
|
||||
{{xmlel, <<"stanza-id">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
|
||||
decode(<<11:8, Rest/binary>>, PNs, J1, J2) ->
|
||||
Ns = <<"urn:xmpp:chat-markers:0">>,
|
||||
{Attrs, Rest2} = decode_attrs(Rest),
|
||||
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
|
||||
{{xmlel, <<"markable">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
|
||||
decode(<<20:8, Rest/binary>>, PNs, J1, J2) ->
|
||||
Ns = <<"urn:xmpp:chat-markers:0">>,
|
||||
{Attrs, Rest2} = prefix_map(fun
|
||||
(<<3:8, Rest3/binary>>) ->
|
||||
{AVal, Rest4} = decode_string(Rest3),
|
||||
{{<<"id">>, AVal}, Rest4};
|
||||
(<<4:8, Rest3/binary>>) ->
|
||||
{AVal, Rest4} = decode_string(Rest3),
|
||||
{{<<"sender">>, <<J1/binary, AVal/binary>>}, Rest4};
|
||||
(<<5:8, Rest3/binary>>) ->
|
||||
{AVal, Rest4} = decode_string(Rest3),
|
||||
{{<<"sender">>, <<J2/binary, AVal/binary>>}, Rest4};
|
||||
(<<6:8, Rest3/binary>>) ->
|
||||
{AVal, Rest4} = decode_string(Rest3),
|
||||
{{<<"sender">>, AVal}, Rest4};
|
||||
(<<2:8, Rest3/binary>>) ->
|
||||
{stop, Rest3};
|
||||
(Data) ->
|
||||
decode_attr(Data)
|
||||
end, Rest),
|
||||
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
|
||||
{{xmlel, <<"displayed">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
|
||||
decode(<<24:8, Rest/binary>>, PNs, J1, J2) ->
|
||||
Ns = <<"urn:xmpp:chat-markers:0">>,
|
||||
{Attrs, Rest2} = prefix_map(fun
|
||||
(<<3:8, Rest3/binary>>) ->
|
||||
{AVal, Rest4} = decode_string(Rest3),
|
||||
{{<<"id">>, AVal}, Rest4};
|
||||
(<<2:8, Rest3/binary>>) ->
|
||||
{stop, Rest3};
|
||||
(Data) ->
|
||||
decode_attr(Data)
|
||||
end, Rest),
|
||||
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
|
||||
{{xmlel, <<"received">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
|
||||
decode(<<16:8, Rest/binary>>, PNs, J1, J2) ->
|
||||
Ns = <<"urn:xmpp:eme:0">>,
|
||||
{Attrs, Rest2} = prefix_map(fun
|
||||
(<<3:8, Rest3/binary>>) ->
|
||||
{{<<"name">>, <<"OMEMO">>}, Rest3};
|
||||
(<<4:8, Rest3/binary>>) ->
|
||||
{AVal, Rest4} = decode_string(Rest3),
|
||||
{{<<"name">>, AVal}, Rest4};
|
||||
(<<5:8, Rest3/binary>>) ->
|
||||
{{<<"namespace">>, <<"eu.siacs.conversations.axolotl">>}, Rest3};
|
||||
(<<6:8, Rest3/binary>>) ->
|
||||
{AVal, Rest4} = decode_string(Rest3),
|
||||
{{<<"namespace">>, AVal}, Rest4};
|
||||
(<<2:8, Rest3/binary>>) ->
|
||||
{stop, Rest3};
|
||||
(Data) ->
|
||||
decode_attr(Data)
|
||||
end, Rest),
|
||||
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
|
||||
{{xmlel, <<"encryption">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
|
||||
decode(<<17:8, Rest/binary>>, PNs, J1, J2) ->
|
||||
Ns = <<"urn:xmpp:delay">>,
|
||||
{Attrs, Rest2} = prefix_map(fun
|
||||
(<<3:8, Rest3/binary>>) ->
|
||||
{{<<"from">>, J1}, Rest3};
|
||||
(<<4:8, Rest3/binary>>) ->
|
||||
{AVal, Rest4} = decode_string(Rest3),
|
||||
{{<<"from">>, AVal}, Rest4};
|
||||
(<<5:8, Rest3/binary>>) ->
|
||||
{AVal, Rest4} = decode_string(Rest3),
|
||||
{{<<"stamp">>, AVal}, Rest4};
|
||||
(<<2:8, Rest3/binary>>) ->
|
||||
{stop, Rest3};
|
||||
(Data) ->
|
||||
decode_attr(Data)
|
||||
end, Rest),
|
||||
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
|
||||
{{xmlel, <<"delay">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
|
||||
decode(<<18:8, Rest/binary>>, PNs, J1, J2) ->
|
||||
Ns = <<"http://jabber.org/protocol/address">>,
|
||||
{Attrs, Rest2} = prefix_map(fun
|
||||
(<<3:8, Rest3/binary>>) ->
|
||||
{AVal, Rest4} = decode_string(Rest3),
|
||||
{{<<"jid">>, <<J1/binary, AVal/binary>>}, Rest4};
|
||||
(<<4:8, Rest3/binary>>) ->
|
||||
{AVal, Rest4} = decode_string(Rest3),
|
||||
{{<<"jid">>, AVal}, Rest4};
|
||||
(<<5:8, Rest3/binary>>) ->
|
||||
{{<<"type">>, <<"ofrom">>}, Rest3};
|
||||
(<<6:8, Rest3/binary>>) ->
|
||||
{AVal, Rest4} = decode_string(Rest3),
|
||||
{{<<"type">>, AVal}, Rest4};
|
||||
(<<2:8, Rest3/binary>>) ->
|
||||
{stop, Rest3};
|
||||
(Data) ->
|
||||
decode_attr(Data)
|
||||
end, Rest),
|
||||
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
|
||||
{{xmlel, <<"address">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
|
||||
decode(<<19:8, Rest/binary>>, PNs, J1, J2) ->
|
||||
Ns = <<"http://jabber.org/protocol/address">>,
|
||||
{Attrs, Rest2} = decode_attrs(Rest),
|
||||
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
|
||||
{{xmlel, <<"addresses">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
|
||||
decode(<<21:8, Rest/binary>>, PNs, J1, J2) ->
|
||||
Ns = <<"urn:xmpp:mam:tmp">>,
|
||||
{Attrs, Rest2} = prefix_map(fun
|
||||
(<<3:8, Rest3/binary>>) ->
|
||||
{AVal, Rest4} = decode_string(Rest3),
|
||||
{{<<"by">>, AVal}, Rest4};
|
||||
(<<4:8, Rest3/binary>>) ->
|
||||
{AVal, Rest4} = decode_string(Rest3),
|
||||
{{<<"id">>, AVal}, Rest4};
|
||||
(<<2:8, Rest3/binary>>) ->
|
||||
{stop, Rest3};
|
||||
(Data) ->
|
||||
decode_attr(Data)
|
||||
end, Rest),
|
||||
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
|
||||
{{xmlel, <<"archived">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
|
||||
decode(<<23:8, Rest/binary>>, PNs, J1, J2) ->
|
||||
Ns = <<"urn:xmpp:receipts">>,
|
||||
{Attrs, Rest2} = decode_attrs(Rest),
|
||||
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
|
||||
{{xmlel, <<"request">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
|
||||
decode(<<25:8, Rest/binary>>, PNs, J1, J2) ->
|
||||
Ns = <<"urn:xmpp:receipts">>,
|
||||
{Attrs, Rest2} = prefix_map(fun
|
||||
(<<3:8, Rest3/binary>>) ->
|
||||
{AVal, Rest4} = decode_string(Rest3),
|
||||
{{<<"id">>, AVal}, Rest4};
|
||||
(<<2:8, Rest3/binary>>) ->
|
||||
{stop, Rest3};
|
||||
(Data) ->
|
||||
decode_attr(Data)
|
||||
end, Rest),
|
||||
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
|
||||
{{xmlel, <<"received">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
|
||||
decode(<<26:8, Rest/binary>>, PNs, J1, J2) ->
|
||||
Ns = <<"http://jabber.org/protocol/chatstates">>,
|
||||
{Attrs, Rest2} = decode_attrs(Rest),
|
||||
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
|
||||
{{xmlel, <<"active">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
|
||||
decode(<<39:8, Rest/binary>>, PNs, J1, J2) ->
|
||||
Ns = <<"http://jabber.org/protocol/chatstates">>,
|
||||
{Attrs, Rest2} = decode_attrs(Rest),
|
||||
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
|
||||
{{xmlel, <<"composing">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
|
||||
decode(<<27:8, Rest/binary>>, PNs, J1, J2) ->
|
||||
Ns = <<"http://jabber.org/protocol/muc#user">>,
|
||||
{Attrs, Rest2} = prefix_map(fun
|
||||
(<<3:8, Rest3/binary>>) ->
|
||||
{AVal, Rest4} = decode_string(Rest3),
|
||||
{{<<"from">>, <<J1/binary, AVal/binary>>}, Rest4};
|
||||
(<<4:8, Rest3/binary>>) ->
|
||||
{AVal, Rest4} = decode_string(Rest3),
|
||||
{{<<"from">>, AVal}, Rest4};
|
||||
(<<2:8, Rest3/binary>>) ->
|
||||
{stop, Rest3};
|
||||
(Data) ->
|
||||
decode_attr(Data)
|
||||
end, Rest),
|
||||
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
|
||||
{{xmlel, <<"invite">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
|
||||
decode(<<28:8, Rest/binary>>, PNs, J1, J2) ->
|
||||
Ns = <<"http://jabber.org/protocol/muc#user">>,
|
||||
{Attrs, Rest2} = decode_attrs(Rest),
|
||||
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
|
||||
{{xmlel, <<"reason">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
|
||||
decode(<<29:8, Rest/binary>>, PNs, J1, J2) ->
|
||||
Ns = <<"http://jabber.org/protocol/muc#user">>,
|
||||
{Attrs, Rest2} = decode_attrs(Rest),
|
||||
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
|
||||
{{xmlel, <<"x">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
|
||||
decode(<<30:8, Rest/binary>>, PNs, J1, J2) ->
|
||||
Ns = <<"jabber:x:conference">>,
|
||||
{Attrs, Rest2} = prefix_map(fun
|
||||
(<<3:8, Rest3/binary>>) ->
|
||||
{{<<"jid">>, J2}, Rest3};
|
||||
(<<4:8, Rest3/binary>>) ->
|
||||
{AVal, Rest4} = decode_string(Rest3),
|
||||
{{<<"jid">>, AVal}, Rest4};
|
||||
(<<2:8, Rest3/binary>>) ->
|
||||
{stop, Rest3};
|
||||
(Data) ->
|
||||
decode_attr(Data)
|
||||
end, Rest),
|
||||
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
|
||||
{{xmlel, <<"x">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
|
||||
decode(<<33:8, Rest/binary>>, PNs, J1, J2) ->
|
||||
Ns = <<"http://jabber.org/protocol/pubsub#event">>,
|
||||
{Attrs, Rest2} = decode_attrs(Rest),
|
||||
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
|
||||
{{xmlel, <<"event">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
|
||||
decode(<<34:8, Rest/binary>>, PNs, J1, J2) ->
|
||||
Ns = <<"http://jabber.org/protocol/pubsub#event">>,
|
||||
{Attrs, Rest2} = prefix_map(fun
|
||||
(<<3:8, Rest3/binary>>) ->
|
||||
{AVal, Rest4} = decode_string(Rest3),
|
||||
{{<<"id">>, AVal}, Rest4};
|
||||
(<<2:8, Rest3/binary>>) ->
|
||||
{stop, Rest3};
|
||||
(Data) ->
|
||||
decode_attr(Data)
|
||||
end, Rest),
|
||||
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
|
||||
{{xmlel, <<"item">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
|
||||
decode(<<35:8, Rest/binary>>, PNs, J1, J2) ->
|
||||
Ns = <<"http://jabber.org/protocol/pubsub#event">>,
|
||||
{Attrs, Rest2} = prefix_map(fun
|
||||
(<<3:8, Rest3/binary>>) ->
|
||||
{{<<"node">>, <<"urn:xmpp:mucsub:nodes:messages">>}, Rest3};
|
||||
(<<4:8, Rest3/binary>>) ->
|
||||
{AVal, Rest4} = decode_string(Rest3),
|
||||
{{<<"node">>, AVal}, Rest4};
|
||||
(<<2:8, Rest3/binary>>) ->
|
||||
{stop, Rest3};
|
||||
(Data) ->
|
||||
decode_attr(Data)
|
||||
end, Rest),
|
||||
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
|
||||
{{xmlel, <<"items">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
|
||||
decode(<<36:8, Rest/binary>>, PNs, J1, J2) ->
|
||||
Ns = <<"p1:push:custom">>,
|
||||
{Attrs, Rest2} = prefix_map(fun
|
||||
(<<3:8, Rest3/binary>>) ->
|
||||
{AVal, Rest4} = decode_string(Rest3),
|
||||
{{<<"key">>, AVal}, Rest4};
|
||||
(<<4:8, Rest3/binary>>) ->
|
||||
{AVal, Rest4} = decode_string(Rest3),
|
||||
{{<<"value">>, AVal}, Rest4};
|
||||
(<<2:8, Rest3/binary>>) ->
|
||||
{stop, Rest3};
|
||||
(Data) ->
|
||||
decode_attr(Data)
|
||||
end, Rest),
|
||||
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
|
||||
{{xmlel, <<"x">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
|
||||
decode(<<37:8, Rest/binary>>, PNs, J1, J2) ->
|
||||
Ns = <<"p1:pushed">>,
|
||||
{Attrs, Rest2} = decode_attrs(Rest),
|
||||
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
|
||||
{{xmlel, <<"x">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
|
||||
decode(<<38:8, Rest/binary>>, PNs, J1, J2) ->
|
||||
Ns = <<"urn:xmpp:message-correct:0">>,
|
||||
{Attrs, Rest2} = prefix_map(fun
|
||||
(<<3:8, Rest3/binary>>) ->
|
||||
{AVal, Rest4} = decode_string(Rest3),
|
||||
{{<<"id">>, AVal}, Rest4};
|
||||
(<<2:8, Rest3/binary>>) ->
|
||||
{stop, Rest3};
|
||||
(Data) ->
|
||||
decode_attr(Data)
|
||||
end, Rest),
|
||||
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
|
||||
{{xmlel, <<"replace">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
|
||||
decode(Other, PNs, J1, J2) ->
|
||||
decode_child(Other, PNs, J1, J2).
|
||||
|
417
tools/xml_compress_gen.erl
Normal file
417
tools/xml_compress_gen.erl
Normal file
@ -0,0 +1,417 @@
|
||||
%% File : xml_compress_gen.erl
|
||||
%% Author : Pawel Chmielowski
|
||||
%% Purpose :
|
||||
%% Created : 14 Sep 2018 Pawel Chmielowski
|
||||
%%
|
||||
%%
|
||||
%% ejabberd, Copyright (C) 2002-2018 ProcessOne
|
||||
%%
|
||||
%% 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 2 of the
|
||||
%% License, 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.,
|
||||
%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%
|
||||
|
||||
-module(xml_compress_gen).
|
||||
-author("pawel@process-one.net").
|
||||
|
||||
-include("xmpp.hrl").
|
||||
|
||||
%% API
|
||||
-export([archive_analyze/3, process_stats/1, gen_code/3]).
|
||||
|
||||
-record(el_stats, {count = 0, empty_count = 0, only_text_count = 0, attrs = #{}, text_stats = #{}}).
|
||||
-record(attr_stats, {count = 0, vals = #{}}).
|
||||
|
||||
archive_analyze(Host, Table, EHost) ->
|
||||
case ejabberd_sql:sql_query(Host, <<"select username, peer, kind, xml from ", Table/binary>>) of
|
||||
{selected, _, Res} ->
|
||||
lists:foldl(
|
||||
fun([U, P, K, X], Stats) ->
|
||||
M = case K of
|
||||
<<"groupchat">> ->
|
||||
U;
|
||||
_ ->
|
||||
<<U/binary, "@", EHost/binary>>
|
||||
end,
|
||||
El = fxml_stream:parse_element(X),
|
||||
analyze_element({El, <<"stream">>, <<"jabber:client">>, M, P}, Stats)
|
||||
end, {0, #{}}, Res);
|
||||
_ ->
|
||||
none
|
||||
end.
|
||||
|
||||
encode_id(Num) when Num < 64 ->
|
||||
iolist_to_binary(io_lib:format("~p:8", [Num])).
|
||||
|
||||
gen_code(_File, _Rules, $<) ->
|
||||
{error, <<"Invalid version">>};
|
||||
gen_code(File, Rules, Ver) when Ver < 64 ->
|
||||
{Data, _} = lists:foldl(
|
||||
fun({Ns, El, Attrs, Text}, {Acc, Id}) ->
|
||||
NsC = case lists:keyfind(Ns, 1, Acc) of
|
||||
false -> [];
|
||||
{_, L} -> L
|
||||
end,
|
||||
{AttrsE, _} = lists:mapfoldl(
|
||||
fun({AName, AVals}, Id2) ->
|
||||
{AD, Id3} = lists:mapfoldl(
|
||||
fun(AVal, Id3) ->
|
||||
{{AVal, encode_id(Id3)}, Id3 + 1}
|
||||
end, Id2, AVals),
|
||||
{{AName, AD ++ [encode_id(Id3)]}, Id3 + 1}
|
||||
end, 3, Attrs),
|
||||
{TextE, Id5} = lists:mapfoldl(
|
||||
fun(TextV, Id4) ->
|
||||
{{TextV, encode_id(Id4)}, Id4 + 1}
|
||||
end, Id + 1, Text),
|
||||
{lists:keystore(Ns, 1, Acc, {Ns, NsC ++ [{El, encode_id(Id), AttrsE, TextE}]}), Id5}
|
||||
end, {[], 5}, Rules),
|
||||
{ok, Dev} = file:open(File, write),
|
||||
Mod = filename:basename(File, ".erl"),
|
||||
io:format(Dev, "-module(~s).~n-export([encode/3, decode/3]).~n~n", [Mod]),
|
||||
RulesS = iolist_to_binary(io_lib:format("~p", [Rules])),
|
||||
RulesS2 = binary:replace(RulesS, <<"\n">>, <<"\n% ">>, [global]),
|
||||
io:format(Dev, "% This file was generated by xml_compress_gen~n%~n"
|
||||
"% Rules used:~n%~n% ~s~n~n", [RulesS2]),
|
||||
VerId = iolist_to_binary(io_lib:format("~p:8", [Ver])),
|
||||
gen_encode(Dev, Data, VerId),
|
||||
gen_decode(Dev, Data, VerId),
|
||||
file:close(Dev),
|
||||
Data.
|
||||
|
||||
gen_decode(Dev, Data, VerId) ->
|
||||
io:format(Dev, "decode(<<$<, _/binary>> = Data, _J1, _J2) ->~n"
|
||||
" fxml_stream:parse_element(Data);~n"
|
||||
"decode(<<~s, Rest/binary>>, J1, J2) ->~n"
|
||||
" {El, _} = decode(Rest, <<\"jabber:client\">>, J1, J2),~n"
|
||||
" El.~n~n", [VerId]),
|
||||
io:format(Dev, "decode_string(Data) ->~n"
|
||||
" case Data of~n"
|
||||
" <<0:2, L:6, Str:L/binary, Rest/binary>> ->~n"
|
||||
" {Str, Rest};~n"
|
||||
" <<1:2, L1:6, 0:2, L2:6, Rest/binary>> ->~n"
|
||||
" L = L2*64 + L1,~n"
|
||||
" <<Str:L/binary, Rest2/binary>> = Rest,~n"
|
||||
" {Str, Rest2};~n"
|
||||
" <<1:2, L1:6, 1:2, L2:6, L3:8, Rest/binary>> ->~n"
|
||||
" L = (L3*64 + L2)*64 + L1,~n"
|
||||
" <<Str:L/binary, Rest2/binary>> = Rest,~n"
|
||||
" {Str, Rest2}~n"
|
||||
" end.~n~n", []),
|
||||
io:format(Dev, "decode_child(<<1:8, Rest/binary>>, _PNs, _J1, _J2) ->~n"
|
||||
" {Text, Rest2} = decode_string(Rest),~n"
|
||||
" {{xmlcdata, Text}, Rest2};~n", []),
|
||||
io:format(Dev, "decode_child(<<2:8, Rest/binary>>, PNs, J1, J2) ->~n"
|
||||
" {Name, Rest2} = decode_string(Rest),~n"
|
||||
" {Attrs, Rest3} = decode_attrs(Rest2),~n"
|
||||
" {Children, Rest4} = decode_children(Rest3, PNs, J1, J2),~n"
|
||||
" {{xmlel, Name, Attrs, Children}, Rest4};~n", []),
|
||||
io:format(Dev, "decode_child(<<3:8, Rest/binary>>, PNs, J1, J2) ->~n"
|
||||
" {Name, Rest2} = decode_string(Rest),~n"
|
||||
" {Ns, Rest3} = decode_string(Rest2),~n"
|
||||
" {Attrs, Rest4} = decode_attrs(Rest3),~n"
|
||||
" {Children, Rest5} = decode_children(Rest4, Ns, J1, J2),~n"
|
||||
" {{xmlel, Name, add_ns(PNs, Ns, Attrs), Children}, Rest5};~n", []),
|
||||
io:format(Dev, "decode_child(<<4:8, Rest/binary>>, _PNs, _J1, _J2) ->~n"
|
||||
" {stop, Rest};~n", []),
|
||||
io:format(Dev, "decode_child(Other, PNs, J1, J2) ->~n"
|
||||
" decode(Other, PNs, J1, J2).~n~n", []),
|
||||
io:format(Dev, "decode_children(Data, PNs, J1, J2) ->~n"
|
||||
" prefix_map(fun(Data2) -> decode(Data2, PNs, J1, J2) end, Data).~n~n", []),
|
||||
io:format(Dev, "decode_attr(<<1:8, Rest/binary>>) ->~n"
|
||||
" {Name, Rest2} = decode_string(Rest),~n"
|
||||
" {Val, Rest3} = decode_string(Rest2),~n"
|
||||
" {{Name, Val}, Rest3};~n", []),
|
||||
io:format(Dev, "decode_attr(<<2:8, Rest/binary>>) ->~n"
|
||||
" {stop, Rest}.~n~n", []),
|
||||
io:format(Dev, "decode_attrs(Data) ->~n"
|
||||
" prefix_map(fun decode_attr/1, Data).~n~n", []),
|
||||
io:format(Dev, "prefix_map(F, Data) ->~n"
|
||||
" prefix_map(F, Data, []).~n~n", []),
|
||||
io:format(Dev, "prefix_map(F, Data, Acc) ->~n"
|
||||
" case F(Data) of~n"
|
||||
" {stop, Rest} ->~n"
|
||||
" {lists:reverse(Acc), Rest};~n"
|
||||
" {Val, Rest} ->~n"
|
||||
" prefix_map(F, Rest, [Val | Acc])~n"
|
||||
" end.~n~n", []),
|
||||
io:format(Dev, "add_ns(Ns, Ns, Attrs) ->~n"
|
||||
" Attrs;~n"
|
||||
"add_ns(_, Ns, Attrs) ->~n"
|
||||
" [{<<\"xmlns\">>, Ns} | Attrs].~n~n", []),
|
||||
lists:foreach(
|
||||
fun({Ns, Els}) ->
|
||||
lists:foreach(
|
||||
fun({Name, Id, Attrs, Text}) ->
|
||||
io:format(Dev, "decode(<<~s, Rest/binary>>, PNs, J1, J2) ->~n"
|
||||
" Ns = ~p,~n", [Id, Ns]),
|
||||
case Attrs of
|
||||
[] ->
|
||||
io:format(Dev, " {Attrs, Rest2} = decode_attrs(Rest),~n", []);
|
||||
_ ->
|
||||
io:format(Dev, " {Attrs, Rest2} = prefix_map(fun~n", []),
|
||||
lists:foreach(
|
||||
fun({AName, AVals}) ->
|
||||
lists:foreach(
|
||||
fun({j1, AId}) ->
|
||||
io:format(Dev, " (<<~s, Rest3/binary>>) ->~n"
|
||||
" {{~p, J1}, Rest3};~n", [AId, AName]);
|
||||
({j2, AId}) ->
|
||||
io:format(Dev, " (<<~s, Rest3/binary>>) ->~n"
|
||||
" {{~p, J2}, Rest3};~n", [AId, AName]);
|
||||
({{j1}, AId}) ->
|
||||
io:format(Dev, " (<<~s, Rest3/binary>>) ->~n"
|
||||
" {AVal, Rest4} = decode_string(Rest3),~n"
|
||||
" {{~p, <<J1/binary, AVal/binary>>}, Rest4};~n",
|
||||
[AId, AName]);
|
||||
({{j2}, AId}) ->
|
||||
io:format(Dev, " (<<~s, Rest3/binary>>) ->~n"
|
||||
" {AVal, Rest4} = decode_string(Rest3),~n"
|
||||
" {{~p, <<J2/binary, AVal/binary>>}, Rest4};~n",
|
||||
[AId, AName]);
|
||||
({AVal, AId}) ->
|
||||
io:format(Dev, " (<<~s, Rest3/binary>>) ->~n"
|
||||
" {{~p, ~p}, Rest3};~n",
|
||||
[AId, AName, AVal]);
|
||||
(AId) ->
|
||||
io:format(Dev, " (<<~s, Rest3/binary>>) ->~n"
|
||||
" {AVal, Rest4} = decode_string(Rest3),~n"
|
||||
" {{~p, AVal}, Rest4};~n",
|
||||
[AId, AName])
|
||||
end, AVals)
|
||||
end, Attrs),
|
||||
io:format(Dev, " (<<2:8, Rest3/binary>>) ->~n"
|
||||
" {stop, Rest3};~n"
|
||||
" (Data) ->~n"
|
||||
" decode_attr(Data)~n"
|
||||
" end, Rest),~n", [])
|
||||
end,
|
||||
case Text of
|
||||
[] ->
|
||||
io:format(Dev, " {Children, Rest6} = decode_children(Rest2, Ns, J1, J2),~n", []);
|
||||
_ ->
|
||||
io:format(Dev, " {Children, Rest6} = prefix_map(fun", []),
|
||||
lists:foreach(
|
||||
fun({TextS, TId}) ->
|
||||
io:format(Dev, " (<<~s, Rest5/binary>>) ->~n"
|
||||
" {{xmlcdata, ~p}, Rest5};~n",
|
||||
[TId, TextS])
|
||||
end, Text),
|
||||
|
||||
io:format(Dev, " (Other) ->~n"
|
||||
" decode_child(Other, Ns, J1, J2)~n"
|
||||
" end, Rest2),~n", [])
|
||||
end,
|
||||
io:format(Dev, " {{xmlel, ~p, add_ns(PNs, Ns, Attrs), Children}, Rest6};~n", [Name])
|
||||
end, Els)
|
||||
end, Data),
|
||||
io:format(Dev, "decode(Other, PNs, J1, J2) ->~n"
|
||||
" decode_child(Other, PNs, J1, J2).~n~n", []).
|
||||
|
||||
|
||||
gen_encode(Dev, Data, VerId) ->
|
||||
io:format(Dev, "encode(El, J1, J2) ->~n"
|
||||
" encode_child(El, <<\"jabber:client\">>,~n"
|
||||
" J1, J2, byte_size(J1), byte_size(J2), <<~s>>).~n~n", [VerId]),
|
||||
io:format(Dev, "encode_attr({<<\"xmlns\">>, _}, Acc) ->~n"
|
||||
" Acc;~n"
|
||||
"encode_attr({N, V}, Acc) ->~n"
|
||||
" <<Acc/binary, 1:8, (encode_string(N))/binary,~n"
|
||||
" (encode_string(V))/binary>>.~n~n", []),
|
||||
io:format(Dev, "encode_attrs(Attrs, Acc) ->~n"
|
||||
" lists:foldl(fun encode_attr/2, Acc, Attrs).~n~n", []),
|
||||
io:format(Dev, "encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->~n"
|
||||
" E1 = if~n"
|
||||
" PNs == Ns -> encode_attrs(Attrs, <<Pfx/binary, 2:8, (encode_string(Name))/binary>>);~n"
|
||||
" true -> encode_attrs(Attrs, <<Pfx/binary, 3:8, "
|
||||
"(encode_string(Ns))/binary, (encode_string(Name))/binary>>)~n"
|
||||
" end,~n"
|
||||
" E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E1/binary, 2:8>>),~n"
|
||||
" <<E2/binary, 4:8>>.~n~n", []),
|
||||
io:format(Dev, "encode_child({xmlel, Name, Attrs, Children}, PNs, J1, J2, J1L, J2L, Pfx) ->~n"
|
||||
" case lists:keyfind(<<\"xmlns\">>, 1, Attrs) of~n"
|
||||
" false ->~n"
|
||||
" encode(PNs, PNs, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx);~n"
|
||||
" {_, Ns} ->~n"
|
||||
" encode(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx)~n"
|
||||
" end;~n"
|
||||
"encode_child({xmlcdata, Data}, _PNs, _J1, _J2, _J1L, _J2L, Pfx) ->~n"
|
||||
" <<Pfx/binary, 1:8, (encode_string(Data))/binary>>.~n~n", []),
|
||||
io:format(Dev, "encode_children(Children, PNs, J1, J2, J1L, J2L, Pfx) ->~n"
|
||||
" lists:foldl(~n"
|
||||
" fun(Child, Acc) ->~n"
|
||||
" encode_child(Child, PNs, J1, J2, J1L, J2L, Acc)~n"
|
||||
" end, Pfx, Children).~n~n", []),
|
||||
io:format(Dev, "encode_string(Data) ->~n"
|
||||
" <<V1:4, V2:6, V3:6>> = <<(byte_size(Data)):16/unsigned-big-integer>>,~n"
|
||||
" case {V1, V2, V3} of~n"
|
||||
" {0, 0, V3} ->~n"
|
||||
" <<V3:8, Data/binary>>;~n"
|
||||
" {0, V2, V3} ->~n"
|
||||
" <<(V3 bor 64):8, V2:8, Data/binary>>;~n"
|
||||
" _ ->~n"
|
||||
" <<(V3 bor 64):8, (V2 bor 64):8, V1:8, Data/binary>>~n"
|
||||
" end.~n~n", []),
|
||||
lists:foreach(
|
||||
fun({Ns, Els}) ->
|
||||
io:format(Dev, "encode(PNs, ~p = Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->~n"
|
||||
" case Name of~n", [Ns]),
|
||||
lists:foreach(
|
||||
fun({ElN, Id, Attrs, Text}) ->
|
||||
io:format(Dev, " ~p ->~n", [ElN]),
|
||||
case Attrs of
|
||||
[] ->
|
||||
io:format(Dev, " E = encode_attrs(Attrs, <<Pfx/binary, ~s>>),~n", [Id]);
|
||||
_ ->
|
||||
io:format(Dev, " E = lists:foldl(fun~n", []),
|
||||
lists:foreach(
|
||||
fun({AName, AVals}) ->
|
||||
case AVals of
|
||||
[AIdS] when is_binary(AIdS) ->
|
||||
io:format(Dev, " ({~p, AVal}, Acc) ->~n"
|
||||
" <<Acc/binary, ~s, (encode_string(AVal))/binary>>;~n",
|
||||
[AName, AIdS]);
|
||||
_ ->
|
||||
io:format(Dev, " ({~p, AVal}, Acc) ->~n"
|
||||
" case AVal of~n", [AName]),
|
||||
lists:foreach(
|
||||
fun({j1, AId}) ->
|
||||
io:format(Dev, " J1 -> <<Acc/binary, ~s>>;~n",
|
||||
[AId]);
|
||||
({j2, AId}) ->
|
||||
io:format(Dev, " J2 -> <<Acc/binary, ~s>>;~n",
|
||||
[AId]);
|
||||
({{j1}, AId}) ->
|
||||
io:format(Dev, " <<J1:J1L/binary, Rest/binary>> -> "
|
||||
"<<Acc/binary, ~s, (encode_string(Rest))/binary>>;~n",
|
||||
[AId]);
|
||||
({{j2}, AId}) ->
|
||||
io:format(Dev, " <<J2:J2L/binary, Rest/binary>> -> "
|
||||
"<<Acc/binary, ~s, (encode_string(Rest))/binary>>;~n",
|
||||
[AId]);
|
||||
({AVal, AId}) ->
|
||||
io:format(Dev, " ~p -> <<Acc/binary, ~s>>;~n",
|
||||
[AVal, AId]);
|
||||
(AId) ->
|
||||
io:format(Dev, " _ -> <<Acc/binary, ~s, "
|
||||
"(encode_string(AVal))/binary>>~n",
|
||||
[AId])
|
||||
end, AVals),
|
||||
io:format(Dev, " end;~n", [])
|
||||
end
|
||||
end, Attrs),
|
||||
io:format(Dev, " (Attr, Acc) -> encode_attr(Attr, Acc)~n", []),
|
||||
io:format(Dev, " end, <<Pfx/binary, ~s>>, Attrs),~n", [Id])
|
||||
end,
|
||||
case Text of
|
||||
[] ->
|
||||
io:format(Dev, " E2 = encode_children(Children, Ns, "
|
||||
"J1, J2, J1L, J2L, <<E/binary, 2:8>>),~n", []);
|
||||
_ ->
|
||||
io:format(Dev, " E2 = lists:foldl(fun~n", []),
|
||||
lists:foreach(
|
||||
fun({TextV, TId}) ->
|
||||
io:format(Dev, " ({xmlcdata, ~p}, Acc) -> <<Acc/binary, ~s>>;~n", [TextV, TId])
|
||||
end, Text),
|
||||
io:format(Dev, " (El, Acc) -> encode_child(El, Ns, J1, J2, J1L, J2L, Acc)~n", []),
|
||||
io:format(Dev, " end, <<E/binary, 2:8>>, Children),~n", [])
|
||||
end,
|
||||
io:format(Dev, " <<E2/binary, 4:8>>;~n", [])
|
||||
end, Els),
|
||||
io:format(Dev, " _ -> encode_el(PNs, Ns, Name, Attrs, Children, "
|
||||
"J1, J2, J1L, J2L, Pfx)~nend;~n", [])
|
||||
end, Data),
|
||||
io:format(Dev, "encode(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->~n"
|
||||
" encode_el(PNs, Ns, Name, Attrs, Children, "
|
||||
"J1, J2, J1L, J2L, Pfx).~n~n", []).
|
||||
|
||||
process_stats({_Counts, Stats}) ->
|
||||
SStats = lists:sort(
|
||||
fun({_, #el_stats{count = C1}}, {_, #el_stats{count = C2}}) ->
|
||||
C1 >= C2
|
||||
end, maps:to_list(Stats)),
|
||||
lists:map(
|
||||
fun({Name, #el_stats{count = C, attrs = A, text_stats = T}}) ->
|
||||
[Ns, El] = binary:split(Name, <<"<">>),
|
||||
Attrs = lists:filtermap(
|
||||
fun({AN, #attr_stats{count = AC, vals = AV}}) ->
|
||||
if
|
||||
AC*5 < C ->
|
||||
false;
|
||||
true ->
|
||||
AVC = AC div min(maps:size(AV)*2, 10),
|
||||
AVA = [N || {N, C2} <- maps:to_list(AV), C2 > AVC],
|
||||
{true, {AN, AVA}}
|
||||
end
|
||||
end, maps:to_list(A)),
|
||||
Text = [TE || {TE, TC} <- maps:to_list(T), TC > C/2],
|
||||
{Ns, El, Attrs, Text}
|
||||
end, SStats).
|
||||
|
||||
analyze_elements(Elements, Stats, PName, PNS, J1, J2) ->
|
||||
lists:foldl(fun analyze_element/2, Stats, lists:map(fun(V) -> {V, PName, PNS, J1, J2} end, Elements)).
|
||||
|
||||
maps_update(Key, F, InitVal, Map) ->
|
||||
case maps:is_key(Key, Map) of
|
||||
true ->
|
||||
maps:update_with(Key, F, Map);
|
||||
_ ->
|
||||
maps:put(Key, F(InitVal), Map)
|
||||
end.
|
||||
|
||||
analyze_element({{xmlcdata, Data}, PName, PNS, _J1, _J2}, {ElCount, Stats}) ->
|
||||
Stats2 = maps_update(<<PNS/binary, "<", PName/binary>>,
|
||||
fun(#el_stats{text_stats = TS} = E) ->
|
||||
TS2 = maps_update(Data, fun(C) -> C + 1 end, 0, TS),
|
||||
E#el_stats{text_stats = TS2}
|
||||
end, #el_stats{}, Stats),
|
||||
{ElCount, Stats2};
|
||||
analyze_element({#xmlel{name = Name, attrs = Attrs, children = Children}, _PName, PNS, J1, J2}, {ElCount, Stats}) ->
|
||||
XMLNS = case lists:keyfind(<<"xmlns">>, 1, Attrs) of
|
||||
{_, NS} ->
|
||||
NS;
|
||||
false ->
|
||||
PNS
|
||||
end,
|
||||
NStats = maps_update(<<XMLNS/binary, "<", Name/binary>>,
|
||||
fun(#el_stats{count = C, empty_count = EC, only_text_count = TC, attrs = A} = ES) ->
|
||||
A2 = lists:foldl(
|
||||
fun({<<"xmlns">>, _}, AMap) ->
|
||||
AMap;
|
||||
({AName, AVal}, AMap) ->
|
||||
J1S = size(J1),
|
||||
J2S = size(J2),
|
||||
AVal2 = case AVal of
|
||||
J1 ->
|
||||
j1;
|
||||
J2 ->
|
||||
j2;
|
||||
<<J1:J1S/binary, _Rest/binary>> ->
|
||||
{j1};
|
||||
<<J2:J2S/binary, _Rest/binary>> ->
|
||||
{j2};
|
||||
Other ->
|
||||
Other
|
||||
end,
|
||||
maps_update(AName, fun(#attr_stats{count = AC, vals = AV}) ->
|
||||
AV2 = maps_update(AVal2, fun(C2) -> C2 + 1 end, 0, AV),
|
||||
#attr_stats{count = AC + 1, vals = AV2}
|
||||
end, #attr_stats{}, AMap)
|
||||
end, A, Attrs),
|
||||
ES#el_stats{count = C + 1,
|
||||
empty_count = if Children == [] -> EC + 1; true ->
|
||||
EC end,
|
||||
only_text_count = case Children of [{xmlcdata, _}] -> TC + 1; _ -> TC end,
|
||||
attrs = A2}
|
||||
end, #el_stats{}, Stats),
|
||||
analyze_elements(Children, {ElCount + 1, NStats}, Name, XMLNS, J1, J2).
|
Loading…
Reference in New Issue
Block a user