aboutsummaryrefslogtreecommitdiff
path: root/src/mod_xmlrpc.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/mod_xmlrpc.erl')
-rw-r--r--src/mod_xmlrpc.erl1066
1 files changed, 0 insertions, 1066 deletions
diff --git a/src/mod_xmlrpc.erl b/src/mod_xmlrpc.erl
deleted file mode 100644
index e5cdaf7d9..000000000
--- a/src/mod_xmlrpc.erl
+++ /dev/null
@@ -1,1066 +0,0 @@
-%%%----------------------------------------------------------------------
-%%% File : mod_xmlrpc.erl
-%%% Author : Badlop / Mickael Remond / Christophe Romain
-%%% Purpose : XML-RPC server
-%%% Created :
-%%% Id :
-%%%----------------------------------------------------------------------
-
-%%%/***************************************************************************
-%%% * *
-%%% * 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. *
-%%% * *
-%%% ***************************************************************************/
-%%%
-%%%
-%%% MOD_XMLRPC - an XML-RPC server module for ejabberd
-%%%
-%%% v0.5 - 17 March 2008
-%%%
-%%% http://ejabberd.jabber.ru/mod_xmlrpc
-%%%
-%%% (C) 2005, Badlop
-%%% 2006, Process-one
-%%% 2007, Process-one
-%%% 2008, Process-one
-%%%
-%%% Changelog:
-%%%
-%%% 0.7 - 02 April 2009 - cromain
-%%% - add user nick change
-%%%
-%%% 0.6 - 02 June 2008 - cromain
-%%% - add user existance checking
-%%% - improve parameter checking
-%%% - allow orderless parameter
-%%%
-%%% 0.5 - 17 March 2008 - cromain
-%%% - add user changing and higher level methods
-%%%
-%%% 0.4 - 18 February 2008 - cromain
-%%% - add roster handling
-%%% - add message sending
-%%% - code and api clean-up
-%%%
-%%% 0.3 - 18 October 2007 - cromain
-%%% - presence improvement
-%%% - add new functionality
-%%%
-%%% 0.2 - 4 March 2006 - mremond
-%%% - Code clean-up
-%%% - Made it compatible with current ejabberd SVN version
-%%%
-%%% 0.1.2 - 28 December 2005
-%%% - Now compatible with ejabberd 1.0.0
-%%% - The XMLRPC server is started only once, not once for every virtual host
-%%% - Added comments for handlers. Every available handler must be explained
-%%%
-
--module(mod_xmlrpc).
--author('Process-one').
--vsn('0.6').
-
--behaviour(gen_mod).
-
--export([start/2,
- handler/2,
- link_contacts/5,
- unlink_contacts/3,
- loop/1,
- stop/1]).
-
--export([add_rosteritem/6]).
--export([add_rosteritem_groups/5,
- del_rosteritem_groups/5,
- modify_rosteritem_groups/7]).
-
--include("ejabberd.hrl").
--include("jlib.hrl").
--include("mod_roster.hrl").
-
--ifdef(EJABBERD1).
--record(session, {sid, usr, us, priority}). %% ejabberd 1.1.x
--else.
--record(session, {sid, usr, us, priority, info}). %% ejabberd 2.0.x
--endif.
-
-
--define(PROCNAME, ejabberd_mod_xmlrpc).
--define(PORT, 4560).
--define(TIMEOUT, 5000).
-
-%% -----------------------------
-%% Module interface
-%% -----------------------------
-
-start(_Host, Opts) ->
- case whereis(?PROCNAME) of
- undefined ->
- %% get options
- Port = gen_mod:get_opt(port, Opts, ?PORT),
- MaxSessions = 10,
- Timeout = gen_mod:get_opt(timeout, Opts, ?TIMEOUT),
- Handler = {mod_xmlrpc, handler},
- State = tryit,
-
- %% TODO: this option gives
- %% error_info: {function_clause,[{gen_tcp,mod,[{ip,{127,0,0,1}}]},
- %%case gen_mod:get_opt(listen_all, Opts, false) of
- %% true -> Ip = all;
- %% false -> Ip = {127, 0, 0, 1}
- %%end,
- Ip = all,
-
- %% start the XML-RPC server
- {ok, Pid} = xmlrpc:start_link(Ip, Port, MaxSessions, Timeout, Handler, State),
-
- %% start the loop process
- register(?PROCNAME, spawn(?MODULE, loop, [Pid])),
- ok;
- _ ->
- ok
- end.
-
-loop(Pid) ->
- receive
- stop ->
- xmlrpc:stop(Pid)
- end.
-
-stop(_Host) ->
- case whereis(?PROCNAME) of
- undefined ->
- ok;
- _Pid ->
- ?PROCNAME ! stop,
- unregister(?PROCNAME)
- end.
-
-
-%% -----------------------------
-%% Handlers
-%% -----------------------------
-
-handler(tryit, Call) ->
- try handler(notry, Call) of
- Result -> Result
- catch
- A:B ->
- ?ERROR_MSG("Problem '~p' in~nCall: ~p~nError: ~p", [A, Call, B]),
- {false, {response, [-100]}}
- end;
-
-% Call: Arguments: Returns:
-
-%% .............................
-%% Debug
-
-%% echothis String String
-handler(_State, {call, echothis, [A]}) ->
- {false, {response, [A]}};
-
-%% multhis struct[{a, Integer}, {b, Integer}] Integer
-handler(_State, {call, multhis, [{struct, Struct}]}) ->
- [{a, A}, {b, B}] = lists:sort(Struct),
- {false, {response, [A*B]}};
-
-%% .............................
-%% User administration
-
-%% create_account struct[{user, String}, {server, Server}, {password, String}] Integer
-handler(_State, {call, create_account, [{struct, Struct}]}) ->
- [{password, P}, {server, S}, {user, U}] = lists:sort(Struct),
- case ejabberd_auth:try_register(U, S, P) of
- {atomic, ok} ->
- {false, {response, [0]}};
- {atomic, exists} ->
- {false, {response, [409]}};
- _ ->
- {false, {response, [1]}}
- end;
-
-%% delete_account struct[{user, String}, {server, Server}] Integer
-handler(_State, {call, delete_account, [{struct, Struct}]}) ->
- [{server, S}, {user, U}] = lists:sort(Struct),
- Fun = fun() -> ejabberd_auth:remove_user(U, S) end,
- user_action(U, S, Fun, ok);
-
-%% change_password struct[{user, String}, {server, String}, {newpass, String}] Integer
-handler(_State, {call, change_password, [{struct, Struct}]}) ->
- [{newpass, P}, {server, S}, {user, U}] = lists:sort(Struct),
- Fun = fun() -> ejabberd_auth:set_password(U, S, P) end,
- user_action(U, S, Fun, ok);
-
-%% set_nickname struct[{user, String}, {server, String}, {nick, String}] Integer
-handler(_State, {call, set_nickname, [{struct, Struct}]}) ->
- [{nick, N}, {server, S}, {user, U}] = lists:sort(Struct),
- Fun = fun() -> case mod_vcard:process_sm_iq(
- {jid, U, S, "", U, S, ""},
- {jid, U, S, "", U, S, ""},
- {iq, "", set, "", "en",
- {xmlelement, "vCard",
- [{"xmlns", "vcard-temp"}], [
- {xmlelement, "NICKNAME", [], [{xmlcdata, N}]}
- ]
- }}) of
- {iq, [], result, [], _, []} -> ok;
- _ -> error
- end
- end,
- user_action(U, S, Fun, ok);
-
-%% set_rosternick struct[{user, String}, {server, String}, {nick, String}] Integer
-handler(_State, {call, set_rosternick, [{struct, Struct}]}) ->
- [{nick, N}, {server, S}, {user, U}] = lists:sort(Struct),
- Fun = fun() -> change_rosternick(U, S, N) end,
- user_action(U, S, Fun, ok);
-
-%% add_rosteritem struct[{user, String}, {server, String},
-%% {jid, String}, {group, String}, {nick, String}, {subs, String}] Integer
-handler(_State, {call, add_rosteritem, [{struct, Struct}]}) ->
- [{group, G},{jid, JID},{nick, N},{server, S},{subs, Subs},{user, U}] = lists:sort(Struct),
- Fun = fun() -> add_rosteritem(U, S, JID, N, G, Subs) end,
- user_action(U, S, Fun, {atomic, ok});
-
-%% link_contacts struct[{jid1, String}, {nick1, String}, {jid2, String}, {nick2, String}] Integer
-handler(_State, {call, link_contacts, [{struct, Struct}]}) ->
- [{group1, G1}, {group2, G2}, {jid1, JID1}, {jid2, JID2}, {nick1, Nick1}, {nick2, Nick2}] = lists:sort(Struct),
- {U1, S1, _} = jlib:jid_tolower(jlib:string_to_jid(JID1)),
- {U2, S2, _} = jlib:jid_tolower(jlib:string_to_jid(JID2)),
- case {ejabberd_auth:is_user_exists(U1, S1), ejabberd_auth:is_user_exists(U2, S2)} of
- {true, true} ->
- case link_contacts(JID1, Nick1, G1, JID2, Nick2, G2) of
- {atomic, ok} ->
- {false, {response, [0]}};
- _ ->
- {false, {response, [1]}}
- end;
- _ ->
- {false, {response, [404]}}
- end;
-
-%% delete_rosteritem struct[{user, String}, {server, String}, {jid, String}] Integer
-handler(_State, {call, delete_rosteritem, [{struct, Struct}]}) ->
- [{jid, JID}, {server, S}, {user, U}] = lists:sort(Struct),
- Fun = fun() -> del_rosteritem(U, S, JID) end,
- user_action(U, S, Fun, {atomic, ok});
-
-%% unlink_contacts struct[{jid1, String}, {jid2, String}] Integer
-handler(_State, {call, unlink_contacts, [{struct, Struct}]}) ->
- [{jid1, JID1}, {jid2, JID2}] = lists:sort(Struct),
- {U1, S1, _} = jlib:jid_tolower(jlib:string_to_jid(JID1)),
- {U2, S2, _} = jlib:jid_tolower(jlib:string_to_jid(JID2)),
- case {ejabberd_auth:is_user_exists(U1, S1), ejabberd_auth:is_user_exists(U2, S2)} of
- {true, true} ->
- case unlink_contacts(JID1, JID2) of
- {atomic, ok} ->
- {false, {response, [0]}};
- _ ->
- {false, {response, [1]}}
- end;
- _ ->
- {false, {response, [404]}}
- end;
-
-handler(_State, {call, add_rosteritem_groups, [{struct, Struct}]}) ->
- [{jid, JID}, {newgroups, NewGroupsString}, {push, PushString}, {server, Server}, {user, User}] = lists:sort(Struct),
- {U1, S1, _} = jlib:jid_tolower(jlib:string_to_jid(JID)),
- NewGroups = string:tokens(NewGroupsString, ";"),
- Push = list_to_atom(PushString),
- case {ejabberd_auth:is_user_exists(U1, S1), ejabberd_auth:is_user_exists(User, Server)} of
- {true, true} ->
- case add_rosteritem_groups(User, Server, JID, NewGroups, Push) of
- ok ->
- {false, {response, [0]}};
- Error ->
- ?INFO_MSG("Error found: ~n~p", [Error]),
- {false, {response, [1]}}
- end;
- _ ->
- {false, {response, [404]}}
- end;
-
-handler(_State, {call, del_rosteritem_groups, [{struct, Struct}]}) ->
- [{jid, JID}, {newgroups, NewGroupsString}, {push, PushString}, {server, Server}, {user, User}] = lists:sort(Struct),
- {U1, S1, _} = jlib:jid_tolower(jlib:string_to_jid(JID)),
- NewGroups = string:tokens(NewGroupsString, ";"),
- Push = list_to_atom(PushString),
- case {ejabberd_auth:is_user_exists(U1, S1), ejabberd_auth:is_user_exists(User, Server)} of
- {true, true} ->
- case del_rosteritem_groups(User, Server, JID, NewGroups, Push) of
- ok ->
- {false, {response, [0]}};
- Error ->
- ?INFO_MSG("Error found: ~n~p", [Error]),
- {false, {response, [1]}}
- end;
- _ ->
- {false, {response, [404]}}
- end;
-
-handler(_State, {call, modify_rosteritem_groups, [{struct, Struct}]}) ->
- [{jid, JID}, {newgroups, NewGroupsString}, {nick, Nick}, {push, PushString},
- {server, Server}, {subscription, SubsString}, {user, User}] = lists:sort(Struct),
- Subs = list_to_atom(SubsString),
- {U1, S1, _} = jlib:jid_tolower(jlib:string_to_jid(JID)),
- NewGroups = string:tokens(NewGroupsString, ";"),
- Push = list_to_atom(PushString),
- case ejabberd_auth:is_user_exists(User, Server) of
- true ->
- case modify_rosteritem_groups(User, Server, JID, NewGroups, Push, Nick, Subs) of
- ok ->
- {false, {response, [0]}};
- Error ->
- ?INFO_MSG("Error found: ~n~p", [Error]),
- {false, {response, [1]}}
- end;
- _ ->
- {false, {response, [404]}}
- end;
-
-%% get_roster struct[{user, String}, {server, String}]
-%% array[struct[{jid, String}, {groups, array[String]}, {nick, String},
-%% {subscription, String}, {pending, String}]]
-handler(_State, {call, get_roster, [{struct, Struct}]}) ->
- [{server, S}, {user, U}] = lists:sort(Struct),
- case ejabberd_auth:is_user_exists(U, S) of
- true ->
- Roster = format_roster(get_roster(U, S)),
- {false, {response, [{array, Roster}]}};
- false ->
- {false, {response, [404]}}
- end;
-
-%% get_roster_with_presence struct[{user, String}, {server, String}]
-%% array[struct[{jid, String}, {resource, String}, {group, String}, {nick, String},
-%% {subscription, String}, {pending, String},
-%% {show, String}, {status, String}]]
-handler(_State, {call, get_roster_with_presence, [{struct, Struct}]}) ->
- [{server, S}, {user, U}] = lists:sort(Struct),
- case ejabberd_auth:is_user_exists(U, S) of
- true ->
- Roster = format_roster_with_presence(get_roster(U, S)),
- {false, {response, [{array, Roster}]}};
- false ->
- {false, {response, [404]}}
- end;
-
-%% get_presence struct[{user, String}, {server, String}]
-%% array[struct[{jid, String}, {show, String}, {status, String}]]
-handler(_State, {call, get_presence, [{struct, Struct}]}) ->
- [{server, S}, {user, U}] = lists:sort(Struct),
- case ejabberd_auth:is_user_exists(U, S) of
- true ->
- {Resource, Show, Status} = get_presence(U, S),
- FullJID = case Resource of
- [] ->
- lists:flatten([U,"@",S]);
- _ ->
- lists:flatten([U,"@",S,"/",Resource])
- end,
- R = {struct, [{jid, FullJID}, {show, Show}, {status, Status} ]},
- {false, {response, [R]}};
- false ->
- {false, {response, [404]}}
- end;
-
-%% get_resources struct[{user, String}, {server, String}]
-%% array[String]
-handler(_State, {call, get_resources, [{struct, Struct}]}) ->
- [{server, S}, {user, U}] = lists:sort(Struct),
- case ejabberd_auth:is_user_exists(U, S) of
- true ->
- Resources = get_resources(U, S),
- {false, {response, [{array, Resources}]}};
- false ->
- {false, {response, [404]}}
- end;
-
-%% send_chat struct[{from, String}, {to, String}, {body, String}]
-%% Integer
-handler(_State, {call, send_chat, [{struct, Struct}]}) ->
- [{body, Msg}, {from, FromJID}, {to, ToJID}] = lists:sort(Struct),
- From = jlib:string_to_jid(FromJID),
- To = jlib:string_to_jid(ToJID),
- Stanza = {xmlelement, "message", [{"type", "chat"}],
- [{xmlelement, "body", [], [{xmlcdata, Msg}]}]},
- ejabberd_router:route(From, To, Stanza),
- {false, {response, [0]}};
-
-%% send_message struct[{from, String}, {to, String}, {subject, String}, {body, String}]
-%% Integer
-handler(_State, {call, send_message, [{struct, Struct}]}) ->
- [{body, Msg}, {from, FromJID}, {subject, Sub}, {to, ToJID}] = lists:sort(Struct),
- From = jlib:string_to_jid(FromJID),
- To = jlib:string_to_jid(ToJID),
- Stanza = {xmlelement, "message", [{"type", "normal"}],
- [{xmlelement, "subject", [], [{xmlcdata, Sub}]},
- {xmlelement, "body", [], [{xmlcdata, Msg}]}]},
- ejabberd_router:route(From, To, Stanza),
- {false, {response, [0]}};
-
-%% send_stanza struct[{from, String}, {to, String}, {stanza, String}]
-%% Integer
-handler(_State, {call, send_stanza, [{struct, Struct}]}) ->
- [{from, FromJID}, {stanza, StanzaStr}, {to, ToJID}] = lists:sort(Struct),
- case xml_stream:parse_element(StanzaStr) of
- {error, _} ->
- {false, {response, [1]}};
- Stanza ->
- {xmlelement, _, Attrs, _} = Stanza,
- From = jlib:string_to_jid(proplists:get_value("from", Attrs, FromJID)),
- To = jlib:string_to_jid(proplists:get_value("to", Attrs, ToJID)),
- ejabberd_router:route(From, To, Stanza),
- {false, {response, [0]}}
- end;
-
-%% rename_account struct[{user, String}, {server, String}, {newuser, String}, {newserver, String}]
-%% Integer
-handler(_State, {call, rename_account, [{struct, Struct}]}) ->
- [{newserver, NS}, {newuser, NU}, {server, S}, {user, U}] = lists:sort(Struct),
- case ejabberd_auth:is_user_exists(U, S) of
- true ->
- case ejabberd_auth:get_password(U, S) of
- false ->
- {false, {response, [1]}};
- Password ->
- case ejabberd_auth:try_register(NU, NS, Password) of
- {atomic, ok} ->
- OldJID = jlib:jid_to_string({U, S, ""}),
- NewJID = jlib:jid_to_string({NU, NS, ""}),
- Roster = get_roster(U, S),
- lists:foreach(fun(#roster{jid={RU, RS, RE}, name=Nick, groups=Groups}) ->
- NewGroup = extract_group(Groups),
- {NewNick, Group} = case lists:filter(fun(#roster{jid={PU, PS, _}}) ->
- (PU == U) and (PS == S)
- end, get_roster(RU, RS)) of
- [#roster{name=OldNick, groups=OldGroups}|_] -> {OldNick, extract_group(OldGroups)};
- [] -> {NU, []}
- end,
- JIDStr = jlib:jid_to_string({RU, RS, RE}),
- link_contacts(NewJID, NewNick, NewGroup, JIDStr, Nick, Group),
- unlink_contacts(OldJID, JIDStr)
- end, Roster),
- ejabberd_auth:remove_user(U, S),
- {false, {response, [0]}};
- {atomic, exists} ->
- {false, {response, [409]}};
- _ ->
- {false, {response, [1]}}
- end
- end;
- false ->
- {false, {response, [404]}}
- end;
-
-%% add_contacts struct[{user, String}, {server, String},
-%% array[struct[{jid, String}, {group, String}, {nick, String}]]]
-%% Integer
-handler(_State, {call, add_contacts, [{struct, Struct}]}) ->
- [{array, Contacts}, {server, S}, {user, U}] = lists:sort(Struct),
- case ejabberd_auth:is_user_exists(U, S) of
- true ->
- JID1 = jlib:jid_to_string({U, S, ""}),
- Response = lists:foldl(fun({struct, Struct2}, Acc) ->
- [{group, Group}, {jid, JID2}, {nick, Nick}] = lists:sort(Struct2),
- {PU, PS, _} = jlib:jid_tolower(jlib:string_to_jid(JID2)),
- case ejabberd_auth:is_user_exists(PU, PS) of
- true ->
- case link_contacts(JID1, "", "", JID2, Nick, Group) of
- {atomic, ok} -> Acc;
- _ -> 1
- end;
- false ->
- Acc
- end
- end, 0, element(2, Contacts)),
- {false, {response, [Response]}};
- false ->
- {false, {response, [404]}}
- end;
-
-%% remove_contacts struct[{user, String}, {server, String}, array[String]]
-%% Integer
-handler(_State, {call, remove_contacts, [{struct, Struct}]}) ->
- [{array, Contacts}, {server, S}, {user, U}] = lists:sort(Struct),
- case ejabberd_auth:is_user_exists(U, S) of
- true ->
- JID1 = jlib:jid_to_string({U, S, ""}),
- Response = lists:foldl(fun(JID2, Acc) ->
- {PU, PS, _} = jlib:jid_tolower(jlib:string_to_jid(JID2)),
- case ejabberd_auth:is_user_exists(PU, PS) of
- true ->
- case unlink_contacts(JID1, JID2) of
- {atomic, ok} -> Acc;
- _ -> 1
- end;
- false ->
- Acc
- end
- end, 0, element(2, Contacts)),
- {false, {response, [Response]}};
- false ->
- {false, {response, [404]}}
- end;
-
-%% check_users_registration array[struct[{user, String}, {server, String}]]
-%% array[struct[{user, String}, {server, String}, {status, Integer}]]
-handler(_State, {call, check_users_registration, [{array, Users}]}) ->
- Response = lists:map(fun({struct, Struct}) ->
- [{server, S}, {user, U}] = lists:sort(Struct),
- Registered = case ejabberd_auth:is_user_exists(U, S) of
- true -> 1;
- false -> 0
- end,
- {struct, [{user, U}, {server, S}, {status, Registered}]}
- end, Users),
- {false, {response, [{array, Response}]}};
-
-
-%% If no other guard matches
-handler(_State, Payload) ->
- FaultString = lists:flatten(io_lib:format("Unknown call: ~p", [Payload])),
- {false, {response, {fault, -1, FaultString}}}.
-
-
-%% -----------------------------
-%% Internal roster handling
-%% -----------------------------
-
-get_roster(User, Server) ->
- LUser = jlib:nodeprep(User),
- LServer = jlib:nameprep(Server),
- ejabberd_hooks:run_fold(roster_get, LServer, [], [{LUser, LServer}]).
-
-change_rosternick(User, Server, Nick) ->
- LUser = jlib:nodeprep(User),
- LServer = jlib:nameprep(Server),
- LJID = {LUser, LServer, []},
- JID = jlib:jid_to_string(LJID),
- Push = fun(Subscription) ->
- jlib:iq_to_xml(#iq{type = set, xmlns = ?NS_ROSTER, id = "push",
- sub_el = [{xmlelement, "query", [{"xmlns", ?NS_ROSTER}],
- [{xmlelement, "item", [{"jid", JID}, {"name", Nick}, {"subscription", atom_to_list(Subscription)}],
- []}]}]})
- end,
- Result = case roster_backend(Server) of
- mnesia ->
- %% XXX This way of doing can not work with s2s
- mnesia:transaction(
- fun() ->
- lists:foreach(fun(Roster) ->
- {U, S} = Roster#roster.us,
- mnesia:write(Roster#roster{name = Nick}),
- lists:foreach(fun(R) ->
- UJID = jlib:make_jid(U, S, R),
- ejabberd_router:route(UJID, UJID, Push(Roster#roster.subscription))
- end, get_resources(U, S))
- end, mnesia:match_object(#roster{jid = LJID, _ = '_'}))
- end);
- odbc ->
- %%% XXX This way of doing does not work with several domains
- ejabberd_odbc:sql_transaction(Server,
- fun() ->
- SNick = ejabberd_odbc:escape(Nick),
- SJID = ejabberd_odbc:escape(JID),
- ejabberd_odbc:sql_query_t(
- ["update rosterusers"
- " set nick='", SNick, "'"
- " where jid='", SJID, "';"]),
- case ejabberd_odbc:sql_query_t(
- ["select username from rosterusers"
- " where jid='", SJID, "'"
- " and subscription = 'B';"]) of
- {selected, ["username"], Users} ->
- lists:foreach(fun({RU}) ->
- lists:foreach(fun(R) ->
- UJID = jlib:make_jid(RU, Server, R),
- ejabberd_router:route(UJID, UJID, Push(both))
- end, get_resources(RU, Server))
- end, Users);
- _ ->
- ok
- end
- end);
- none ->
- {error, no_roster}
- end,
- case Result of
- {atomic, ok} -> ok;
- _ -> error
- end.
-
-add_rosteritem(User, Server, JID, Nick, Group, Subscription) ->
- add_rosteritem(User, Server, JID, Nick, Group, Subscription, true).
-add_rosteritem(User, Server, JID, Nick, Group, Subscription, Push) ->
- {RU, RS, _} = jlib:jid_tolower(jlib:string_to_jid(JID)),
- LJID = {RU,RS,[]},
- Groups = case Group of
- [] -> [];
- _ -> [Group]
- end,
- Roster = #roster{
- usj = {User,Server,LJID},
- us = {User,Server},
- jid = LJID,
- name = Nick,
- ask = none,
- subscription = list_to_atom(Subscription),
- groups = Groups},
- Result =
- case roster_backend(Server) of
- mnesia ->
- mnesia:transaction(fun() ->
- case mnesia:read({roster,{User,Server,LJID}}) of
- [#roster{subscription=both}] ->
- already_added;
- _ ->
- mnesia:write(Roster)
- end
- end);
- odbc ->
- %% MREMOND: TODO: check if already_added
- case ejabberd_odbc:sql_transaction(Server,
- fun() ->
- Username = ejabberd_odbc:escape(User),
- SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)),
- case ejabberd_odbc:sql_query_t(
- ["select username from rosterusers "
- " where username='", Username, "' "
- " and jid='", SJID,
- "' and subscription = 'B';"]) of
- {selected, ["username"],[]} ->
- ItemVals = record_to_string(Roster),
- ItemGroups = groups_to_string(Roster),
- odbc_queries:update_roster(Server, Username,
- SJID, ItemVals,
- ItemGroups);
- _ ->
- already_added
- end
- end) of
- {atomic, already_added} -> {atomic, already_added};
- {atomic, _} -> {atomic, ok};
- Error -> Error
- end;
- none ->
- %% Apollo change: force roster push anyway with success
- {atomic, ok}
- end,
- case {Result, Push} of
- {{atomic, already_added}, _} -> ok; %% No need for roster push
- {{atomic, ok}, true} -> roster_push(User, Server, JID, Nick, Subscription);
- {{error, no_roster}, true} -> roster_push(User, Server, JID, Nick, Subscription);
- {{atomic, ok}, false} -> ok;
- _ -> error
- end,
- Result.
-
-del_rosteritem(User, Server, JID) ->
- del_rosteritem(User, Server, JID, true).
-del_rosteritem(User, Server, JID, Push) ->
- {RU, RS, _} = jlib:jid_tolower(jlib:string_to_jid(JID)),
- LJID = {RU,RS,[]},
- Result = case roster_backend(Server) of
- mnesia ->
- mnesia:transaction(fun() ->
- mnesia:delete({roster, {User,Server,LJID}})
- end);
- odbc ->
- case ejabberd_odbc:sql_transaction(Server, fun() ->
- Username = ejabberd_odbc:escape(User),
- SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)),
- odbc_queries:del_roster(Server, Username, SJID)
- end) of
- {atomic, _} -> {atomic, ok};
- Error -> Error
- end;
- none ->
- %% Apollo change: force roster push anyway with success
- {atomic, ok}
- end,
- case {Result, Push} of
- {{atomic, ok}, true} -> roster_push(User, Server, JID, "", "remove");
- %{{error, no_roster}, true} -> roster_push(User, Server, JID, "", "remove");
- {{atomic, ok}, false} -> ok;
- _ -> error
- end,
- Result.
-
-link_contacts(JID1, Nick1, JID2, Nick2) ->
- link_contacts(JID1, Nick1, JID2, Nick2, true).
-link_contacts(JID1, Nick1, JID2, Nick2, Push) ->
- link_contacts(JID1, Nick1, [], JID2, Nick2, [], Push).
-
-link_contacts(JID1, Nick1, Group1, JID2, Nick2, Group2) ->
- link_contacts(JID1, Nick1, Group1, JID2, Nick2, Group2, true).
-link_contacts(JID1, Nick1, Group1, JID2, Nick2, Group2, Push) ->
- {U1, S1, _} = jlib:jid_tolower(jlib:string_to_jid(JID1)),
- {U2, S2, _} = jlib:jid_tolower(jlib:string_to_jid(JID2)),
- case add_rosteritem(U1, S1, JID2, Nick2, Group1, "both", Push) of
- {atomic, ok} -> add_rosteritem(U2, S2, JID1, Nick1, Group2, "both", Push);
- Error -> Error
- end.
-
-unlink_contacts(JID1, JID2) ->
- unlink_contacts(JID1, JID2, true).
-unlink_contacts(JID1, JID2, Push) ->
- {U1, S1, _} = jlib:jid_tolower(jlib:string_to_jid(JID1)),
- {U2, S2, _} = jlib:jid_tolower(jlib:string_to_jid(JID2)),
- case del_rosteritem(U1, S1, JID2, Push) of
- {atomic, ok} -> del_rosteritem(U2, S2, JID1, Push);
- Error -> Error
- end.
-
-add_rosteritem_groups(User, Server, JID, NewGroups, Push) ->
- GroupsFun =
- fun(Groups) ->
- lists:usort(NewGroups ++ Groups)
- end,
- change_rosteritem_group(User, Server, JID, GroupsFun, Push).
-
-del_rosteritem_groups(User, Server, JID, NewGroups, Push) ->
- GroupsFun =
- fun(Groups) ->
- Groups -- NewGroups
- end,
- change_rosteritem_group(User, Server, JID, GroupsFun, Push).
-
-modify_rosteritem_groups(User, Server, JID2, NewGroups, Push, Nick, Subs) when NewGroups == [] ->
- JID1 = jlib:jid_to_string(jlib:make_jid(User, Server, "")),
- case unlink_contacts(JID1, JID2) of
- {atomic, ok} ->
- ok;
- Error ->
- Error
- end;
-modify_rosteritem_groups(User, Server, JID, NewGroups, Push, Nick, Subs) ->
- GroupsFun =
- fun(_Groups) ->
- NewGroups
- end,
- change_rosteritem_group(User, Server, JID, GroupsFun, Push, NewGroups, Nick, Subs).
-
-change_rosteritem_group(User, Server, JID, GroupsFun, Push) ->
- change_rosteritem_group(User, Server, JID, GroupsFun, Push, [], "unknownnickname", "both").
-
-change_rosteritem_group(User, Server, JID, GroupsFun, Push, NewGroups, Nick, Subs) ->
- {RU, RS, _} = jlib:jid_tolower(jlib:string_to_jid(JID)),
- LJID = {RU,RS,[]},
- LUser = jlib:nodeprep(User),
- LServer = jlib:nameprep(Server),
- Result =
- case roster_backend(LServer) of
- mnesia ->
- mnesia:transaction(
- fun() ->
- case mnesia:read({roster, {LUser, LServer, LJID}}) of
- [#roster{} = Roster] ->
- NewGroups = GroupsFun(Roster#roster.groups),
- NewRoster = Roster#roster{groups = NewGroups},
- mnesia:write(NewRoster),
- {ok, NewRoster#roster.name,
- NewRoster#roster.subscription,
- NewGroups};
- _ ->
- not_in_roster
- end
- end);
- odbc ->
- ejabberd_odbc:sql_transaction(
- LServer,
- fun() ->
- Username = ejabberd_odbc:escape(User),
- SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)),
- case ejabberd_odbc:sql_query_t(
- ["select nick, subscription from rosterusers "
- " where username='", Username, "' "
- " and jid='", SJID, "';"]) of
- {selected, ["nick", "subscription"],
- [{Name, SSubscription}]} ->
- Subscription =
- case SSubscription of
- "B" -> both;
- "T" -> to;
- "F" -> from;
- _ -> none
- end,
- Groups =
- case odbc_queries:get_roster_groups(
- LServer, Username, SJID) of
- {selected, ["grp"], JGrps}
- when is_list(JGrps) ->
- [JGrp || {JGrp} <- JGrps];
- _ ->
- []
- end,
- NewGroups = GroupsFun(Groups),
- ejabberd_odbc:sql_query_t(
- ["delete from rostergroups "
- " where username='", Username, "' "
- " and jid='", SJID, "';"]),
- lists:foreach(
- fun(Group) ->
- ejabberd_odbc:sql_query_t(
- ["insert into rostergroups("
- " username, jid, grp) "
- " values ('", Username, "',"
- "'", SJID, "',"
- "'", ejabberd_odbc:escape(Group), "');"])
- end,
- NewGroups),
- {ok, Name, Subscription, NewGroups};
- _ ->
- not_in_roster
- end
- end);
- none ->
- %% Apollo change: force roster push anyway with success
- {atomic, {ok, Nick, Subs, NewGroups}}
- end,
- case {Result, Push} of
- {{atomic, {ok, Name, Subscription, NewGroups}}, true} ->
- roster_push(User, Server, JID,
- Name, atom_to_list(Subscription), NewGroups),
- ok;
- {{atomic, {ok, _Name, _Subscription, _NewGroups}}, false} -> ok;
- {{atomic, not_in_roster}, _} -> not_in_roster;
- Error -> {error, Error}
- end.
-
-roster_push(User, Server, JID, Nick, Subscription) ->
- roster_push(User, Server, JID, Nick, Subscription, []).
-
-roster_push(User, Server, JID, Nick, Subscription, Groups) ->
- LJID = jlib:make_jid(User, Server, ""),
- TJID = jlib:string_to_jid(JID),
- {TU, TS, _} = jlib:jid_tolower(TJID),
- Presence = {xmlelement, "presence", [{"type",
- case Subscription of
- "remove" -> "unsubscribed";
- "none" -> "unsubscribe";
- "both" -> "subscribed";
- _ -> "subscribe"
- end}], []},
- ItemAttrs = case Nick of
- "" -> [{"jid", JID}, {"subscription", Subscription}];
- _ -> [{"jid", JID}, {"name", Nick}, {"subscription", Subscription}]
- end,
- ItemGroups =
- lists:map(fun(G) ->
- {xmlelement, "group", [], [{xmlcdata, G}]}
- end, Groups),
- Result =
- jlib:iq_to_xml(
- #iq{type = set, xmlns = ?NS_ROSTER, id = "push",
- sub_el = [{xmlelement, "query", [{"xmlns", ?NS_ROSTER}],
- [{xmlelement, "item", ItemAttrs, ItemGroups}]}]}),
- %% ejabberd_router:route(TJID, LJID, Presence),
- %% ejabberd_router:route(LJID, LJID, Result),
- lists:foreach(
- fun(Resource) ->
- UJID = jlib:make_jid(User, Server, Resource),
- ejabberd_router:route(TJID, UJID, Presence),
- ejabberd_router:route(UJID, UJID, Result),
- case Subscription of
- "remove" -> none;
- _ ->
- lists:foreach(
- fun(TR) ->
- ejabberd_router:route(
- jlib:make_jid(TU, TS, TR), UJID,
- {xmlelement, "presence", [], []})
- end, get_resources(TU, TS))
- end
- end, [R || R <- get_resources(User, Server)]).
-
-roster_backend(Server) ->
- Modules = gen_mod:loaded_modules(Server),
- Mnesia = lists:member(mod_roster, Modules),
- Odbc = lists:member(mod_roster_odbc, Modules),
- if Mnesia -> mnesia;
- true ->
- if Odbc -> odbc;
- true -> none
- end
- end.
-
-record_to_string(#roster{us = {User, _Server},
- jid = JID,
- name = Name,
- subscription = Subscription,
- ask = Ask,
- askmessage = AskMessage}) ->
- Username = ejabberd_odbc:escape(User),
- SJID = ejabberd_odbc:escape(jlib:jid_to_string(jlib:jid_tolower(JID))),
- Nick = ejabberd_odbc:escape(Name),
- SSubscription = case Subscription of
- both -> "B";
- to -> "T";
- from -> "F";
- none -> "N"
- end,
- SAsk = case Ask of
- subscribe -> "S";
- unsubscribe -> "U";
- both -> "B";
- out -> "O";
- in -> "I";
- none -> "N"
- end,
- SAskMessage = ejabberd_odbc:escape(AskMessage),
- [Username, SJID, Nick, SSubscription, SAsk, SAskMessage, "N", "", "item"].
-
-groups_to_string(#roster{us = {User, _Server},
- jid = JID,
- groups = Groups}) ->
- Username = ejabberd_odbc:escape(User),
- SJID = ejabberd_odbc:escape(jlib:jid_to_string(jlib:jid_tolower(JID))),
- %% Empty groups do not need to be converted to string to be inserted in
- %% the database
- lists:foldl(fun([], Acc) -> Acc;
- (Group, Acc) ->
- String = ["'", Username, "',"
- "'", SJID, "',"
- "'", ejabberd_odbc:escape(Group), "'"],
- [String|Acc]
- end, [], Groups).
-
-%% Format roster items as a list of:
-%% [{struct, [{jid, "test@localhost"},{group, "Friends"},{nick, "Nicktest"}]}]
-format_roster([]) ->
- [];
-format_roster(Items) ->
- format_roster(Items, []).
-format_roster([], Structs) ->
- Structs;
-format_roster([#roster{jid=JID, name=Nick, groups=Group,
- subscription=Subs, ask=Ask}|Items], Structs) ->
- {User,Server,_Resource} = JID,
- Struct = {struct, [{jid,lists:flatten([User,"@",Server])},
- {groups, extract_groups(Group)},
- {nick, Nick},
- {subscription, atom_to_list(Subs)},
- {pending, atom_to_list(Ask)}
- ]},
- format_roster(Items, [Struct|Structs]).
-
-%% Format roster items as a list of:
-%% [{struct, [{jid, "test@localhost"}, {resource, "Messenger"}, {group, "Friends"},
-%% {nick, "Nicktest"},{show, "available"}, {status, "Currently at office"}]}]
-%% Note: If user is connected several times, only keep the resource with the
-%% highest non-negative priority
-format_roster_with_presence([]) ->
- [];
-format_roster_with_presence(Items) ->
- format_roster_with_presence(Items, []).
-format_roster_with_presence([], Structs) ->
- Structs;
-format_roster_with_presence([#roster{jid=JID, name=Nick, groups=Group,
- subscription=Subs, ask=Ask}|Items], Structs) ->
- {User,Server,_R} = JID,
- Presence = case Subs of
- both -> get_presence(User, Server);
- from -> get_presence(User, Server);
- _Other -> {"", "unavailable", ""}
- end,
- {Resource, Show, Status} =
- case Presence of
- {_R, "invisible", _S} -> {"", "unavailable", ""};
- _Status -> Presence
- end,
- Struct = {struct, [{jid,lists:flatten([User,"@",Server])},
- {resource, Resource},
- {group, extract_group(Group)},
- {nick, Nick},
- {subscription, atom_to_list(Subs)},
- {pending, atom_to_list(Ask)},
- {show, Show},
- {status, Status}
- ]},
- format_roster_with_presence(Items, [Struct|Structs]).
-
-extract_group([]) -> [];
-%extract_group([Group|_Groups]) -> Group.
-extract_group(Groups) -> string:join(Groups, ";").
-
-extract_groups([]) -> [];
-%extract_groups([Group|_Groups]) -> Group.
-extract_groups(Groups) -> {array, Groups}.
-
-%% -----------------------------
-%% Internal session handling
-%% -----------------------------
-
-%% This is inspired from ejabberd_sm.erl
-get_presence(User, Server) ->
- case get_sessions(User, Server) of
- [] ->
- {"", "unavailable", ""};
- Ss ->
- Session = hd(Ss),
- if Session#session.priority >= 0 ->
- Pid = element(2, Session#session.sid),
- %{_User, _Resource, Show, Status} = rpc:call(node(Pid), ejabberd_c2s, get_presence, [Pid]),
- {_User, Resource, Show, Status} = ejabberd_c2s:get_presence(Pid),
- {Resource, Show, Status};
- true ->
- {"", "unavailable", ""}
- end
- end.
-
-get_resources(User, Server) ->
- lists:map(fun(S) -> element(3, S#session.usr)
- end, get_sessions(User, Server)).
-
-get_sessions(User, Server) ->
- US = {jlib:nodeprep(User), jlib:nameprep(Server)},
- Node = ejabberd_cluster:get_node(US),
- case catch rpc:call(Node, mnesia, dirty_index_read,
- [session, US, #session.us], 5000) of
- Result when is_list(Result), Result /= [] ->
- lists:reverse(lists:keysort(#session.priority, clean_session_list(Result)));
- _ ->
- []
- end.
-
-clean_session_list(Ss) ->
- clean_session_list(lists:keysort(#session.usr, Ss), []).
-
-clean_session_list([], Res) ->
- Res;
-clean_session_list([S], Res) ->
- [S | Res];
-clean_session_list([S1, S2 | Rest], Res) ->
- if
- S1#session.usr == S2#session.usr ->
- if
- S1#session.sid > S2#session.sid ->
- clean_session_list([S1 | Rest], Res);
- true ->
- clean_session_list([S2 | Rest], Res)
- end;
- true ->
- clean_session_list([S2 | Rest], [S1 | Res])
- end.
-
-
-%% -----------------------------
-%% Internal function pattern
-%% -----------------------------
-
-user_action(User, Server, Fun, OK) ->
- case ejabberd_auth:is_user_exists(User, Server) of
- true ->
- case catch Fun() of
- OK ->
- {false, {response, [0]}};
- _ ->
- {false, {response, [1]}}
- end;
- false ->
- {false, {response, [404]}}
- end.