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 t_concat
= table.concat
;
15 local tonumber = tonumber;
16 local pairs
, ipairs
= pairs
, ipairs
;
18 local rm_load_roster
= require
"core.rostermanager".load_roster
;
19 local rm_remove_from_roster
= require
"core.rostermanager".remove_from_roster
;
20 local rm_add_to_roster
= require
"core.rostermanager".add_to_roster
;
21 local rm_roster_push
= require
"core.rostermanager".roster_push
;
22 local core_post_stanza
= prosody
.core_post_stanza
;
24 module
:add_feature("jabber:iq:roster");
26 local rosterver_stream_feature
= st
.stanza("ver", {xmlns
="urn:xmpp:features:rosterver"});
27 module
:hook("stream-features", function(event
)
28 local origin
, features
= event
.origin
, event
.features
;
29 if origin
.username
then
30 features
:add_child(rosterver_stream_feature
);
34 module
:hook("iq/self/jabber:iq:roster:query", function(event
)
35 local session
, stanza
= event
.origin
, event
.stanza
;
37 if stanza
.attr
.type == "get" then
38 local roster
= st
.reply(stanza
);
40 local client_ver
= tonumber(stanza
.tags
[1].attr
.ver
);
41 local server_ver
= tonumber(session
.roster
[false].version
or 1);
43 if not (client_ver
and server_ver
) or client_ver
~= server_ver
then
44 roster
:query("jabber:iq:roster");
45 -- Client does not support versioning, or has stale roster
46 for jid
, item
in pairs(session
.roster
) do
47 if jid
~= "pending" and jid
then
50 subscription
= item
.subscription
,
54 for group
in pairs(item
.groups
) do
55 roster
:tag("group"):text(group
):up();
57 roster
:up(); -- move out from item
60 roster
.tags
[1].attr
.ver
= server_ver
;
63 session
.interested
= true; -- resource is interested in roster updates
64 else -- stanza.attr.type == "set"
65 local query
= stanza
.tags
[1];
66 if #query
.tags
== 1 and query
.tags
[1].name
== "item"
67 and query
.tags
[1].attr
.xmlns
== "jabber:iq:roster" and query
.tags
[1].attr
.jid
68 -- Protection against overwriting roster.pending, until we move it
69 and query
.tags
[1].attr
.jid
~= "pending" then
70 local item
= query
.tags
[1];
71 local from_node
, from_host
= jid_split(stanza
.attr
.from
);
72 local jid
= jid_prep(item
.attr
.jid
);
73 local node
, host
, resource
= jid_split(jid
);
74 if not resource
and host
then
75 if jid
~= from_node
.."@"..from_host
then
76 if item
.attr
.subscription
== "remove" then
77 local roster
= session
.roster
;
78 local r_item
= roster
[jid
];
80 local to_bare
= node
and (node
.."@"..host
) or host
; -- bare JID
81 if r_item
.subscription
== "both" or r_item
.subscription
== "from" or (roster
.pending
and roster
.pending
[jid
]) then
82 core_post_stanza(session
, st
.presence({type="unsubscribed", from
=session
.full_jid
, to
=to_bare
}));
84 if r_item
.subscription
== "both" or r_item
.subscription
== "to" or r_item
.ask
then
85 core_post_stanza(session
, st
.presence({type="unsubscribe", from
=session
.full_jid
, to
=to_bare
}));
87 local success
, err_type
, err_cond
, err_msg
= rm_remove_from_roster(session
, jid
);
89 session
.send(st
.reply(stanza
));
90 rm_roster_push(from_node
, from_host
, jid
);
92 session
.send(st
.error_reply(stanza
, err_type
, err_cond
, err_msg
));
95 session
.send(st
.error_reply(stanza
, "modify", "item-not-found"));
98 local r_item
= {name
= item
.attr
.name
, groups
= {}};
99 if r_item
.name
== "" then r_item
.name
= nil; end
100 if session
.roster
[jid
] then
101 r_item
.subscription
= session
.roster
[jid
].subscription
;
102 r_item
.ask
= session
.roster
[jid
].ask
;
104 r_item
.subscription
= "none";
106 for _
, child
in ipairs(item
) do
107 if child
.name
== "group" then
108 local text
= t_concat(child
);
109 if text
and text
~= "" then
110 r_item
.groups
[text
] = true;
114 local success
, err_type
, err_cond
, err_msg
= rm_add_to_roster(session
, jid
, r_item
);
117 session
.send(st
.reply(stanza
));
118 -- and push change to all resources
119 rm_roster_push(from_node
, from_host
, jid
);
121 -- Adding to roster failed
122 session
.send(st
.error_reply(stanza
, err_type
, err_cond
, err_msg
));
126 -- Trying to add self to roster
127 session
.send(st
.error_reply(stanza
, "cancel", "not-allowed"));
130 -- Invalid JID added to roster
131 session
.send(st
.error_reply(stanza
, "modify", "bad-request")); -- FIXME what's the correct error?
134 -- Roster set didn't include a single item, or its name wasn't 'item'
135 session
.send(st
.error_reply(stanza
, "modify", "bad-request"));
141 module
:hook_global("user-deleted", function(event
)
142 local username
, host
= event
.username
, event
.host
;
143 if host
~= module
.host
then return end
144 local bare
= username
.. "@" .. host
;
145 local roster
= rm_load_roster(username
, host
);
146 for jid
, item
in pairs(roster
) do
147 if jid
and jid
~= "pending" then
148 if item
.subscription
== "both" or item
.subscription
== "from" or (roster
.pending
and roster
.pending
[jid
]) then
149 module
:send(st
.presence({type="unsubscribed", from
=bare
, to
=jid
}));
151 if item
.subscription
== "both" or item
.subscription
== "to" or item
.ask
then
152 module
:send(st
.presence({type="unsubscribe", from
=bare
, to
=jid
}));