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
|
-- Prosody IM
-- Copyright (C) 2008-2009 Matthew Wild
-- Copyright (C) 2008-2009 Waqas Hussain
--
-- This project is MIT/X11 licensed. Please see the
-- COPYING file in the source package for more information.
--
if module:get_host_type() ~= "component" then
error("MUC should be loaded as a component, please see http://prosody.im/doc/components", 0);
end
local muc_host = module:get_host();
local muc_name = module:get_option("name");
if type(muc_name) ~= "string" then muc_name = "Prosody Chatrooms"; end
local history_length = 20;
local muc_new_room = module:require "muc".new_room;
local register_component = require "core.componentmanager".register_component;
local deregister_component = require "core.componentmanager".deregister_component;
local jid_split = require "util.jid".split;
local st = require "util.stanza";
local uuid_gen = require "util.uuid".generate;
local datamanager = require "util.datamanager";
local rooms = {};
local persistent_rooms = datamanager.load(nil, muc_host, "persistent") or {};
local component;
local function room_route_stanza(room, stanza) core_post_stanza(component, stanza); end
local function room_save(room, forced)
local node = jid_split(room.jid);
persistent_rooms[room.jid] = room._data.persistent;
if room._data.persistent then
local history = room._data.history;
room._data.history = nil;
local data = {
jid = room.jid;
_data = room._data;
_affiliations = room._affiliations;
};
datamanager.store(node, muc_host, "config", data);
room._data.history = history;
elseif forced then
datamanager.store(node, muc_host, "config", nil);
end
if forced then datamanager.store(nil, muc_host, "persistent", persistent_rooms); end
end
for jid in pairs(persistent_rooms) do
local node = jid_split(jid);
local data = datamanager.load(node, muc_host, "config") or {};
local room = muc_new_room(jid);
room._data = data._data;
room._affiliations = data._affiliations;
room.route_stanza = room_route_stanza;
room.save = room_save;
rooms[jid] = room;
end
local host_room = muc_new_room(muc_host);
host_room.route_stanza = room_route_stanza;
host_room.save = room_save;
local function get_disco_info(stanza)
return st.iq({type='result', id=stanza.attr.id, from=muc_host, to=stanza.attr.from}):query("http://jabber.org/protocol/disco#info")
:tag("identity", {category='conference', type='text', name=muc_name}):up()
:tag("feature", {var="http://jabber.org/protocol/muc"}); -- TODO cache disco reply
end
local function get_disco_items(stanza)
local reply = st.iq({type='result', id=stanza.attr.id, from=muc_host, to=stanza.attr.from}):query("http://jabber.org/protocol/disco#items");
for jid, room in pairs(rooms) do
if not room._data.hidden then
reply:tag("item", {jid=jid, name=jid}):up();
end
end
return reply; -- TODO cache disco reply
end
local function handle_to_domain(origin, stanza)
local type = stanza.attr.type;
if type == "error" or type == "result" then return; end
if stanza.name == "iq" and type == "get" then
local xmlns = stanza.tags[1].attr.xmlns;
if xmlns == "http://jabber.org/protocol/disco#info" then
origin.send(get_disco_info(stanza));
elseif xmlns == "http://jabber.org/protocol/disco#items" then
origin.send(get_disco_items(stanza));
elseif xmlns == "http://jabber.org/protocol/muc#unique" then
origin.send(st.reply(stanza):tag("unique", {xmlns = xmlns}):text(uuid_gen())); -- FIXME Random UUIDs can theoretically have collisions
else
origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); -- TODO disco/etc
end
else
host_room:handle_stanza(origin, stanza);
--origin.send(st.error_reply(stanza, "cancel", "service-unavailable", "The muc server doesn't deal with messages and presence directed at it"));
end
end
component = register_component(muc_host, function(origin, stanza)
local to_node, to_host, to_resource = jid_split(stanza.attr.to);
if to_node then
local bare = to_node.."@"..to_host;
if to_host == muc_host or bare == muc_host then
local room = rooms[bare];
if not room then
room = muc_new_room(bare);
room.route_stanza = room_route_stanza;
room.save = room_save;
rooms[bare] = room;
end
room:handle_stanza(origin, stanza);
if not next(room._occupants) and not persistent_rooms[room.jid] then -- empty, non-persistent room
rooms[bare] = nil; -- discard room
end
else --[[not for us?]] end
return;
end
-- to the main muc domain
handle_to_domain(origin, stanza);
end);
function component.send(stanza) -- FIXME do a generic fix
if stanza.attr.type == "result" or stanza.attr.type == "error" then
core_post_stanza(component, stanza);
else error("component.send only supports result and error stanzas at the moment"); end
end
prosody.hosts[module:get_host()].muc = { rooms = rooms };
module.unload = function()
deregister_component(muc_host);
end
module.save = function()
return {rooms = rooms};
end
module.restore = function(data)
rooms = {};
for jid, oldroom in pairs(data.rooms or {}) do
local room = muc_new_room(jid);
room._jid_nick = oldroom._jid_nick;
room._occupants = oldroom._occupants;
room._data = oldroom._data;
room._affiliations = oldroom._affiliations;
room.route_stanza = room_route_stanza;
room.save = room_save;
rooms[jid] = room;
end
prosody.hosts[module:get_host()].muc = { rooms = rooms };
end
|