]> granicus.if.org Git - ejabberd/commitdiff
ODBC support for mod_blocking
authorEvgeniy Khramtsov <ekhramtsov@process-one.net>
Thu, 12 Apr 2012 04:22:56 +0000 (14:22 +1000)
committerEvgeniy Khramtsov <ekhramtsov@process-one.net>
Thu, 12 Apr 2012 04:22:56 +0000 (14:22 +1000)
doc/guide.tex
src/mod_blocking_odbc.erl [new file with mode: 0644]
src/mod_privacy_odbc.erl
src/odbc/odbc_queries.erl

index 06814b5f18d0a65e36d46ba58cb74fcd660923be..ca60f37d12a3b9fafcc185cbc3a9c3086fc0e2c0 100644 (file)
@@ -68,6 +68,7 @@
 \newcommand{\modannounce}{\module{mod\_announce}}
 \newcommand{\modannounceodbc}{\module{mod\_announce\_odbc}}
 \newcommand{\modblocking}{\module{mod\_blocking}}
+\newcommand{\modblockingodbc}{\module{mod\_blocking\_odbc}}
 \newcommand{\modcaps}{\module{mod\_caps}}
 \newcommand{\modconfigure}{\module{mod\_configure}}
 \newcommand{\moddisco}{\module{mod\_disco}}
@@ -2586,6 +2587,7 @@ The following table lists all modules included in \ejabberd{}.
     \hline \ahrefloc{modannounce}{\modannounceodbc{}} & Manage announcements & recommends \modadhoc{} \\
     & & supported DB (*) \\
     \hline \modblocking{} & Simple Communications Blocking (\xepref{0191}) & \modprivacy{} \\
+    \hline \modblockingodbc{} & Simple Communications Blocking (\xepref{0191}) & \modprivacyodbc{} \\
     \hline \modcaps{} &  Entity Capabilities (\xepref{0115}) & \\
     \hline \modconfigure{} & Server configuration using Ad-Hoc & \modadhoc{} \\
     \hline \ahrefloc{moddisco}{\moddisco{}} & Service Discovery (\xepref{0030}) &  \\
@@ -2664,6 +2666,7 @@ database for the following data:
 \item vCard-Based Avatars: Use \term{mod\_vcard\_xupdate\_odbc} instead of \term{mod\_vcard\_xupdate}.
 \item Private XML storage: Use \term{mod\_private\_odbc} instead of \term{mod\_private}.
 \item User rules for blocking communications: Use \term{mod\_privacy\_odbc} instead of \term{mod\_privacy}.
+\item Simple Communications Blocking: Use \term{mod\_blocking\_odbc} instead of \term{mod\_blocking}.
 \item Pub-Sub nodes, items and subscriptions: Use \term{mod\_pubsub\_odbc} instead of \term{mod\_pubsub}.
 \item Multi-user chats: Use \term{mod\_muc\_odbc} instead of \term{mod\_muc}.
 \item Manage announcements: Use \term{mod\_announce\_odbc} instead of \term{mod\_announce}.
