-module(ephraim_roster). -compile([debug_info, export_all]). -include_lib("exmpp/include/exmpp.hrl"). -record(roster, { entries :: dict() }). -record(roster_entry, { name :: binary() | undefined, subscription :: atom() | undefined, groups :: set(), resources :: dict() }). -record(resource_entry, { priority :: integer(), show :: atom(), status :: binary() }). -spec init() -> ok. init() -> loop(#roster{entries=dict:new()}). -spec updateResource(#roster{}, binary(), integer(), atom(), atom(), binary()) -> #roster{}. updateResource(Roster, JID, Priority, Type, Show, Status) -> {Node, Domain, Resource} = exmpp_jid:to_lower(exmpp_jid:parse(JID)), BareJID = list_to_binary([Node, <<"@">>, Domain]), RosterEntry = case dict:find(BareJID, Roster#roster.entries) of {ok, Entry} -> Entry; error -> #roster_entry{resources=dict:new()} end, Resources = case Type of available -> ResourceEntry = #resource_entry{priority=Priority,show=Show,status=Status}, dict:store(Resource, ResourceEntry, RosterEntry#roster_entry.resources); _ -> RosterEntry#roster_entry.resources end, Entries = dict:store(BareJID, RosterEntry#roster_entry{resources=Resources}, Roster#roster.entries), if RosterEntry#roster_entry.subscription =:= undefined -> ok; true -> ephraim ! {ui_update, {roster_update, BareJID, dict:to_list(Resources)}} end, Roster#roster{entries=Entries}. -spec updateRosterEntry(#roster{}, binary(), binary(), atom(), set()) -> #roster{}. updateRosterEntry(Roster, JID, Name, Subscription, Groups) -> Resources = case dict:find(JID, Roster#roster.entries) of {ok, Entry} -> Entry#roster_entry.resources; error -> dict:new() end, NewEntry = #roster_entry{subscription=Subscription,name=Name,resources=Resources,groups=Groups}, Entries = dict:store(JID, NewEntry, Roster#roster.entries), ephraim ! {ui_update, {roster_update, JID, Name, Subscription, sets:to_list(Groups), dict:to_list(Resources)}}, Roster#roster{entries=Entries}. -spec getGroups([#xmlel{}]) -> set(). getGroups(Els) -> getGroups(sets:new(), Els). -spec getGroups(set(), [#xmlel{}]) -> set(). getGroups(Groups, []) -> Groups; getGroups(Groups, [#xmlel{ns='jabber:iq:roster',name=group,children=[#xmlcdata{cdata=Group}]}|Rest]) -> getGroups(sets:add_element(Group, Groups), Rest); getGroups(Groups, [_|Rest]) -> getGroups(Groups, Rest). -spec handleRosterIQ(#roster{}, #xmlel{}) -> #roster{}. handleRosterIQ(Roster, Item) -> JID = exmpp_xml:get_attribute(Item, jid, undefined), Name = exmpp_xml:get_attribute(Item, name, undefined), Groups = getGroups(Item#xmlel.children), Subscription = binary_to_atom(exmpp_xml:get_attribute(Item, subscription, undefined), utf8), updateRosterEntry(Roster, JID, Name, Subscription, Groups). -spec handleRosterIQs(#roster{}, [#xmlel{}]) -> #roster{}. handleRosterIQs(Roster, []) -> Roster; handleRosterIQs(Roster, [Item = #xmlel{ns='jabber:iq:roster',name=item}|Rest]) -> Roster2 = handleRosterIQ(Roster, Item), handleRosterIQs(Roster2, Rest); handleRosterIQs(Roster, [_|Rest]) -> handleRosterIQs(Roster, Rest). -spec loop(#roster{}) -> ok. loop(Roster) -> receive stop -> ok; {presence, Packet} -> From = exmpp_xml:get_attribute(Packet, from, <<"unknown">>), Priority = exmpp_presence:get_priority(Packet), Type = exmpp_presence:get_type(Packet), Show = exmpp_presence:get_show(Packet), Status = exmpp_presence:get_status(Packet), Roster2 = updateResource(Roster, From, Priority, Type, Show, Status), loop(Roster2); {roster_iq, Payload} -> Roster2 = handleRosterIQs(Roster, Payload#xmlel.children), %io:format("ephraim_roster: IQ: ~p~n", [Payload]), loop(Roster2); Msg -> io:format("ephraim_roster: ~p~n", [Msg]), loop(Roster) end.