From 1c00a9713d575fc505ef1e89ab76471985bb452a Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 27 Apr 2010 12:33:56 +0200 Subject: Automatic vcard avatar addition in presence (thanks to Igor Goryachev)(EJAB-208) --- src/ejabberd_c2s.erl | 19 ++++++++ src/ejabberd_sm.erl | 11 +++++ src/jlib.hrl | 1 + src/mod_vcard.erl | 3 +- src/mod_vcard_odbc.erl | 4 +- src/mod_vcard_xupdate.erl | 121 ++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 157 insertions(+), 2 deletions(-) create mode 100644 src/mod_vcard_xupdate.erl (limited to 'src') diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index 04a3fbcb1..b09d41433 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -1332,6 +1332,25 @@ handle_info(system_shutdown, StateName, StateData) -> ok end, {stop, normal, StateData}; +handle_info({force_update_presence, LUser}, StateName, + #state{user = LUser, server = LServer} = StateData) -> + NewStateData = + case StateData#state.pres_last of + {xmlelement, "presence", _Attrs, _Els} -> + PresenceEl = ejabberd_hooks:run_fold( + c2s_update_presence, + LServer, + StateData#state.pres_last, + [LUser, LServer]), + StateData2 = StateData#state{pres_last = PresenceEl}, + presence_update(StateData2#state.jid, + PresenceEl, + StateData2), + StateData2; + _ -> + StateData + end, + {next_state, StateName, NewStateData}; handle_info(Info, StateName, StateData) -> ?ERROR_MSG("Unexpected info: ~p", [Info]), fsm_next_state(StateName, StateData). diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl index 296400f84..782732603 100644 --- a/src/ejabberd_sm.erl +++ b/src/ejabberd_sm.erl @@ -47,6 +47,7 @@ register_iq_handler/4, register_iq_handler/5, unregister_iq_handler/2, + force_update_presence/1, connected_users/0, connected_users_number/0, user_resources/2, @@ -711,6 +712,16 @@ process_iq(From, To, Packet) -> ok end. +force_update_presence({LUser, _LServer} = US) -> + case catch mnesia:dirty_index_read(session, US, #session.us) of + {'EXIT', _Reason} -> + ok; + Ss -> + lists:foreach(fun(#session{sid = {_, Pid}}) -> + Pid ! {force_update_presence, LUser} + end, Ss) + end. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% ejabberd commands diff --git a/src/jlib.hrl b/src/jlib.hrl index 8047c1a82..47c65f68a 100644 --- a/src/jlib.hrl +++ b/src/jlib.hrl @@ -22,6 +22,7 @@ -define(NS_DISCO_ITEMS, "http://jabber.org/protocol/disco#items"). -define(NS_DISCO_INFO, "http://jabber.org/protocol/disco#info"). -define(NS_VCARD, "vcard-temp"). +-define(NS_VCARD_UPDATE, "vcard-temp:x:update"). -define(NS_AUTH, "jabber:iq:auth"). -define(NS_AUTH_ERROR, "jabber:iq:auth:error"). -define(NS_REGISTER, "jabber:iq:register"). diff --git a/src/mod_vcard.erl b/src/mod_vcard.erl index 4384087f4..3cdd3cffa 100644 --- a/src/mod_vcard.erl +++ b/src/mod_vcard.erl @@ -267,7 +267,8 @@ set_vcard(User, LServer, VCARD) -> orgunit = OrgUnit, lorgunit = LOrgUnit }) end, - mnesia:transaction(F) + mnesia:transaction(F), + ejabberd_hooks:run(vcard_set, LServer, [LUser, LServer, VCARD]) end. -define(TLFIELD(Type, Label, Var), diff --git a/src/mod_vcard_odbc.erl b/src/mod_vcard_odbc.erl index 30c28883a..aa35bd4be 100644 --- a/src/mod_vcard_odbc.erl +++ b/src/mod_vcard_odbc.erl @@ -247,7 +247,9 @@ set_vcard(User, LServer, VCARD) -> SLLocality, SLMiddle, SLNickname, SLOrgName, SLOrgUnit, SLocality, SMiddle, SNickname, SOrgName, - SOrgUnit, SVCARD, Username) + SOrgUnit, SVCARD, Username), + + ejabberd_hooks:run(vcard_set, LServer, [LUser, LServer, VCARD]) end. -define(TLFIELD(Type, Label, Var), diff --git a/src/mod_vcard_xupdate.erl b/src/mod_vcard_xupdate.erl new file mode 100644 index 000000000..38503c4c3 --- /dev/null +++ b/src/mod_vcard_xupdate.erl @@ -0,0 +1,121 @@ +%%%---------------------------------------------------------------------- +%%% File : mod_vcard_xupdate.erl +%%% Author : Igor Goryachev +%%% Purpose : Add avatar hash in presence on behalf of client (XEP-0153) +%%% Created : 9 Mar 2007 by Igor Goryachev +%%%---------------------------------------------------------------------- + +-module(mod_vcard_xupdate). + +-behaviour(gen_mod). + +%% gen_mod callbacks +-export([start/2, + stop/1]). + +%% hooks +-export([update_presence/3, + vcard_set/3]). + +-include("ejabberd.hrl"). +-include("jlib.hrl"). + +-record(vcard_xupdate, {us, hash}). + +%%==================================================================== +%% gen_mod callbacks +%%==================================================================== + +start(Host, _Opts) -> + mnesia:create_table(vcard_xupdate, + [{disc_copies, [node()]}, + {attributes, record_info(fields, vcard_xupdate)}]), + ejabberd_hooks:add(c2s_update_presence, Host, + ?MODULE, update_presence, 100), + ejabberd_hooks:add(vcard_set, Host, + ?MODULE, vcard_set, 100), + ok. + +stop(Host) -> + ejabberd_hooks:delete(c2s_update_presence, Host, + ?MODULE, update_presence, 100), + ejabberd_hooks:delete(vcard_set, Host, + ?MODULE, vcard_set, 100), + ok. + +%%==================================================================== +%% Hooks +%%==================================================================== + +update_presence({xmlelement, "presence", Attrs, _Els} = Packet, User, Host) -> + case xml:get_attr_s("type", Attrs) of + [] -> + presence_with_xupdate(Packet, User, Host); + _ -> + Packet + end; +update_presence(Packet, _User, _Host) -> + Packet. + +vcard_set(LUser, LServer, VCARD) -> + US = {LUser, LServer}, + case xml:get_path_s(VCARD, [{elem, "PHOTO"}, {elem, "BINVAL"}, cdata]) of + [] -> + remove_xupdate(LUser, LServer); + BinVal -> + add_xupdate(LUser, LServer, sha:sha(jlib:decode_base64(BinVal))) + end, + ejabberd_sm:force_update_presence(US). + +%%==================================================================== +%% Mnesia storage +%%==================================================================== + +add_xupdate(LUser, LServer, Hash) -> + F = fun() -> + mnesia:write(#vcard_xupdate{us = {LUser, LServer}, hash = Hash}) + end, + mnesia:transaction(F). + +get_xupdate(LUser, LServer) -> + case mnesia:dirty_read(vcard_xupdate, {LUser, LServer}) of + [#vcard_xupdate{hash = Hash}] -> + Hash; + _ -> + undefined + end. + +remove_xupdate(LUser, LServer) -> + F = fun() -> + mnesia:delete({vcard_xupdate, {LUser, LServer}}) + end, + mnesia:transaction(F). + +%%%---------------------------------------------------------------------- +%%% Presence stanza rebuilding +%%%---------------------------------------------------------------------- + +presence_with_xupdate({xmlelement, "presence", Attrs, Els}, User, Host) -> + XPhotoEl = build_xphotoel(User, Host), + Els2 = presence_with_xupdate2(Els, [], XPhotoEl), + {xmlelement, "presence", Attrs, Els2}. + +presence_with_xupdate2([], Els2, XPhotoEl) -> + lists:reverse([XPhotoEl | Els2]); +%% This clause assumes that the x element contains only the XMLNS attribute: +presence_with_xupdate2([{xmlelement, "x", [{"xmlns", ?NS_VCARD_UPDATE}], _} + | Els], Els2, XPhotoEl) -> + presence_with_xupdate2(Els, Els2, XPhotoEl); +presence_with_xupdate2([El | Els], Els2, XPhotoEl) -> + presence_with_xupdate2(Els, [El | Els2], XPhotoEl). + +build_xphotoel(User, Host) -> + Hash = get_xupdate(User, Host), + PhotoSubEls = case Hash of + Hash when is_list(Hash) -> + [{xmlcdata, Hash}]; + _ -> + [] + end, + PhotoEl = [{xmlelement, "photo", [], PhotoSubEls}], + {xmlelement, "x", [{"xmlns", ?NS_VCARD_UPDATE}], PhotoEl}. -- cgit v1.2.3