aboutsummaryrefslogtreecommitdiff
path: root/src/mod_carboncopy.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/mod_carboncopy.erl')
-rw-r--r--src/mod_carboncopy.erl104
1 files changed, 54 insertions, 50 deletions
diff --git a/src/mod_carboncopy.erl b/src/mod_carboncopy.erl
index 24c09bffd..81cf78a9d 100644
--- a/src/mod_carboncopy.erl
+++ b/src/mod_carboncopy.erl
@@ -7,7 +7,7 @@
%%% {mod_carboncopy, []}
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2015 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -25,7 +25,9 @@
%%%
%%%----------------------------------------------------------------------
-module (mod_carboncopy).
+
-author ('ecestari@process-one.net').
+-protocol({xep, 280, '0.8'}).
-behavior(gen_mod).
@@ -33,13 +35,9 @@
-export([start/2,
stop/1]).
-%% Hooks:
--export([user_send_packet/3,
- user_receive_packet/4,
- iq_handler2/3,
- iq_handler1/3,
- remove_connection/4,
- is_carbon_copy/1]).
+-export([user_send_packet/4, user_receive_packet/5,
+ iq_handler2/3, iq_handler1/3, remove_connection/4,
+ is_carbon_copy/1, mod_opt_type/1]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -57,9 +55,9 @@ is_carbon_copy(Packet) ->
is_carbon_copy(Packet, <<"received">>).
is_carbon_copy(Packet, Direction) ->
- case xml:get_subtag(Packet, Direction) of
+ case fxml:get_subtag(Packet, Direction) of
#xmlel{name = Direction, attrs = Attrs} ->
- case xml:get_attr_s(<<"xmlns">>, Attrs) of
+ case fxml:get_attr_s(<<"xmlns">>, Attrs) of
?NS_CARBONS_2 -> true;
?NS_CARBONS_1 -> true;
_ -> false
@@ -106,7 +104,7 @@ iq_handler1(From, To, IQ) ->
iq_handler(From, _To, #iq{type=set, sub_el = #xmlel{name = Operation, children = []}} = IQ, CC)->
?DEBUG("carbons IQ received: ~p", [IQ]),
- {U, S, R} = jlib:jid_tolower(From),
+ {U, S, R} = jid:tolower(From),
Result = case Operation of
<<"enable">>->
?INFO_MSG("carbons enabled for user ~s@~s/~s", [U,S,R]),
@@ -115,7 +113,7 @@ iq_handler(From, _To, #iq{type=set, sub_el = #xmlel{name = Operation, children
?INFO_MSG("carbons disabled for user ~s@~s/~s", [U,S,R]),
disable(S, U, R)
end,
- case Result of
+ case Result of
ok ->
?DEBUG("carbons IQ result: ok", []),
IQ#iq{type=result, sub_el=[]};
@@ -127,43 +125,43 @@ iq_handler(From, _To, #iq{type=set, sub_el = #xmlel{name = Operation, children
iq_handler(_From, _To, IQ, _CC)->
IQ#iq{type=error, sub_el = [?ERR_NOT_ALLOWED]}.
-user_send_packet(From, To, Packet) ->
+user_send_packet(Packet, _C2SState, From, To) ->
check_and_forward(From, To, Packet, sent).
-user_receive_packet(JID, _From, To, Packet) ->
+user_receive_packet(Packet, _C2SState, JID, _From, To) ->
check_and_forward(JID, To, Packet, received).
-
-% verifier si le trafic est local
-% Modified from original version:
+
+% Modified from original version:
% - registered to the user_send_packet hook, to be called only once even for multicast
% - do not support "private" message mode, and do not modify the original packet in any way
% - we also replicate "read" notifications
check_and_forward(JID, To, Packet, Direction)->
- case is_chat_or_normal_message(Packet) andalso
- xml:get_subtag(Packet, <<"private">>) == false andalso
- xml:get_subtag(Packet, <<"no-copy">>) == false of
+ case is_chat_message(Packet) andalso
+ fxml:get_subtag(Packet, <<"private">>) == false andalso
+ fxml:get_subtag(Packet, <<"no-copy">>) == false of
true ->
case is_carbon_copy(Packet) of
false ->
- send_copies(JID, To, Packet, Direction);
+ send_copies(JID, To, Packet, Direction),
+ Packet;
true ->
- %% stop the hook chain, we don't want mod_logdb to register
- %% this message (duplicate)
- stop
+ %% stop the hook chain, we don't want logging modules to duplicates
+ %% this message
+ {stop, Packet}
end;
_ ->
- ok
+ Packet
end.
remove_connection(User, Server, Resource, _Status)->
disable(Server, User, Resource),
ok.
-
+
%%% Internal
%% Direction = received | sent <received xmlns='urn:xmpp:carbons:1'/>
send_copies(JID, To, Packet, Direction)->
- {U, S, R} = jlib:jid_tolower(JID),
+ {U, S, R} = jid:tolower(JID),
PrioRes = ejabberd_sm:get_user_present_resources(U, S),
{_, AvailRs} = lists:unzip(PrioRes),
{MaxPrio, MaxRes} = case catch lists:max(PrioRes) of
@@ -182,7 +180,7 @@ send_copies(JID, To, Packet, Direction)->
TargetJIDs = case {IsBareTo, R} of
{true, MaxRes} ->
OrigTo = fun(Res) -> lists:member({MaxPrio, Res}, PrioRes) end,
- [ {jlib:make_jid({U, S, CCRes}), CC_Version}
+ [ {jid:make({U, S, CCRes}), CC_Version}
|| {CCRes, CC_Version} <- list(U, S),
lists:member(CCRes, AvailRs), not OrigTo(CCRes) ];
{true, _} ->
@@ -193,16 +191,16 @@ send_copies(JID, To, Packet, Direction)->
%% MaxRes) in order to avoid duplicates.
[];
{false, _} ->
- [ {jlib:make_jid({U, S, CCRes}), CC_Version}
+ [ {jid:make({U, S, CCRes}), CC_Version}
|| {CCRes, CC_Version} <- list(U, S),
lists:member(CCRes, AvailRs), CCRes /= R ]
- %TargetJIDs = lists:delete(JID, [ jlib:make_jid({U, S, CCRes}) || CCRes <- list(U, S) ]),
+ %TargetJIDs = lists:delete(JID, [ jid:make({U, S, CCRes}) || CCRes <- list(U, S) ]),
end,
lists:map(fun({Dest,Version}) ->
- {_, _, Resource} = jlib:jid_tolower(Dest),
+ {_, _, Resource} = jid:tolower(Dest),
?DEBUG("Sending: ~p =/= ~p", [R, Resource]),
- Sender = jlib:make_jid({U, S, <<>>}),
+ Sender = jid:make({U, S, <<>>}),
%{xmlelement, N, A, C} = Packet,
New = build_forward_packet(JID, Packet, Sender, Dest, Direction, Version),
ejabberd_router:route(Sender, Dest, New)
@@ -210,31 +208,31 @@ send_copies(JID, To, Packet, Direction)->
ok.
build_forward_packet(JID, Packet, Sender, Dest, Direction, ?NS_CARBONS_2) ->
- #xmlel{name = <<"message">>,
+ #xmlel{name = <<"message">>,
attrs = [{<<"xmlns">>, <<"jabber:client">>},
{<<"type">>, message_type(Packet)},
- {<<"from">>, jlib:jid_to_string(Sender)},
- {<<"to">>, jlib:jid_to_string(Dest)}],
- children = [
- #xmlel{name = list_to_binary(atom_to_list(Direction)),
+ {<<"from">>, jid:to_string(Sender)},
+ {<<"to">>, jid:to_string(Dest)}],
+ children = [
+ #xmlel{name = list_to_binary(atom_to_list(Direction)),
attrs = [{<<"xmlns">>, ?NS_CARBONS_2}],
children = [
- #xmlel{name = <<"forwarded">>,
+ #xmlel{name = <<"forwarded">>,
attrs = [{<<"xmlns">>, ?NS_FORWARD}],
children = [
complete_packet(JID, Packet, Direction)]}
]}
]};
build_forward_packet(JID, Packet, Sender, Dest, Direction, ?NS_CARBONS_1) ->
- #xmlel{name = <<"message">>,
+ #xmlel{name = <<"message">>,
attrs = [{<<"xmlns">>, <<"jabber:client">>},
{<<"type">>, message_type(Packet)},
- {<<"from">>, jlib:jid_to_string(Sender)},
- {<<"to">>, jlib:jid_to_string(Dest)}],
- children = [
- #xmlel{name = list_to_binary(atom_to_list(Direction)),
+ {<<"from">>, jid:to_string(Sender)},
+ {<<"to">>, jid:to_string(Dest)}],
+ children = [
+ #xmlel{name = list_to_binary(atom_to_list(Direction)),
attrs = [{<<"xmlns">>, ?NS_CARBONS_1}]},
- #xmlel{name = <<"forwarded">>,
+ #xmlel{name = <<"forwarded">>,
attrs = [{<<"xmlns">>, ?NS_FORWARD}],
children = [complete_packet(JID, Packet, Direction)]}
]}.
@@ -261,7 +259,7 @@ complete_packet(From, #xmlel{name = <<"message">>, attrs = OrigAttrs} = Packet,
Attrs = lists:keystore(<<"xmlns">>, 1, OrigAttrs, {<<"xmlns">>, <<"jabber:client">>}),
case proplists:get_value(<<"from">>, Attrs) of
undefined ->
- Packet#xmlel{attrs = [{<<"from">>, jlib:jid_to_string(From)}|Attrs]};
+ Packet#xmlel{attrs = [{<<"from">>, jid:to_string(From)}|Attrs]};
_ ->
Packet#xmlel{attrs = Attrs}
end;
@@ -270,20 +268,26 @@ complete_packet(_From, #xmlel{name = <<"message">>, attrs=OrigAttrs} = Packet, r
Packet#xmlel{attrs = Attrs}.
message_type(#xmlel{attrs = Attrs}) ->
- case xml:get_attr(<<"type">>, Attrs) of
+ case fxml:get_attr(<<"type">>, Attrs) of
{value, Type} -> Type;
false -> <<"normal">>
end.
-is_chat_or_normal_message(#xmlel{name = <<"message">>} = Packet) ->
+is_chat_message(#xmlel{name = <<"message">>} = Packet) ->
case message_type(Packet) of
<<"chat">> -> true;
- <<"normal">> -> true;
+ <<"normal">> -> has_non_empty_body(Packet);
_ -> false
end;
-is_chat_or_normal_message(_Packet) -> false.
+is_chat_message(_Packet) -> false.
+
+has_non_empty_body(Packet) ->
+ fxml:get_subtag_cdata(Packet, <<"body">>) =/= <<"">>.
%% list {resource, cc_version} with carbons enabled for given user and host
-list(User, Server)->
+list(User, Server) ->
mnesia:dirty_select(?TABLE, [{#carboncopy{us = {User, Server}, resource = '$2', version = '$3'}, [], [{{'$2','$3'}}]}]).
+
+mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
+mod_opt_type(_) -> [iqdisc].