aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ejabberd_local.erl12
-rw-r--r--src/flex_offline.erl2
-rw-r--r--src/mam_query.erl220
-rw-r--r--src/mod_carboncopy.erl4
-rw-r--r--src/mod_mam.erl197
-rw-r--r--src/mod_mam_mnesia.erl8
-rw-r--r--src/mod_mam_sql.erl18
-rw-r--r--src/mod_offline.erl3
-rw-r--r--src/mod_offline_riak.erl2
-rw-r--r--src/mod_offline_sql.erl3
-rw-r--r--src/mod_roster.erl2
-rw-r--r--src/muc_register.erl2
-rw-r--r--src/muc_request.erl2
-rw-r--r--src/muc_roomconfig.erl2
-rw-r--r--src/muc_roominfo.erl2
-rw-r--r--src/pubsub_get_pending.erl2
-rw-r--r--src/pubsub_node_config.erl2
-rw-r--r--src/pubsub_publish_options.erl2
-rw-r--r--src/pubsub_subscribe_authorization.erl2
-rw-r--r--src/pubsub_subscribe_options.erl2
-rw-r--r--src/xmpp_util.erl9
21 files changed, 360 insertions, 138 deletions
diff --git a/src/ejabberd_local.erl b/src/ejabberd_local.erl
index d7849396b..74d86945d 100644
--- a/src/ejabberd_local.erl
+++ b/src/ejabberd_local.erl
@@ -177,15 +177,19 @@ unregister_iq_handler(Host, XMLNS) ->
refresh_iq_handlers() ->
ejabberd_local ! refresh_iq_handlers.
--spec bounce_resource_packet(jid(), jid(), stanza()) -> ok.
-bounce_resource_packet(_From, _To, #presence{}) ->
+-spec bounce_resource_packet(jid(), jid(), stanza()) -> stop.
+bounce_resource_packet(_From, #jid{lresource = <<"">>}, #presence{}) ->
+ ok;
+bounce_resource_packet(_From, #jid{lresource = <<"">>},
+ #message{type = headline}) ->
ok;
bounce_resource_packet(From, To, Packet) ->
Lang = xmpp:get_lang(Packet),
Txt = <<"No available resource found">>,
Err = xmpp:make_error(Packet,
xmpp:err_item_not_found(Txt, Lang)),
- ejabberd_router:route(To, From, Err).
+ ejabberd_router:route(To, From, Err),
+ stop.
%%====================================================================
%% gen_server callbacks
@@ -283,7 +287,7 @@ do_route(From, To, Packet) ->
ejabberd_sm:route(From, To, Packet);
is_record(Packet, iq), To#jid.lresource == <<"">> ->
process_iq(From, To, Packet);
- Type == result; Type == error; Type == headline ->
+ Type == result; Type == error ->
ok;
true ->
ejabberd_hooks:run(local_send_to_resource_hook,
diff --git a/src/flex_offline.erl b/src/flex_offline.erl
index 090ab3ddf..acc57342e 100644
--- a/src/flex_offline.erl
+++ b/src/flex_offline.erl
@@ -12,7 +12,7 @@
-include("flex_offline.hrl").
--export_type([{property, 0}, {result, 0}, {form, 0}]).
+-export_type([property/0, result/0, form/0]).
dec_int(Val, Min, Max) ->
case list_to_integer(binary_to_list(Val)) of
diff --git a/src/mam_query.erl b/src/mam_query.erl
new file mode 100644
index 000000000..cb5bfe13a
--- /dev/null
+++ b/src/mam_query.erl
@@ -0,0 +1,220 @@
+%% Created automatically by xdata generator (xdata_codec.erl)
+%% Source: mam_query.xdata
+%% Form type: urn:xmpp:mam:1
+%% Document: XEP-0313
+
+-module(mam_query).
+
+-export([decode/1, decode/2, encode/1, encode/2,
+ format_error/1]).
+
+-include("xmpp_codec.hrl").
+
+-include("mam_query.hrl").
+
+-export_type([property/0, result/0, form/0]).
+
+enc_jid(J) -> jid:to_string(J).
+
+dec_jid(Val) ->
+ case jid:from_string(Val) of
+ error -> erlang:error(badarg);
+ J -> J
+ end.
+
+format_error({form_type_mismatch, Type}) ->
+ <<"FORM_TYPE doesn't match '", Type/binary, "'">>;
+format_error({bad_var_value, Var, Type}) ->
+ <<"Bad value of field '", Var/binary, "' of type '",
+ Type/binary, "'">>;
+format_error({missing_value, Var, Type}) ->
+ <<"Missing value of field '", Var/binary, "' of type '",
+ Type/binary, "'">>;
+format_error({too_many_values, Var, Type}) ->
+ <<"Too many values for field '", Var/binary,
+ "' of type '", Type/binary, "'">>;
+format_error({unknown_var, Var, Type}) ->
+ <<"Unknown field '", Var/binary, "' of type '",
+ Type/binary, "'">>;
+format_error({missing_required_var, Var, Type}) ->
+ <<"Missing required field '", Var/binary, "' of type '",
+ Type/binary, "'">>.
+
+decode(Fs) -> decode(Fs, []).
+
+decode(Fs, Acc) ->
+ case lists:keyfind(<<"FORM_TYPE">>, #xdata_field.var,
+ Fs)
+ of
+ false -> decode(Fs, Acc, []);
+ #xdata_field{values = [<<"urn:xmpp:mam:1">>]} ->
+ decode(Fs, Acc, []);
+ _ ->
+ erlang:error({?MODULE,
+ {form_type_mismatch, <<"urn:xmpp:mam:1">>}})
+ end.
+
+encode(Cfg) -> encode(Cfg, fun (Text) -> Text end).
+
+encode(List, Translate) when is_list(List) ->
+ Fs = [case Opt of
+ {with, Val} -> [encode_with(Val, Translate)];
+ {with, _, _} -> erlang:error({badarg, Opt});
+ {start, Val} -> [encode_start(Val, Translate)];
+ {start, _, _} -> erlang:error({badarg, Opt});
+ {'end', Val} -> [encode_end(Val, Translate)];
+ {'end', _, _} -> erlang:error({badarg, Opt});
+ {withtext, Val} -> [encode_withtext(Val, Translate)];
+ {withtext, _, _} -> erlang:error({badarg, Opt});
+ #xdata_field{} -> [Opt];
+ _ -> []
+ end
+ || Opt <- List],
+ FormType = #xdata_field{var = <<"FORM_TYPE">>,
+ type = hidden, values = [<<"urn:xmpp:mam:1">>]},
+ [FormType | lists:flatten(Fs)].
+
+decode([#xdata_field{var = <<"with">>, values = [Value]}
+ | Fs],
+ Acc, Required) ->
+ try dec_jid(Value) of
+ Result ->
+ decode(Fs, [{with, Result} | Acc],
+ lists:delete(<<"with">>, Required))
+ catch
+ _:_ ->
+ erlang:error({?MODULE,
+ {bad_var_value, <<"with">>, <<"urn:xmpp:mam:1">>}})
+ end;
+decode([#xdata_field{var = <<"with">>, values = []} = F
+ | Fs],
+ Acc, Required) ->
+ decode([F#xdata_field{var = <<"with">>, values = [<<>>]}
+ | Fs],
+ Acc, Required);
+decode([#xdata_field{var = <<"with">>} | _], _, _) ->
+ erlang:error({?MODULE,
+ {too_many_values, <<"with">>, <<"urn:xmpp:mam:1">>}});
+decode([#xdata_field{var = <<"start">>,
+ values = [Value]}
+ | Fs],
+ Acc, Required) ->
+ try xmpp_util:decode_timestamp(Value) of
+ Result ->
+ decode(Fs, [{start, Result} | Acc],
+ lists:delete(<<"start">>, Required))
+ catch
+ _:_ ->
+ erlang:error({?MODULE,
+ {bad_var_value, <<"start">>, <<"urn:xmpp:mam:1">>}})
+ end;
+decode([#xdata_field{var = <<"start">>, values = []} = F
+ | Fs],
+ Acc, Required) ->
+ decode([F#xdata_field{var = <<"start">>,
+ values = [<<>>]}
+ | Fs],
+ Acc, Required);
+decode([#xdata_field{var = <<"start">>} | _], _, _) ->
+ erlang:error({?MODULE,
+ {too_many_values, <<"start">>, <<"urn:xmpp:mam:1">>}});
+decode([#xdata_field{var = <<"end">>, values = [Value]}
+ | Fs],
+ Acc, Required) ->
+ try xmpp_util:decode_timestamp(Value) of
+ Result ->
+ decode(Fs, [{'end', Result} | Acc],
+ lists:delete(<<"end">>, Required))
+ catch
+ _:_ ->
+ erlang:error({?MODULE,
+ {bad_var_value, <<"end">>, <<"urn:xmpp:mam:1">>}})
+ end;
+decode([#xdata_field{var = <<"end">>, values = []} = F
+ | Fs],
+ Acc, Required) ->
+ decode([F#xdata_field{var = <<"end">>, values = [<<>>]}
+ | Fs],
+ Acc, Required);
+decode([#xdata_field{var = <<"end">>} | _], _, _) ->
+ erlang:error({?MODULE,
+ {too_many_values, <<"end">>, <<"urn:xmpp:mam:1">>}});
+decode([#xdata_field{var = <<"withtext">>,
+ values = [Value]}
+ | Fs],
+ Acc, Required) ->
+ try Value of
+ Result ->
+ decode(Fs, [{withtext, Result} | Acc],
+ lists:delete(<<"withtext">>, Required))
+ catch
+ _:_ ->
+ erlang:error({?MODULE,
+ {bad_var_value, <<"withtext">>, <<"urn:xmpp:mam:1">>}})
+ end;
+decode([#xdata_field{var = <<"withtext">>,
+ values = []} =
+ F
+ | Fs],
+ Acc, Required) ->
+ decode([F#xdata_field{var = <<"withtext">>,
+ values = [<<>>]}
+ | Fs],
+ Acc, Required);
+decode([#xdata_field{var = <<"withtext">>} | _], _,
+ _) ->
+ erlang:error({?MODULE,
+ {too_many_values, <<"withtext">>,
+ <<"urn:xmpp:mam:1">>}});
+decode([#xdata_field{var = Var} | Fs], Acc, Required) ->
+ if Var /= <<"FORM_TYPE">> ->
+ erlang:error({?MODULE,
+ {unknown_var, Var, <<"urn:xmpp:mam:1">>}});
+ true -> decode(Fs, Acc, Required)
+ end;
+decode([], _, [Var | _]) ->
+ erlang:error({?MODULE,
+ {missing_required_var, Var, <<"urn:xmpp:mam:1">>}});
+decode([], Acc, []) -> Acc.
+
+encode_with(Value, Translate) ->
+ Values = case Value of
+ undefined -> [];
+ Value -> [enc_jid(Value)]
+ end,
+ Opts = [],
+ #xdata_field{var = <<"with">>, values = Values,
+ required = false, type = 'jid-single', options = Opts,
+ desc = <<>>, label = Translate(<<"User JID">>)}.
+
+encode_start(Value, Translate) ->
+ Values = case Value of
+ undefined -> [];
+ Value -> [Value]
+ end,
+ Opts = [],
+ #xdata_field{var = <<"start">>, values = Values,
+ required = false, type = 'text-single', options = Opts,
+ desc = <<>>,
+ label = Translate(<<"Search from the date">>)}.
+
+encode_end(Value, Translate) ->
+ Values = case Value of
+ undefined -> [];
+ Value -> [Value]
+ end,
+ Opts = [],
+ #xdata_field{var = <<"end">>, values = Values,
+ required = false, type = 'text-single', options = Opts,
+ desc = <<>>,
+ label = Translate(<<"Search until the date">>)}.
+
+encode_withtext(Value, Translate) ->
+ Values = case Value of
+ <<>> -> [];
+ Value -> [Value]
+ end,
+ Opts = [],
+ #xdata_field{var = <<"withtext">>, values = Values,
+ required = false, type = 'text-single', options = Opts,
+ desc = <<>>, label = Translate(<<"Search the text">>)}.
diff --git a/src/mod_carboncopy.erl b/src/mod_carboncopy.erl
index 7d8ca5332..1c8ca1fdc 100644
--- a/src/mod_carboncopy.erl
+++ b/src/mod_carboncopy.erl
@@ -228,8 +228,8 @@ complete_packet(_From, Msg, _Direction) ->
-spec is_chat_message(stanza()) -> boolean().
is_chat_message(#message{type = chat}) ->
true;
-is_chat_message(#message{type = normal, body = Body}) ->
- xmpp:get_text(Body) /= <<"">>;
+is_chat_message(#message{type = normal, body = [_|_]}) ->
+ true;
is_chat_message(_) ->
false.
diff --git a/src/mod_mam.erl b/src/mod_mam.erl
index f9ef104bd..61754ae59 100644
--- a/src/mod_mam.erl
+++ b/src/mod_mam.erl
@@ -54,12 +54,13 @@
-callback delete_old_messages(binary() | global,
erlang:timestamp(),
all | chat | groupchat) -> any().
--callback extended_fields() -> [xdata_field()].
+-callback extended_fields() -> [mam_query:property() | #xdata_field{}].
-callback store(xmlel(), binary(), {binary(), binary()}, chat | groupchat,
jid(), binary(), recv | send) -> {ok, binary()} | any().
-callback write_prefs(binary(), binary(), #archive_prefs{}, binary()) -> ok | any().
-callback get_prefs(binary(), binary()) -> {ok, #archive_prefs{}} | error.
--callback select(binary(), jid(), jid(), mam_query(), chat | groupchat) ->
+-callback select(binary(), jid(), jid(), mam_query:result(),
+ #rsm_set{} | undefined, chat | groupchat) ->
{[{binary(), non_neg_integer(), xmlel()}], boolean(), non_neg_integer()}.
%%%===================================================================
@@ -259,8 +260,9 @@ muc_filter_message(Pkt, #state{config = Config} = MUCState,
end.
set_stanza_id(Pkt, JID, ID) ->
- Archived = #mam_archived{by = JID, id = ID},
- StanzaID = #stanza_id{by = JID, id = ID},
+ BareJID = jid:remove_resource(JID),
+ Archived = #mam_archived{by = BareJID, id = ID},
+ StanzaID = #stanza_id{by = BareJID, id = ID},
NewEls = [Archived, StanzaID|xmpp:get_els(Pkt)],
xmpp:set_els(Pkt, NewEls).
@@ -308,43 +310,24 @@ muc_process_iq(#iq{type = get,
muc_process_iq(IQ, _MUCState) ->
IQ.
-parse_query(#mam_query{xdata = #xdata{fields = Fs}} = Query, Lang) ->
- try
- lists:foldl(
- fun(#xdata_field{var = <<"start">>, values = [Data|_]}, Q) ->
- try xmpp_util:decode_timestamp(Data) of
- {_, _, _} = TS -> Q#mam_query{start = TS}
- catch _:{bad_timestamp, _} -> throw({error, <<"start">>})
- end;
- (#xdata_field{var = <<"end">>, values = [Data|_]}, Q) ->
- try xmpp_util:decode_timestamp(Data) of
- {_, _, _} = TS -> Q#mam_query{start = TS}
- catch _:{bad_timestamp, _} -> throw({error, <<"end">>})
- end;
- (#xdata_field{var = <<"with">>, values = [Data|_]}, Q) ->
- case jid:from_string(Data) of
- error -> throw({error, <<"with">>});
- J -> Q#mam_query{with = J}
- end;
- (#xdata_field{var = <<"withtext">>, values = [Data|_]}, Q) ->
- case Data of
- <<"">> -> throw({error, <<"withtext">>});
- _ -> Q#mam_query{withtext = Data}
- end;
- (#xdata_field{var = <<"FORM_TYPE">>, values = [NS|_]}, Q) ->
- case Query#mam_query.xmlns of
- NS -> Q;
- _ -> throw({error, <<"FORM_TYPE">>})
- end;
- (#xdata_field{}, Acc) ->
- Acc
- end, Query, Fs)
- catch throw:{error, Var} ->
- Txt = io_lib:format("Incorrect value of field '~s'", [Var]),
- {error, xmpp:err_bad_request(iolist_to_binary(Txt), Lang)}
+parse_query(#mam_query{xmlns = ?NS_MAM_TMP,
+ start = Start, 'end' = End,
+ with = With, withtext = Text}, _Lang) ->
+ {ok, [{start, Start}, {'end', End},
+ {with, With}, {withtext, Text}]};
+parse_query(#mam_query{xdata = #xdata{}} = Query, Lang) ->
+ X = xmpp_util:set_xdata_field(
+ #xdata_field{var = <<"FORM_TYPE">>,
+ type = hidden, values = [?NS_MAM_1]},
+ Query#mam_query.xdata),
+ try mam_query:decode(X#xdata.fields) of
+ Form -> {ok, Form}
+ catch _:{mam_query, Why} ->
+ Txt = mam_query:format_error(Why),
+ {error, xmpp:err_bad_request(Txt, Lang)}
end;
-parse_query(Query, _Lang) ->
- Query.
+parse_query(#mam_query{}, _Lang) ->
+ {ok, []}.
disco_sm_features(empty, From, To, Node, Lang) ->
disco_sm_features({result, []}, From, To, Node, Lang);
@@ -402,17 +385,16 @@ delete_old_messages(_TypeBin, _Days) ->
%%%===================================================================
process_iq(LServer, #iq{sub_els = [#mam_query{xmlns = NS}]} = IQ) ->
- CommonFields = [#xdata_field{type = hidden,
- var = <<"FORM_TYPE">>,
- values = [NS]},
- #xdata_field{type = 'jid-single', var = <<"with">>},
- #xdata_field{type = 'text-single', var = <<"start">>},
- #xdata_field{type = 'text-single', var = <<"end">>}],
Mod = gen_mod:db_mod(LServer, ?MODULE),
+ CommonFields = [{with, undefined},
+ {start, undefined},
+ {'end', undefined}],
ExtendedFields = Mod:extended_fields(),
- Fields = CommonFields ++ ExtendedFields,
- Form = #xdata{type = form, fields = Fields},
- xmpp:make_iq_result(IQ, #mam_query{xmlns = NS, xdata = Form}).
+ Fields = mam_query:encode(CommonFields ++ ExtendedFields),
+ X = xmpp_util:set_xdata_field(
+ #xdata_field{var = <<"FORM_TYPE">>, type = hidden, values = [NS]},
+ #xdata{type = form, fields = Fields}),
+ xmpp:make_iq_result(IQ, #mam_query{xmlns = NS, xdata = X}).
% Preference setting (both v0.2 & v0.3)
process_iq(#iq{type = set, lang = Lang,
@@ -457,15 +439,17 @@ process_iq(LServer, #iq{from = #jid{luser = LUser}, lang = Lang,
{groupchat, _Role, _MUCState} ->
ok
end,
- case parse_query(SubEl, Lang) of
+ case SubEl of
#mam_query{rsm = #rsm_set{index = I}} when is_integer(I) ->
xmpp:make_error(IQ, xmpp:err_feature_not_implemented());
- #mam_query{rsm = RSM, xmlns = NS} = Query ->
- NewRSM = limit_max(RSM, NS),
- NewQuery = Query#mam_query{rsm = NewRSM},
- select_and_send(LServer, NewQuery, IQ, MsgType);
- {error, Err} ->
- xmpp:make_error(IQ, Err)
+ #mam_query{rsm = RSM, xmlns = NS} ->
+ case parse_query(SubEl, Lang) of
+ {ok, Query} ->
+ NewRSM = limit_max(RSM, NS),
+ select_and_send(LServer, Query, NewRSM, IQ, MsgType);
+ {error, Err} ->
+ xmpp:make_error(IQ, Err)
+ end
end.
should_archive(#message{type = error}, _LServer) ->
@@ -493,57 +477,57 @@ should_archive(_, _LServer) ->
-spec strip_my_archived_tag(stanza(), binary()) -> stanza().
strip_my_archived_tag(Pkt, LServer) ->
- NewPkt = xmpp:decode_els(
- Pkt, ?NS_CLIENT,
+ Els = xmpp:get_els(Pkt),
+ NewEls = lists:filter(
fun(El) ->
- case xmpp:get_name(El) of
- <<"archived">> ->
- xmpp:get_ns(El) == ?NS_MAM_TMP;
- <<"stanza-id">> ->
- xmpp:get_ns(El) == ?NS_SID_0;
- _ ->
- false
+ Name = xmpp:get_name(El),
+ NS = xmpp:get_ns(El),
+ if (Name == <<"archived">> andalso NS == ?NS_MAM_TMP);
+ (Name == <<"stanza-id">> andalso NS == ?NS_SID_0) ->
+ try xmpp:decode(El) of
+ #mam_archived{by = By} ->
+ By#jid.lserver /= LServer;
+ #stanza_id{by = By} ->
+ By#jid.lserver /= LServer
+ catch _:{xmpp_codec, _} ->
+ false
+ end;
+ true ->
+ true
end
- end),
- NewEls = lists:filter(
- fun(#mam_archived{by = By}) ->
- By#jid.lserver /= LServer;
- (#stanza_id{by = By}) ->
- By#jid.lserver /= LServer;
- (_) ->
- true
- end, xmpp:get_els(NewPkt)),
- xmpp:set_els(NewPkt, NewEls).
+ end, Els),
+ xmpp:set_els(Pkt, NewEls).
+-spec strip_x_jid_tags(stanza()) -> stanza().
strip_x_jid_tags(Pkt) ->
- NewPkt = xmpp:decode_els(
- Pkt, ?NS_CLIENT,
+ Els = xmpp:get_els(Pkt),
+ NewEls = lists:filter(
fun(El) ->
case xmpp:get_name(El) of
<<"x">> ->
- case xmpp:get_ns(El) of
- ?NS_MUC_USER -> true;
- ?NS_MUC_ADMIN -> true;
- ?NS_MUC_OWNER -> true;
- _ -> false
- end;
+ NS = xmpp:get_ns(El),
+ Items = if NS == ?NS_MUC_USER;
+ NS == ?NS_MUC_ADMIN;
+ NS == ?NS_MUC_OWNER ->
+ try xmpp:decode(El) of
+ #muc_user{items = Is} -> Is;
+ #muc_admin{items = Is} -> Is;
+ #muc_owner{items = Is} -> Is
+ catch _:{xmpp_codec, _} ->
+ []
+ end;
+ true ->
+ []
+ end,
+ not lists:any(
+ fun(#muc_item{jid = JID}) ->
+ JID /= undefined
+ end, Items);
_ ->
- false
+ true
end
- end),
- NewEls = lists:filter(
- fun(El) ->
- Items = case El of
- #muc_user{items = Is} -> Is;
- #muc_admin{items = Is} -> Is;
- #muc_owner{items = Is} -> Is;
- _ -> []
- end,
- not lists:any(fun(#muc_item{jid = JID}) ->
- JID /= undefined
- end, Items)
- end, xmpp:get_els(NewPkt)),
- xmpp:set_els(NewPkt, NewEls).
+ end, Els),
+ xmpp:set_els(Pkt, NewEls).
should_archive_peer(C2SState,
#archive_prefs{default = Default,
@@ -625,7 +609,7 @@ has_no_store_hint(Message) ->
-spec is_resent(message(), binary()) -> boolean().
is_resent(Pkt, LServer) ->
case xmpp:get_subtag(Pkt, #stanza_id{}) of
- #stanza_id{by = #jid{luser = <<>>, lserver = LServer}} ->
+ #stanza_id{by = #jid{lserver = LServer}} ->
true;
_ ->
false
@@ -741,21 +725,22 @@ maybe_activate_mam(LUser, LServer) ->
ok
end.
-select_and_send(LServer, Query, #iq{from = From, to = To} = IQ, MsgType) ->
+select_and_send(LServer, Query, RSM, #iq{from = From, to = To} = IQ, MsgType) ->
{Msgs, IsComplete, Count} =
case MsgType of
chat ->
- select(LServer, From, From, Query, MsgType);
+ select(LServer, From, From, Query, RSM, MsgType);
{groupchat, _Role, _MUCState} ->
- select(LServer, From, To, Query, MsgType)
+ select(LServer, From, To, Query, RSM, MsgType)
end,
SortedMsgs = lists:keysort(2, Msgs),
send(SortedMsgs, Count, IsComplete, IQ).
-select(_LServer, JidRequestor, JidArchive,
- #mam_query{start = Start, 'end' = End, rsm = RSM},
+select(_LServer, JidRequestor, JidArchive, Query, RSM,
{groupchat, _Role, #state{config = #config{mam = false},
history = History}} = MsgType) ->
+ Start = proplists:get_value(start, Query),
+ End = proplists:get_value('end', Query),
#lqueue{len = L, queue = Q} = History,
Msgs =
lists:flatmap(
@@ -786,9 +771,9 @@ select(_LServer, JidRequestor, JidArchive,
_ ->
{Msgs, true, L}
end;
-select(LServer, JidRequestor, JidArchive, Query, MsgType) ->
+select(LServer, JidRequestor, JidArchive, Query, RSM, MsgType) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
- Mod:select(LServer, JidRequestor, JidArchive, Query, MsgType).
+ Mod:select(LServer, JidRequestor, JidArchive, Query, RSM, MsgType).
msg_to_el(#archive_msg{timestamp = TS, packet = Pkt1, nick = Nick, peer = Peer},
MsgType, JidRequestor, #jid{lserver = LServer} = JidArchive) ->
diff --git a/src/mod_mam_mnesia.erl b/src/mod_mam_mnesia.erl
index e913d5a45..8b9c6676c 100644
--- a/src/mod_mam_mnesia.erl
+++ b/src/mod_mam_mnesia.erl
@@ -12,7 +12,7 @@
%% API
-export([init/2, remove_user/2, remove_room/3, delete_old_messages/3,
- extended_fields/0, store/7, write_prefs/4, get_prefs/2, select/5]).
+ extended_fields/0, store/7, write_prefs/4, get_prefs/2, select/6]).
-include_lib("stdlib/include/ms_transform.hrl").
-include("xmpp.hrl").
@@ -132,8 +132,10 @@ get_prefs(LUser, LServer) ->
select(_LServer, JidRequestor,
#jid{luser = LUser, lserver = LServer} = JidArchive,
- #mam_query{start = Start, 'end' = End,
- with = With, rsm = RSM}, MsgType) ->
+ Query, RSM, MsgType) ->
+ Start = proplists:get_value(start, Query),
+ End = proplists:get_value('end', Query),
+ With = proplists:get_value(with, Query),
LWith = if With /= undefined -> jid:tolower(With);
true -> undefined
end,
diff --git a/src/mod_mam_sql.erl b/src/mod_mam_sql.erl
index 1491f70f2..c500745a3 100644
--- a/src/mod_mam_sql.erl
+++ b/src/mod_mam_sql.erl
@@ -14,7 +14,7 @@
%% API
-export([init/2, remove_user/2, remove_room/3, delete_old_messages/3,
- extended_fields/0, store/7, write_prefs/4, get_prefs/2, select/5]).
+ extended_fields/0, store/7, write_prefs/4, get_prefs/2, select/6]).
-include_lib("stdlib/include/ms_transform.hrl").
-include("xmpp.hrl").
@@ -51,7 +51,7 @@ delete_old_messages(ServerHost, TimeStamp, Type) ->
ok.
extended_fields() ->
- [#xdata_field{type = 'text-single', var = <<"withtext">>}].
+ [{withtext, <<"">>}].
store(Pkt, LServer, {LUser, LHost}, Type, Peer, Nick, _Dir) ->
TSinteger = p1_time_compat:system_time(micro_seconds),
@@ -124,12 +124,12 @@ get_prefs(LUser, LServer) ->
end.
select(LServer, JidRequestor, #jid{luser = LUser} = JidArchive,
- MAMQuery, MsgType) ->
+ MAMQuery, RSM, MsgType) ->
User = case MsgType of
chat -> LUser;
{groupchat, _Role, _MUCState} -> jid:to_string(JidArchive)
end,
- {Query, CountQuery} = make_sql_query(User, LServer, MAMQuery),
+ {Query, CountQuery} = make_sql_query(User, LServer, MAMQuery, RSM),
% TODO from XEP-0313 v0.2: "To conserve resources, a server MAY place a
% reasonable limit on how many stanzas may be pushed to a client in one
% request. If a query returns a number of stanzas greater than this limit
@@ -139,7 +139,7 @@ select(LServer, JidRequestor, #jid{luser = LUser} = JidArchive,
case {ejabberd_sql:sql_query(LServer, Query),
ejabberd_sql:sql_query(LServer, CountQuery)} of
{{selected, _, Res}, {selected, _, [[Count]]}} ->
- {Max, Direction, _} = get_max_direction_id(MAMQuery#mam_query.rsm),
+ {Max, Direction, _} = get_max_direction_id(RSM),
{Res1, IsComplete} =
if Max >= 0 andalso Max /= undefined andalso length(Res) > Max ->
if Direction == before ->
@@ -194,9 +194,11 @@ usec_to_now(Int) ->
Sec = Secs rem 1000000,
{MSec, Sec, USec}.
-make_sql_query(User, LServer,
- #mam_query{start = Start, 'end' = End, with = With,
- withtext = WithText, rsm = RSM}) ->
+make_sql_query(User, LServer, MAMQuery, RSM) ->
+ Start = proplists:get_value(start, MAMQuery),
+ End = proplists:get_value('end', MAMQuery),
+ With = proplists:get_value(with, MAMQuery),
+ WithText = proplists:get_value(withtext, MAMQuery),
{Max, Direction, ID} = get_max_direction_id(RSM),
ODBCType = ejabberd_config:get_option(
{sql_type, LServer},
diff --git a/src/mod_offline.erl b/src/mod_offline.erl
index 2f6d52c36..d007bf3c6 100644
--- a/src/mod_offline.erl
+++ b/src/mod_offline.erl
@@ -99,7 +99,8 @@
-callback remove_expired_messages(binary()) -> {atomic, any()}.
-callback remove_old_messages(non_neg_integer(), binary()) -> {atomic, any()}.
-callback remove_user(binary(), binary()) -> {atomic, any()}.
--callback read_message_headers(binary(), binary()) -> any().
+-callback read_message_headers(binary(), binary()) ->
+ [{non_neg_integer(), jid(), jid(), undefined | erlang:timestamp(), xmlel()}].
-callback read_message(binary(), binary(), non_neg_integer()) ->
{ok, #offline_msg{}} | error.
-callback remove_message(binary(), binary(), non_neg_integer()) -> ok | {error, any()}.
diff --git a/src/mod_offline_riak.erl b/src/mod_offline_riak.erl
index 241a8d650..24d565383 100644
--- a/src/mod_offline_riak.erl
+++ b/src/mod_offline_riak.erl
@@ -88,7 +88,7 @@ read_message_headers(LUser, LServer) ->
fun(#offline_msg{from = From, to = To, packet = Pkt,
timestamp = TS}) ->
Seq = now_to_integer(TS),
- {Seq, From, To, Pkt}
+ {Seq, From, To, TS, Pkt}
end, Rs),
lists:keysort(1, Hdrs);
_Err ->
diff --git a/src/mod_offline_sql.erl b/src/mod_offline_sql.erl
index 2b7a40bff..025aa56f5 100644
--- a/src/mod_offline_sql.erl
+++ b/src/mod_offline_sql.erl
@@ -103,8 +103,9 @@ read_message_headers(LUser, LServer) ->
case xml_to_offline_msg(XML) of
{ok, #offline_msg{from = From,
to = To,
+ timestamp = TS,
packet = El}} ->
- [{Seq, From, To, El}];
+ [{Seq, From, To, TS, El}];
_ ->
[]
end
diff --git a/src/mod_roster.erl b/src/mod_roster.erl
index c344213f3..2da09d317 100644
--- a/src/mod_roster.erl
+++ b/src/mod_roster.erl
@@ -330,7 +330,7 @@ set_roster(#roster{us = {LUser, LServer}, jid = LJID} = Item) ->
transaction(
LServer,
fun() ->
- roster_subscribe_t(LUser, LServer, LJID, Item)
+ update_roster_t(LUser, LServer, LJID, Item)
end).
del_roster(LUser, LServer, LJID) ->
diff --git a/src/muc_register.erl b/src/muc_register.erl
index cddce2b98..c2b951dfc 100644
--- a/src/muc_register.erl
+++ b/src/muc_register.erl
@@ -12,7 +12,7 @@
-include("muc_register.hrl").
--export_type([{property, 0}, {result, 0}, {form, 0}]).
+-export_type([property/0, result/0, form/0]).
dec_bool(<<"1">>) -> true;
dec_bool(<<"0">>) -> false;
diff --git a/src/muc_request.erl b/src/muc_request.erl
index 4c7becd2e..2d79ba0a5 100644
--- a/src/muc_request.erl
+++ b/src/muc_request.erl
@@ -12,7 +12,7 @@
-include("muc_request.hrl").
--export_type([{property, 0}, {result, 0}, {form, 0}]).
+-export_type([property/0, result/0, form/0]).
dec_enum(Val, Enums) ->
AtomVal = erlang:binary_to_existing_atom(Val, utf8),
diff --git a/src/muc_roomconfig.erl b/src/muc_roomconfig.erl
index 73ceb649e..7d18bab66 100644
--- a/src/muc_roomconfig.erl
+++ b/src/muc_roomconfig.erl
@@ -12,7 +12,7 @@
-include("muc_roomconfig.hrl").
--export_type([{property, 0}, {result, 0}, {form, 0}]).
+-export_type([property/0, result/0, form/0]).
dec_int(Val, Min, Max) ->
case list_to_integer(binary_to_list(Val)) of
diff --git a/src/muc_roominfo.erl b/src/muc_roominfo.erl
index 809dcef5b..bd5cb011b 100644
--- a/src/muc_roominfo.erl
+++ b/src/muc_roominfo.erl
@@ -12,7 +12,7 @@
-include("muc_roominfo.hrl").
--export_type([{property, 0}, {result, 0}, {form, 0}]).
+-export_type([property/0, result/0, form/0]).
dec_int(Val, Min, Max) ->
case list_to_integer(binary_to_list(Val)) of
diff --git a/src/pubsub_get_pending.erl b/src/pubsub_get_pending.erl
index 1a7de6a2d..c1f2ba3ad 100644
--- a/src/pubsub_get_pending.erl
+++ b/src/pubsub_get_pending.erl
@@ -12,7 +12,7 @@
-include("pubsub_get_pending.hrl").
--export_type([{property, 0}, {result, 0}, {form, 0}]).
+-export_type([property/0, result/0, form/0]).
format_error({form_type_mismatch, Type}) ->
<<"FORM_TYPE doesn't match '", Type/binary, "'">>;
diff --git a/src/pubsub_node_config.erl b/src/pubsub_node_config.erl
index 47ed10b49..e831d6a83 100644
--- a/src/pubsub_node_config.erl
+++ b/src/pubsub_node_config.erl
@@ -12,7 +12,7 @@
-include("pubsub_node_config.hrl").
--export_type([{property, 0}, {result, 0}, {form, 0}]).
+-export_type([property/0, result/0, form/0]).
dec_int(Val, Min, Max) ->
case list_to_integer(binary_to_list(Val)) of
diff --git a/src/pubsub_publish_options.erl b/src/pubsub_publish_options.erl
index 8d0229071..6e96946fd 100644
--- a/src/pubsub_publish_options.erl
+++ b/src/pubsub_publish_options.erl
@@ -12,7 +12,7 @@
-include("pubsub_publish_options.hrl").
--export_type([{property, 0}, {result, 0}, {form, 0}]).
+-export_type([property/0, result/0, form/0]).
dec_enum(Val, Enums) ->
AtomVal = erlang:binary_to_existing_atom(Val, utf8),
diff --git a/src/pubsub_subscribe_authorization.erl b/src/pubsub_subscribe_authorization.erl
index e019ed6b9..46538da8d 100644
--- a/src/pubsub_subscribe_authorization.erl
+++ b/src/pubsub_subscribe_authorization.erl
@@ -12,7 +12,7 @@
-include("pubsub_subscribe_authorization.hrl").
--export_type([{property, 0}, {result, 0}, {form, 0}]).
+-export_type([property/0, result/0, form/0]).
dec_bool(<<"1">>) -> true;
dec_bool(<<"0">>) -> false;
diff --git a/src/pubsub_subscribe_options.erl b/src/pubsub_subscribe_options.erl
index 446a84a00..02c046995 100644
--- a/src/pubsub_subscribe_options.erl
+++ b/src/pubsub_subscribe_options.erl
@@ -12,7 +12,7 @@
-include("pubsub_subscribe_options.hrl").
--export_type([{property, 0}, {result, 0}, {form, 0}]).
+-export_type([property/0, result/0, form/0]).
dec_enum(Val, Enums) ->
AtomVal = erlang:binary_to_existing_atom(Val, utf8),
diff --git a/src/xmpp_util.erl b/src/xmpp_util.erl
index 57440b50e..22b8ea597 100644
--- a/src/xmpp_util.erl
+++ b/src/xmpp_util.erl
@@ -11,7 +11,8 @@
%% API
-export([add_delay_info/3, add_delay_info/4, unwrap_carbon/1,
is_standalone_chat_state/1, get_xdata_values/2,
- has_xdata_var/2, make_adhoc_response/1, make_adhoc_response/2,
+ set_xdata_field/2, has_xdata_var/2,
+ make_adhoc_response/1, make_adhoc_response/2,
decode_timestamp/1, encode_timestamp/1]).
-include("xmpp.hrl").
@@ -78,6 +79,12 @@ get_xdata_values(Var, #xdata{fields = Fields}) ->
false -> []
end.
+-spec set_xdata_field(xdata_field(), xdata()) -> xdata().
+set_xdata_field(Field, #xdata{fields = Fields} = X) ->
+ NewFields = lists:keystore(Field#xdata_field.var, #xdata_field.var,
+ Fields, Field),
+ X#xdata{fields = NewFields}.
+
-spec has_xdata_var(binary(), xdata()) -> boolean().
has_xdata_var(Var, #xdata{fields = Fields}) ->
lists:keymember(Var, #xdata_field.var, Fields).