89 return st.iq({type='result', id=stanza.attr.id, from=stanza.attr.to, to=stanza.attr.from}):query("http://jabber.org/protocol/disco#items"); |
90 return st.iq({type='result', id=stanza.attr.id, from=stanza.attr.to, to=stanza.attr.from}):query("http://jabber.org/protocol/disco#items"); |
90 end -- TODO allow non-private rooms]] |
91 end -- TODO allow non-private rooms]] |
91 |
92 |
92 -- |
93 -- |
93 |
94 |
94 local function room_broadcast_presence(room, stanza, code, nick) |
95 local room_mt = {}; |
|
96 |
|
97 local function room_mt:broadcast_presence(stanza, code, nick) |
95 stanza = get_filtered_presence(stanza); |
98 stanza = get_filtered_presence(stanza); |
96 local data = room._participants[stanza.attr.from]; |
99 local data = self._participants[stanza.attr.from]; |
97 stanza:tag("x", {xmlns='http://jabber.org/protocol/muc#user'}) |
100 stanza:tag("x", {xmlns='http://jabber.org/protocol/muc#user'}) |
98 :tag("item", {affiliation=data.affiliation, role=data.role, nick=nick}):up(); |
101 :tag("item", {affiliation=data.affiliation, role=data.role, nick=nick}):up(); |
99 if code then |
102 if code then |
100 stanza:tag("status", {code=code}):up(); |
103 stanza:tag("status", {code=code}):up(); |
101 end |
104 end |
102 local me; |
105 local me; |
103 for occupant, o_data in pairs(room._participants) do |
106 for occupant, o_data in pairs(self._participants) do |
104 if occupant ~= stanza.attr.from then |
107 if occupant ~= stanza.attr.from then |
105 for jid in pairs(o_data.sessions) do |
108 for jid in pairs(o_data.sessions) do |
106 stanza.attr.to = jid; |
109 stanza.attr.to = jid; |
107 room:route_stanza(stanza); |
110 self:route_stanza(stanza); |
108 end |
111 end |
109 else |
112 else |
110 me = o_data; |
113 me = o_data; |
111 end |
114 end |
112 end |
115 end |
113 if me then |
116 if me then |
114 stanza:tag("status", {code='110'}); |
117 stanza:tag("status", {code='110'}); |
115 for jid in pairs(me.sessions) do |
118 for jid in pairs(me.sessions) do |
116 stanza.attr.to = jid; |
119 stanza.attr.to = jid; |
117 room:route_stanza(stanza); |
120 self:route_stanza(stanza); |
118 end |
121 end |
119 end |
122 end |
120 end |
123 end |
121 local function room_broadcast_message(room, stanza, historic) |
124 local function room_mt:broadcast_message(stanza, historic) |
122 for occupant, o_data in pairs(room._participants) do |
125 for occupant, o_data in pairs(self._participants) do |
123 for jid in pairs(o_data.sessions) do |
126 for jid in pairs(o_data.sessions) do |
124 stanza.attr.to = jid; |
127 stanza.attr.to = jid; |
125 room:route_stanza(stanza); |
128 self:route_stanza(stanza); |
126 end |
129 end |
127 end |
130 end |
128 if historic then -- add to history |
131 if historic then -- add to history |
129 local history = room._data['history']; |
132 local history = self._data['history']; |
130 if not history then history = {}; room._data['history'] = history; end |
133 if not history then history = {}; self._data['history'] = history; end |
131 -- stanza = st.clone(stanza); |
134 -- stanza = st.clone(stanza); |
132 stanza:tag("delay", {xmlns = "urn:xmpp:delay", from = muc_domain, stamp = datetime.datetime()}):up(); -- XEP-0203 |
135 stanza:tag("delay", {xmlns = "urn:xmpp:delay", from = muc_domain, stamp = datetime.datetime()}):up(); -- XEP-0203 |
133 stanza:tag("x", {xmlns = "jabber:x:delay", from = muc_domain, stamp = datetime.legacy()}):up(); -- XEP-0091 (deprecated) |
136 stanza:tag("x", {xmlns = "jabber:x:delay", from = muc_domain, stamp = datetime.legacy()}):up(); -- XEP-0091 (deprecated) |
134 t_insert(history, st.clone(st.preserialize(stanza))); |
137 t_insert(history, st.clone(st.preserialize(stanza))); |
135 while #history > history_length do t_remove(history, 1) end |
138 while #history > history_length do t_remove(history, 1) end |
136 end |
139 end |
137 end |
140 end |
138 |
141 |
139 |
142 |
140 local function room_send_occupant_list(room, to) |
143 function room_mt:send_occupant_list(to) |
141 local current_nick = room._jid_nick[to]; |
144 local current_nick = self._jid_nick[to]; |
142 for occupant, o_data in pairs(room._participants) do |
145 for occupant, o_data in pairs(self._participants) do |
143 if occupant ~= current_nick then |
146 if occupant ~= current_nick then |
144 local pres = get_filtered_presence(o_data.sessions[o_data.jid]); |
147 local pres = get_filtered_presence(o_data.sessions[o_data.jid]); |
145 pres.attr.to, pres.attr.from = to, occupant; |
148 pres.attr.to, pres.attr.from = to, occupant; |
146 pres:tag("x", {xmlns='http://jabber.org/protocol/muc#user'}) |
149 pres:tag("x", {xmlns='http://jabber.org/protocol/muc#user'}) |
147 :tag("item", {affiliation=o_data.affiliation, role=o_data.role}):up(); |
150 :tag("item", {affiliation=o_data.affiliation, role=o_data.role}):up(); |
148 room:route_stanza(pres); |
151 self:route_stanza(pres); |
149 end |
152 end |
150 end |
153 end |
151 end |
154 end |
152 local function room_send_history(room, to) |
155 function room_mt:send_history(to) |
153 local history = room._data['history']; -- send discussion history |
156 local history = self._data['history']; -- send discussion history |
154 if history then |
157 if history then |
155 for _, msg in ipairs(history) do |
158 for _, msg in ipairs(history) do |
156 msg = st.deserialize(msg); |
159 msg = st.deserialize(msg); |
157 msg.attr.to=to; |
160 msg.attr.to=to; |
158 room:route_stanza(msg); |
161 self:route_stanza(msg); |
159 end |
162 end |
160 end |
163 end |
161 if room._data['subject'] then |
164 if self._data['subject'] then |
162 room:route_stanza(st.message({type='groupchat', from=room.jid, to=to}):tag("subject"):text(room._data['subject'])); |
165 self:route_stanza(st.message({type='groupchat', from=self.jid, to=to}):tag("subject"):text(self._data['subject'])); |
163 end |
166 end |
164 end |
167 end |
165 |
168 |
166 local function room_get_disco_info(self, stanza) end |
169 local function room_get_disco_info(self, stanza) end |
167 local function room_get_disco_items(self, stanza) end |
170 local function room_get_disco_items(self, stanza) end |
168 local function room_set_subject(room, current_nick, subject) |
171 function room_mt:set_subject(current_nick, subject) |
169 -- TODO check nick's authority |
172 -- TODO check nick's authority |
170 if subject == "" then subject = nil; end |
173 if subject == "" then subject = nil; end |
171 room._data['subject'] = subject; |
174 self._data['subject'] = subject; |
172 local msg = st.message({type='groupchat', from=current_nick}) |
175 local msg = st.message({type='groupchat', from=current_nick}) |
173 :tag('subject'):text(subject):up(); |
176 :tag('subject'):text(subject):up(); |
174 room_broadcast_message(room, msg, false); |
177 self:broadcast_message(msg, false); |
175 return true; |
178 return true; |
176 end |
179 end |
177 |
180 |
178 local function room_handle_to_occupant(self, origin, stanza) -- PM, vCards, etc |
181 function room_mt:handle_to_occupant(origin, stanza) -- PM, vCards, etc |
179 local from, to = stanza.attr.from, stanza.attr.to; |
182 local from, to = stanza.attr.from, stanza.attr.to; |
180 local room = jid_bare(to); |
183 local room = jid_bare(to); |
181 local current_nick = self._jid_nick[from]; |
184 local current_nick = self._jid_nick[from]; |
182 local type = stanza.attr.type; |
185 local type = stanza.attr.type; |
183 log("debug", "room: %s, current_nick: %s, stanza: %s", room or "nil", current_nick or "nil", stanza:top_tag()); |
186 log("debug", "room: %s, current_nick: %s, stanza: %s", room or "nil", current_nick or "nil", stanza:top_tag()); |
186 local pr = get_filtered_presence(stanza); |
189 local pr = get_filtered_presence(stanza); |
187 pr.attr.from = current_nick; |
190 pr.attr.from = current_nick; |
188 if type == "error" then -- error, kick em out! |
191 if type == "error" then -- error, kick em out! |
189 if current_nick then |
192 if current_nick then |
190 log("debug", "kicking %s from %s", current_nick, room); |
193 log("debug", "kicking %s from %s", current_nick, room); |
191 room_handle_to_occupant(self, origin, st.presence({type='unavailable', from=from, to=to}):tag('status'):text('This participant is kicked from the room because he sent an error presence')); -- send unavailable |
194 self:handle_to_occupant(origin, st.presence({type='unavailable', from=from, to=to}) |
|
195 :tag('status'):text('This participant is kicked from the room because he sent an error presence')); -- send unavailable |
192 end |
196 end |
193 elseif type == "unavailable" then -- unavailable |
197 elseif type == "unavailable" then -- unavailable |
194 if current_nick then |
198 if current_nick then |
195 log("debug", "%s leaving %s", current_nick, room); |
199 log("debug", "%s leaving %s", current_nick, room); |
196 local data = self._participants[current_nick]; |
200 local data = self._participants[current_nick]; |
197 data.role = 'none'; |
201 data.role = 'none'; |
198 room_broadcast_presence(self, pr); |
202 self:broadcast_presence(pr); |
199 self._participants[current_nick] = nil; |
203 self._participants[current_nick] = nil; |
200 self._jid_nick[from] = nil; |
204 self._jid_nick[from] = nil; |
201 end |
205 end |
202 elseif not type then -- available |
206 elseif not type then -- available |
203 if current_nick then |
207 if current_nick then |
204 --if #pr == #stanza or current_nick ~= to then -- commented because google keeps resending directed presence |
208 --if #pr == #stanza or current_nick ~= to then -- commented because google keeps resending directed presence |
205 if current_nick == to then -- simple presence |
209 if current_nick == to then -- simple presence |
206 log("debug", "%s broadcasted presence", current_nick); |
210 log("debug", "%s broadcasted presence", current_nick); |
207 self._participants[current_nick].sessions[from] = pr; |
211 self._participants[current_nick].sessions[from] = pr; |
208 room_broadcast_presence(self, pr); |
212 self:broadcast_presence(pr); |
209 else -- change nick |
213 else -- change nick |
210 if self._participants[to] then |
214 if self._participants[to] then |
211 log("debug", "%s couldn't change nick", current_nick); |
215 log("debug", "%s couldn't change nick", current_nick); |
212 origin.send(st.error_reply(stanza, "cancel", "conflict"):tag("x", {xmlns = "http://jabber.org/protocol/muc"})); |
216 origin.send(st.error_reply(stanza, "cancel", "conflict"):tag("x", {xmlns = "http://jabber.org/protocol/muc"})); |
213 else |
217 else |
214 local data = self._participants[current_nick]; |
218 local data = self._participants[current_nick]; |
215 local to_nick = select(3, jid_split(to)); |
219 local to_nick = select(3, jid_split(to)); |
216 if to_nick then |
220 if to_nick then |
217 log("debug", "%s (%s) changing nick to %s", current_nick, data.jid, to); |
221 log("debug", "%s (%s) changing nick to %s", current_nick, data.jid, to); |
218 local p = st.presence({type='unavailable', from=current_nick}); |
222 local p = st.presence({type='unavailable', from=current_nick}); |
219 room_broadcast_presence(self, p, '303', to_nick); |
223 self:broadcast_presence(p, '303', to_nick); |
220 self._participants[current_nick] = nil; |
224 self._participants[current_nick] = nil; |
221 self._participants[to] = data; |
225 self._participants[to] = data; |
222 self._jid_nick[from] = to; |
226 self._jid_nick[from] = to; |
223 pr.attr.from = to; |
227 pr.attr.from = to; |
224 self._participants[to].sessions[from] = pr; |
228 self._participants[to].sessions[from] = pr; |
225 room_broadcast_presence(self, pr); |
229 self:broadcast_presence(pr); |
226 else |
230 else |
227 --TODO malformed-jid |
231 --TODO malformed-jid |
228 end |
232 end |
229 end |
233 end |
230 end |
234 end |
231 --else -- possible rejoin |
235 --else -- possible rejoin |
232 -- log("debug", "%s had connection replaced", current_nick); |
236 -- log("debug", "%s had connection replaced", current_nick); |
233 -- handle_to_occupant(origin, st.presence({type='unavailable', from=from, to=to}):tag('status'):text('Replaced by new connection'):up()); -- send unavailable |
237 -- self:handle_to_occupant(origin, st.presence({type='unavailable', from=from, to=to}) |
234 -- handle_to_occupant(origin, stanza); -- resend available |
238 -- :tag('status'):text('Replaced by new connection'):up()); -- send unavailable |
|
239 -- self:handle_to_occupant(origin, stanza); -- resend available |
235 --end |
240 --end |
236 else -- enter room |
241 else -- enter room |
237 local new_nick = to; |
242 local new_nick = to; |
238 if self._participants[to] then |
243 if self._participants[to] then |
239 new_nick = nil; |
244 new_nick = nil; |
266 origin.send(st.error_reply(stanza, "cancel", "not-acceptable")); |
271 origin.send(st.error_reply(stanza, "cancel", "not-acceptable")); |
267 elseif stanza.name == "message" and type == "groupchat" then -- groupchat messages not allowed in PM |
272 elseif stanza.name == "message" and type == "groupchat" then -- groupchat messages not allowed in PM |
268 origin.send(st.error_reply(stanza, "modify", "bad-request")); |
273 origin.send(st.error_reply(stanza, "modify", "bad-request")); |
269 elseif stanza.name == "message" and type == "error" and get_kickable_error(stanza) then |
274 elseif stanza.name == "message" and type == "error" and get_kickable_error(stanza) then |
270 log("debug", "%s kicked from %s for sending an error message", current_nick, room); |
275 log("debug", "%s kicked from %s for sending an error message", current_nick, room); |
271 room_handle_to_occupant(self, origin, st.presence({type='unavailable', from=from, to=to}):tag('status'):text('This participant is kicked from the room because he sent an error message to another occupant')); -- send unavailable |
276 self:handle_to_occupant(origin, st.presence({type='unavailable', from=from, to=to}):tag('status'):text('This participant is kicked from the room because he sent an error message to another occupant')); -- send unavailable |
272 else -- private stanza |
277 else -- private stanza |
273 local o_data = self._participants[to]; |
278 local o_data = self._participants[to]; |
274 if o_data then |
279 if o_data then |
275 log("debug", "%s sent private stanza to %s (%s)", from, to, o_data.jid); |
280 log("debug", "%s sent private stanza to %s (%s)", from, to, o_data.jid); |
276 local jid = o_data.jid; |
281 local jid = o_data.jid; |