diff --git a/src/mod_blocking_odbc.erl b/src/mod_blocking_odbc.erl
new file mode 100644 (file)
index 0000000..016e794
--- /dev/null
@@ -0,0 +1,365 @@
+%%%----------------------------------------------------------------------
+%%% File    : mod_blocking_odbc.erl
+%%% Author  : Stephan Maka
+%%% Purpose : XEP-0191: Simple Communications Blocking
+%%% Created : 24 Aug 2008 by Stephan Maka <stephan@spaceboyz.net>
+%%%
+%%%
+%%% ejabberd, Copyright (C) 2002-2012   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., 59 Temple Place, Suite 330, Boston, MA
+%%% 02111-1307 USA
+%%%
+%%%----------------------------------------------------------------------
+
+-module(mod_blocking_odbc).
+
+-behaviour(gen_mod).
+
+-export([start/2, stop/1,
+        process_iq/3,
+        process_iq_set/4,
+        process_iq_get/5]).
+
+-include("ejabberd.hrl").
+-include("jlib.hrl").
+-include("mod_privacy.hrl").
+
+start(Host, Opts) ->
+    IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
+    ejabberd_hooks:add(privacy_iq_get, Host,
+                      ?MODULE, process_iq_get, 40),
+    ejabberd_hooks:add(privacy_iq_set, Host,
+                      ?MODULE, process_iq_set, 40),
+    mod_disco:register_feature(Host, ?NS_BLOCKING),
+    gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_BLOCKING,
+                                 ?MODULE, process_iq, IQDisc).
+
+stop(Host) ->
+    ejabberd_hooks:delete(privacy_iq_get, Host,
+                         ?MODULE, process_iq_get, 40),
+    ejabberd_hooks:delete(privacy_iq_set, Host,
+                         ?MODULE, process_iq_set, 40),
+    mod_disco:unregister_feature(Host, ?NS_BLOCKING),
+    gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_BLOCKING).
+
+process_iq(_From, _To, IQ) ->
+    SubEl = IQ#iq.sub_el,
+    IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}.
+
+process_iq_get(_, From, _To,
+              #iq{xmlns = ?NS_BLOCKING,
+                  sub_el = {xmlelement, "blocklist", _, _}},
+              _) ->
+    #jid{luser = LUser, lserver = LServer} = From,
+    {stop, process_blocklist_get(LUser, LServer)};
+
+process_iq_get(Acc, _, _, _, _) ->
+    Acc.
+
+process_iq_set(_, From, _To, #iq{xmlns = ?NS_BLOCKING,
+                                sub_el = {xmlelement, SubElName, _, SubEls}}) ->
+    #jid{luser = LUser, lserver = LServer} = From,
+    Res =
+        case {SubElName, xml:remove_cdata(SubEls)} of
+            {"block", []} ->
+                {error, ?ERR_BAD_REQUEST};
+            {"block", Els} ->
+                JIDs = parse_blocklist_items(Els, []),
+                process_blocklist_block(LUser, LServer, JIDs);
+            {"unblock", []} ->
+                process_blocklist_unblock_all(LUser, LServer);
+            {"unblock", Els} ->
+                JIDs = parse_blocklist_items(Els, []),
+                process_blocklist_unblock(LUser, LServer, JIDs);
+            _ ->
+                {error, ?ERR_BAD_REQUEST}
+        end,
+    {stop, Res};
+
+process_iq_set(Acc, _, _,  _) ->
+    Acc.
+
+is_list_needdb(Items) ->
+    lists:any(
+      fun(X) ->
+             case X#listitem.type of
+                 subscription -> true;
+                 group -> true;
+                 _ -> false
+             end
+      end, Items).
+
+list_to_blocklist_jids([], JIDs) ->
+    JIDs;
+
+list_to_blocklist_jids([#listitem{type = jid,
+                                 action = deny,
+                                 value = JID} = Item | Items], JIDs) ->
+    case Item of
+       #listitem{match_all = true} ->
+           Match = true;
+       #listitem{match_iq = true,
+                 match_message = true,
+                 match_presence_in = true,
+                 match_presence_out = true} ->
+           Match = true;
+       _ ->
+           Match = false
+    end,
+    if
+       Match ->
+           list_to_blocklist_jids(Items, [JID | JIDs]);
+       true ->
+           list_to_blocklist_jids(Items, JIDs)
+    end;
+
+% Skip Privacy List items than cannot be mapped to Blocking items
+list_to_blocklist_jids([_ | Items], JIDs) ->
+    list_to_blocklist_jids(Items, JIDs).
+
+parse_blocklist_items([], JIDs) ->
+    JIDs;
+
+parse_blocklist_items([{xmlelement, "item", Attrs, _} | Els], JIDs) ->
+    case xml:get_attr("jid", Attrs) of
+       {value, JID1} ->
+           JID = jlib:jid_tolower(jlib:string_to_jid(JID1)),
+           parse_blocklist_items(Els, [JID | JIDs]);
+       false ->
+           % Tolerate missing jid attribute
+           parse_blocklist_items(Els, JIDs)
+    end;
+
+parse_blocklist_items([_ | Els], JIDs) ->
+    % Tolerate unknown elements
+    parse_blocklist_items(Els, JIDs).
+
+process_blocklist_block(LUser, LServer, JIDs) ->
+    F = fun() ->
+                Default =
+                    case mod_privacy_odbc:sql_get_default_privacy_list_t(LUser) of
+                        {selected, ["name"], []} ->
+                            Name = "Blocked contacts",
+                            mod_privacy_odbc:sql_add_privacy_list(LUser, Name),
+                            mod_privacy_odbc:sql_set_default_privacy_list(
+                              LUser, Name),
+                            Name;
+                        {selected, ["name"], [{Name}]} ->
+                            Name
+                    end,
+                {selected, ["id"], [{ID}]} =
+                    mod_privacy_odbc:sql_get_privacy_list_id_t(LUser, Default),
+                case mod_privacy_odbc:sql_get_privacy_list_data_by_id_t(ID) of
+                    {selected,
+                     ["t", "value", "action", "ord",
+                      "match_all", "match_iq", "match_message",
+                      "match_presence_in",
+                      "match_presence_out"],
+                     RItems = [_|_]} ->
+                        List = lists:map(
+                                 fun mod_privacy_odbc:raw_to_item/1,
+                                 RItems);
+                    _ ->
+                        List = []
+                end,
+                AlreadyBlocked = list_to_blocklist_jids(List, []),
+                NewList =
+                    lists:foldr(
+                      fun(JID, List1) ->
+                              case lists:member(JID, AlreadyBlocked) of
+                                  true ->
+                                      List1;
+                                  false ->
+                                      [#listitem{type = jid,
+                                                 value = JID,
+                                                 action = deny,
+                                                 order = 0,
+                                                 match_all = true
+                                                } | List1]
+                              end
+                      end, List, JIDs),
+                NewRItems = lists:map(
+                              fun mod_privacy_odbc:item_to_raw/1,
+                              NewList),
+                mod_privacy_odbc:sql_set_privacy_list(
+                  ID, NewRItems),
+                {ok, Default, NewList}
+        end,
+    case ejabberd_odbc:sql_transaction(LServer, F) of
+       {atomic, {error, _} = Error} ->
+           Error;
+       {atomic, {ok, Default, List}} ->
+            UserList = make_userlist(Default, List),
+           broadcast_list_update(LUser, LServer, Default, UserList),
+           broadcast_blocklist_event(LUser, LServer, {block, JIDs}),
+           {result, [], UserList};
+       _ ->
+           {error, ?ERR_INTERNAL_SERVER_ERROR}
+    end.
+
+process_blocklist_unblock_all(LUser, LServer) ->
+    F = fun() ->
+                case mod_privacy_odbc:sql_get_default_privacy_list_t(LUser) of
+                    {selected, ["name"], []} ->
+                        ok;
+                    {selected, ["name"], [{Default}]} ->
+                        {selected, ["id"], [{ID}]} =
+                            mod_privacy_odbc:sql_get_privacy_list_id_t(
+                              LUser, Default),
+                        case mod_privacy_odbc:sql_get_privacy_list_data_by_id_t(ID) of
+                            {selected,
+                             ["t", "value", "action", "ord",
+                              "match_all", "match_iq", "match_message",
+                              "match_presence_in",
+                              "match_presence_out"],
+                             RItems = [_|_]} ->
+                                List = lists:map(
+                                         fun mod_privacy_odbc:raw_to_item/1,
+                                         RItems),
+                                NewList =
+                                    lists:filter(
+                                      fun(#listitem{action = A}) ->
+                                              A =/= deny
+                                      end, List),
+                                NewRItems = lists:map(
+                                              fun mod_privacy_odbc:item_to_raw/1,
+                                              NewList),
+                                mod_privacy_odbc:sql_set_privacy_list(
+                                  ID, NewRItems),
+                                {ok, Default, NewList};
+                            _ ->
+                                ok
+                        end;
+                    _ ->
+                        ok
+                end
+        end,
+    case ejabberd_odbc:sql_transaction(LServer, F) of
+       {atomic, {error, _} = Error} ->
+           Error;
+       {atomic, ok} ->
+           {result, []};
+       {atomic, {ok, Default, List}} ->
+            UserList = make_userlist(Default, List),
+           broadcast_list_update(LUser, LServer, Default, UserList),
+           broadcast_blocklist_event(LUser, LServer, unblock_all),
+           {result, [], UserList};
+       _ ->
+           {error, ?ERR_INTERNAL_SERVER_ERROR}
+    end.
+
+process_blocklist_unblock(LUser, LServer, JIDs) ->
+    F = fun() ->
+                case mod_privacy_odbc:sql_get_default_privacy_list_t(LUser) of
+                    {selected, ["name"], []} ->
+                        ok;
+                    {selected, ["name"], [{Default}]} ->
+                        {selected, ["id"], [{ID}]} =
+                            mod_privacy_odbc:sql_get_privacy_list_id_t(
+                              LUser, Default),
+                        case mod_privacy_odbc:sql_get_privacy_list_data_by_id_t(ID) of
+                            {selected,
+                             ["t", "value", "action", "ord",
+                              "match_all", "match_iq", "match_message",
+                              "match_presence_in",
+                              "match_presence_out"],
+                             RItems = [_|_]} ->
+                                List = lists:map(
+                                         fun mod_privacy_odbc:raw_to_item/1,
+                                         RItems),
+                                NewList =
+                                    lists:filter(
+                                      fun(#listitem{action = deny,
+                                                    type = jid,
+                                                    value = JID}) ->
+                                              not(lists:member(JID, JIDs));
+                                         (_) ->
+                                              true
+                                      end, List),
+                                NewRItems = lists:map(
+                                              fun mod_privacy_odbc:item_to_raw/1,
+                                              NewList),
+                                mod_privacy_odbc:sql_set_privacy_list(
+                                  ID, NewRItems),
+                                {ok, Default, NewList};
+                            _ ->
+                                ok
+                        end;
+                    _ ->
+                        ok
+                end
+        end,
+    case ejabberd_odbc:sql_transaction(LServer, F) of
+       {atomic, {error, _} = Error} ->
+           Error;
+       {atomic, ok} ->
+           {result, []};
+       {atomic, {ok, Default, List}} ->
+            UserList = make_userlist(Default, List),
+           broadcast_list_update(LUser, LServer, Default, UserList),
+           broadcast_blocklist_event(LUser, LServer, {unblock, JIDs}),
+           {result, [], UserList};
+       _ ->
+           {error, ?ERR_INTERNAL_SERVER_ERROR}
+    end.
+
+make_userlist(Name, List) ->
+    NeedDb = is_list_needdb(List),
+    #userlist{name = Name, list = List, needdb = NeedDb}.
+
+broadcast_list_update(LUser, LServer, Name, UserList) ->
+    ejabberd_router:route(
+      jlib:make_jid(LUser, LServer, ""),
+      jlib:make_jid(LUser, LServer, ""),
+      {xmlelement, "broadcast", [],
+       [{privacy_list, UserList, Name}]}).
+
+broadcast_blocklist_event(LUser, LServer, Event) ->
+    JID = jlib:make_jid(LUser, LServer, ""),
+    ejabberd_router:route(
+      JID, JID,
+      {xmlelement, "broadcast", [],
+       [{blocking, Event}]}).
+
+process_blocklist_get(LUser, LServer) ->
+    case catch mod_privacy_odbc:sql_get_default_privacy_list(LUser, LServer) of
+       {selected, ["name"], []} ->
+            {result, [{xmlelement, "blocklist",
+                       [{"xmlns", ?NS_BLOCKING}], []}]};
+       {selected, ["name"], [{Default}]} ->
+           case catch mod_privacy_odbc:sql_get_privacy_list_data(
+                         LUser, LServer, Default) of
+               {selected, ["t", "value", "action", "ord", "match_all",
+                           "match_iq", "match_message",
+                           "match_presence_in", "match_presence_out"],
+                RItems} ->
+                    List = lists:map(fun mod_privacy_odbc:raw_to_item/1, RItems),
+                    JIDs = list_to_blocklist_jids(List, []),
+                    Items = lists:map(
+                             fun(JID) ->
+                                     ?DEBUG("JID: ~p",[JID]),
+                                     {xmlelement, "item",
+                                      [{"jid", jlib:jid_to_string(JID)}], []}
+                             end, JIDs),
+                   {result,
+                    [{xmlelement, "blocklist", [{"xmlns", ?NS_BLOCKING}],
+                      Items}]};
+                {'EXIT', _} ->
+                    {error, ?ERR_INTERNAL_SERVER_ERROR}
+            end;
+        {'EXIT', _} ->
+            {error, ?ERR_INTERNAL_SERVER_ERROR}
+    end.
index 432617ebe066ed36be4eca4ed03633c7d86a89d6..87303b57ac912d4a8b93257c4f50c48f07999ad1 100644 (file)
         check_packet/6,
         remove_user/2,
          item_to_raw/1,
