summaryrefslogtreecommitdiffstats
path: root/src/core/ephraim_roster.erl
blob: bfc40b1a3b65960f36bc9c549809251d1c89647c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
-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);
		    unavailable ->
			dict:erase(Resource, 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, RosterEntry#roster_entry.name, RosterEntry#roster_entry.subscription,
				   sets:to_list(RosterEntry#roster_entry.groups), 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.