2 -- Copyright (C) 2008-2010 Matthew Wild
3 -- Copyright (C) 2008-2010 Waqas Hussain
5 -- This project is MIT/X11 licensed. Please see the
6 -- COPYING file in the source package for more information.
10 local st
= require
"util.stanza"
12 local jid_split
= require
"util.jid".split
;
13 local jid_prep
= require
"util.jid".prep
;
14 local tonumber = tonumber;
17 local rm_load_roster
= require
"core.rostermanager".load_roster
;
18 local rm_remove_from_roster
= require
"core.rostermanager".remove_from_roster
;
19 local rm_add_to_roster
= require
"core.rostermanager".add_to_roster
;
20 local rm_roster_push
= require
"core.rostermanager".roster_push
;
22 module
:add_feature("jabber:iq:roster");
24 local rosterver_stream_feature
= st
.stanza("ver", {xmlns
="urn:xmpp:features:rosterver"});
25 module
:hook("stream-features", function(event
)
26 local origin
, features
= event
.origin
, event
.features
;
27 if origin
.username
then
28 features
:add_child(rosterver_stream_feature
);
32 module
:hook("iq/self/jabber:iq:roster:query", function(event
)
33 local session
, stanza
= event
.origin
, event
.stanza
;
35 if stanza
.attr
.type == "get" then
36 local roster
= st
.reply(stanza
);
38 local client_ver
= tonumber(stanza
.tags
[1].attr
.ver
);
39 local server_ver
= tonumber(session
.roster
[false].version
or 1);
41 if not (client_ver
and server_ver
) or client_ver
~= server_ver
then
42 roster
:query("jabber:iq:roster");
43 -- Client does not support versioning, or has stale roster
44 for jid
, item
in pairs(session
.roster
) do
48 subscription
= item
.subscription
,
52 for group
in pairs(item
.groups
) do
53 roster
:text_tag("group", group
);
55 roster
:up(); -- move out from item
58 roster
.tags
[1].attr
.ver
= server_ver
;
61 session
.interested
= true; -- resource is interested in roster updates
62 else -- stanza.attr.type == "set"
63 local query
= stanza
.tags
[1];
64 if #query
.tags
== 1 and query
.tags
[1].name
== "item"
65 and query
.tags
[1].attr
.xmlns
== "jabber:iq:roster" and query
.tags
[1].attr
.jid
then
66 local item
= query
.tags
[1];
67 local from_node
, from_host
= jid_split(stanza
.attr
.from
);
68 local jid
= jid_prep(item
.attr
.jid
);
69 local node
, host
, resource
= jid_split(jid
);
70 if not resource
and host
then
71 if jid
~= from_node
.."@"..from_host
then
72 if item
.attr
.subscription
== "remove" then
73 local roster
= session
.roster
;
74 local r_item
= roster
[jid
];
76 module
:fire_event("roster-item-removed", {
77 username
= node
, jid
= jid
, item
= r_item
, origin
= session
, roster
= roster
,
79 local success
, err_type
, err_cond
, err_msg
= rm_remove_from_roster(session
, jid
);
81 session
.send(st
.reply(stanza
));
82 rm_roster_push(from_node
, from_host
, jid
);
84 session
.send(st
.error_reply(stanza
, err_type
, err_cond
, err_msg
));
87 session
.send(st
.error_reply(stanza
, "modify", "item-not-found"));
90 local r_item
= {name
= item
.attr
.name
, groups
= {}};
91 if r_item
.name
== "" then r_item
.name
= nil; end
92 if session
.roster
[jid
] then
93 r_item
.subscription
= session
.roster
[jid
].subscription
;
94 r_item
.ask
= session
.roster
[jid
].ask
;
96 r_item
.subscription
= "none";
98 for group
in item
:childtags("group") do
99 local text
= group
:get_text();
101 r_item
.groups
[text
] = true;
104 local success
, err_type
, err_cond
, err_msg
= rm_add_to_roster(session
, jid
, r_item
);
107 session
.send(st
.reply(stanza
));
108 -- and push change to all resources
109 rm_roster_push(from_node
, from_host
, jid
);
111 -- Adding to roster failed
112 session
.send(st
.error_reply(stanza
, err_type
, err_cond
, err_msg
));
116 -- Trying to add self to roster
117 session
.send(st
.error_reply(stanza
, "cancel", "not-allowed"));
120 -- Invalid JID added to roster
121 session
.send(st
.error_reply(stanza
, "modify", "bad-request")); -- FIXME what's the correct error?
124 -- Roster set didn't include a single item, or its name wasn't 'item'
125 session
.send(st
.error_reply(stanza
, "modify", "bad-request"));
131 module
:hook_global("user-deleted", function(event
)
132 local username
, host
= event
.username
, event
.host
;
133 local origin
= event
.origin
or prosody
.hosts
[host
];
134 if host
~= module
.host
then return end
135 local roster
= rm_load_roster(username
, host
);
136 for jid
, item
in pairs(roster
) do
138 module
:fire_event("roster-item-removed", {
139 username
= username
, jid
= jid
, item
= item
, roster
= roster
, origin
= origin
,
142 for pending_jid
in pairs(item
.pending
) do
143 module
:fire_event("roster-item-removed", {
144 username
= username
, jid
= pending_jid
, roster
= roster
, origin
= origin
,