+         raw_to_item/1,
         updated_list/3]).
 
+%% For mod_blocking_odbc
+-export([sql_add_privacy_list/2,
+         sql_get_default_privacy_list/2,
+         sql_get_default_privacy_list_t/1,
+         sql_get_privacy_list_data/3,
+         sql_get_privacy_list_data_by_id_t/1,
+         sql_get_privacy_list_id_t/2,
+         sql_set_default_privacy_list/2,
+         sql_set_privacy_list/2]).
+
 -include("ejabberd.hrl").
 -include("jlib.hrl").
 -include("mod_privacy.hrl").
@@ -836,6 +847,9 @@ sql_get_privacy_list_data(LUser, LServer, Name) ->
 sql_get_privacy_list_data_by_id(ID, LServer) ->
     odbc_queries:get_privacy_list_data_by_id(LServer, ID).
 
+sql_get_privacy_list_data_by_id_t(ID) ->
+    odbc_queries:get_privacy_list_data_by_id_t(ID).
+
 sql_set_default_privacy_list(LUser, Name) ->
     Username = ejabberd_odbc:escape(LUser),
     SName = ejabberd_odbc:escape(Name),
index 2af5f2ba63b4f705139a44bd3740a3a69b0fe1bd..3ec3b1be5ae7b5c2561c11fe12aeec006eaa3b5b 100644 (file)
@@ -70,6 +70,7 @@
         get_privacy_list_id_t/2,
         get_privacy_list_data/3,
         get_privacy_list_data_by_id/2,
