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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
|
-module(ephraim_roster).
-compile([debug_info, export_all]).
-include_lib("exmpp/include/exmpp.hrl").
-record(roster, {
entries :: dict()
}).
-record(avatar, {
data :: binary()
}).
-record(roster_entry, {
name :: binary() | undefined,
subscription :: atom() | undefined,
groups :: set(),
resources :: dict(),
avatar :: #avatar{} | undefined
}).
-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,
NewEntry = RosterEntry#roster_entry{resources=Resources},
Entries = dict:store(BareJID, NewEntry, Roster#roster.entries),
uiUpdate(BareJID, NewEntry),
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),
uiUpdate(JID, NewEntry),
Roster#roster{entries=Entries}.
-spec updateAvatar(#roster{}, binary(), binary()) -> #roster{}.
updateAvatar(Roster, JID, Avatar) ->
RosterEntry = case dict:find(JID, Roster#roster.entries) of
{ok, Entry} ->
Entry;
error ->
#roster_entry{resources=dict:new()}
end,
NewEntry = RosterEntry#roster_entry{avatar=#avatar{data=Avatar}},
Entries = dict:store(JID, NewEntry, Roster#roster.entries),
uiUpdate(JID, NewEntry),
Roster#roster{entries=Entries}.
-spec uiUpdate(binary(), #roster_entry{}) -> ok.
uiUpdate(JID, RosterEntry) ->
if RosterEntry#roster_entry.subscription =:= undefined ->
ok;
true ->
ephraim ! {ui_update, {roster_update, JID, RosterEntry#roster_entry.name, RosterEntry#roster_entry.subscription,
sets:to_list(RosterEntry#roster_entry.groups), dict:to_list(RosterEntry#roster_entry.resources),
RosterEntry#roster_entry.avatar}},
ok
end.
-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);
{avatar, From, Avatar} ->
Roster2 = updateAvatar(Roster, From, Avatar),
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.
|