+         get_privacy_list_data_by_id_t/1,
         set_default_privacy_list/2,
         unset_default_privacy_list/2,
         remove_privacy_list/2,
@@ -506,6 +507,13 @@ get_privacy_list_data_by_id(LServer, ID) ->
        "from privacy_list_data "
        "where id='", ID, "' order by ord;"]).
 
+get_privacy_list_data_by_id_t(ID) ->
+    ejabberd_odbc:sql_query_t(
+      ["select t, value, action, ord, match_all, match_iq, "
+       "match_message, match_presence_in, match_presence_out "
+       "from privacy_list_data "
+       "where id='", ID, "' order by ord;"]).
+
 set_default_privacy_list(Username, SName) ->
     update_t("privacy_default_list", ["username", "name"],
             [Username, SName], ["username='", Username, "'"]).
@@ -834,6 +842,10 @@ get_privacy_list_data_by_id(LServer, ID) ->
       LServer,
       ["EXECUTE dbo.get_privacy_list_data_by_id '", ID, "'"]).
 
+get_privacy_list_data_by_id_t(ID) ->
+    ejabberd_odbc:sql_query_t(
+      ["EXECUTE dbo.get_privacy_list_data_by_id '", ID, "'"]).
+
 set_default_privacy_list(Username, SName) ->
     ejabberd_odbc:sql_query_t(
       ["EXECUTE dbo.set_default_privacy_list '", Username, "' , '", SName, "'"]).