Comparison

plugins/muc/muc.lib.lua @ 5580:db5d1a350cc7

mod_muc: Refactor config form handling, and allow for clients to submit incomplete forms. Fixes #246
author Matthew Wild <mwild1@gmail.com>
date Thu, 16 May 2013 14:17:25 +0100
parent 5577:8b09b0d068d4
child 5600:1b326a1e4da6
comparison
equal deleted inserted replaced
5579:e449e6342e36 5580:db5d1a350cc7
96 if affiliation == "owner" or affiliation == "admin" then 96 if affiliation == "owner" or affiliation == "admin" then
97 return "moderator"; 97 return "moderator";
98 elseif affiliation == "member" then 98 elseif affiliation == "member" then
99 return "participant"; 99 return "participant";
100 elseif not affiliation then 100 elseif not affiliation then
101 if not self:is_members_only() then 101 if not self:get_members_only() then
102 return self:is_moderated() and "visitor" or "participant"; 102 return self:get_moderated() and "visitor" or "participant";
103 end 103 end
104 end 104 end
105 end 105 end
106 106
107 function room_mt:broadcast_presence(stanza, sid, code, nick) 107 function room_mt:broadcast_presence(stanza, sid, code, nick)
216 local count = 0; for _ in pairs(self._occupants) do count = count + 1; end 216 local count = 0; for _ in pairs(self._occupants) do count = count + 1; end
217 return st.reply(stanza):query("http://jabber.org/protocol/disco#info") 217 return st.reply(stanza):query("http://jabber.org/protocol/disco#info")
218 :tag("identity", {category="conference", type="text", name=self:get_name()}):up() 218 :tag("identity", {category="conference", type="text", name=self:get_name()}):up()
219 :tag("feature", {var="http://jabber.org/protocol/muc"}):up() 219 :tag("feature", {var="http://jabber.org/protocol/muc"}):up()
220 :tag("feature", {var=self:get_password() and "muc_passwordprotected" or "muc_unsecured"}):up() 220 :tag("feature", {var=self:get_password() and "muc_passwordprotected" or "muc_unsecured"}):up()
221 :tag("feature", {var=self:is_moderated() and "muc_moderated" or "muc_unmoderated"}):up() 221 :tag("feature", {var=self:get_moderated() and "muc_moderated" or "muc_unmoderated"}):up()
222 :tag("feature", {var=self:is_members_only() and "muc_membersonly" or "muc_open"}):up() 222 :tag("feature", {var=self:get_members_only() and "muc_membersonly" or "muc_open"}):up()
223 :tag("feature", {var=self:is_persistent() and "muc_persistent" or "muc_temporary"}):up() 223 :tag("feature", {var=self:get_persistent() and "muc_persistent" or "muc_temporary"}):up()
224 :tag("feature", {var=self:is_hidden() and "muc_hidden" or "muc_public"}):up() 224 :tag("feature", {var=self:get_hidden() and "muc_hidden" or "muc_public"}):up()
225 :tag("feature", {var=self._data.whois ~= "anyone" and "muc_semianonymous" or "muc_nonanonymous"}):up() 225 :tag("feature", {var=self._data.whois ~= "anyone" and "muc_semianonymous" or "muc_nonanonymous"}):up()
226 :add_child(dataform.new({ 226 :add_child(dataform.new({
227 { name = "FORM_TYPE", type = "hidden", value = "http://jabber.org/protocol/muc#roominfo" }, 227 { name = "FORM_TYPE", type = "hidden", value = "http://jabber.org/protocol/muc#roominfo" },
228 { name = "muc#roominfo_description", label = "Description"}, 228 { name = "muc#roominfo_description", label = "Description"},
229 { name = "muc#roominfo_occupants", label = "Number of occupants", value = tostring(count) } 229 { name = "muc#roominfo_occupants", label = "Number of occupants", value = tostring(count) }
294 if self._data.moderated ~= moderated then 294 if self._data.moderated ~= moderated then
295 self._data.moderated = moderated; 295 self._data.moderated = moderated;
296 if self.save then self:save(true); end 296 if self.save then self:save(true); end
297 end 297 end
298 end 298 end
299 function room_mt:is_moderated() 299 function room_mt:get_moderated()
300 return self._data.moderated; 300 return self._data.moderated;
301 end 301 end
302 function room_mt:set_members_only(members_only) 302 function room_mt:set_members_only(members_only)
303 members_only = members_only and true or nil; 303 members_only = members_only and true or nil;
304 if self._data.members_only ~= members_only then 304 if self._data.members_only ~= members_only then
305 self._data.members_only = members_only; 305 self._data.members_only = members_only;
306 if self.save then self:save(true); end 306 if self.save then self:save(true); end
307 end 307 end
308 end 308 end
309 function room_mt:is_members_only() 309 function room_mt:get_members_only()
310 return self._data.members_only; 310 return self._data.members_only;
311 end 311 end
312 function room_mt:set_persistent(persistent) 312 function room_mt:set_persistent(persistent)
313 persistent = persistent and true or nil; 313 persistent = persistent and true or nil;
314 if self._data.persistent ~= persistent then 314 if self._data.persistent ~= persistent then
315 self._data.persistent = persistent; 315 self._data.persistent = persistent;
316 if self.save then self:save(true); end 316 if self.save then self:save(true); end
317 end 317 end
318 end 318 end
319 function room_mt:is_persistent() 319 function room_mt:get_persistent()
320 return self._data.persistent; 320 return self._data.persistent;
321 end 321 end
322 function room_mt:set_hidden(hidden) 322 function room_mt:set_hidden(hidden)
323 hidden = hidden and true or nil; 323 hidden = hidden and true or nil;
324 if self._data.hidden ~= hidden then 324 if self._data.hidden ~= hidden then
325 self._data.hidden = hidden; 325 self._data.hidden = hidden;
326 if self.save then self:save(true); end 326 if self.save then self:save(true); end
327 end 327 end
328 end 328 end
329 function room_mt:is_hidden() 329 function room_mt:get_hidden()
330 return self._data.hidden; 330 return self._data.hidden;
331 end
332 function room_mt:get_public()
333 return not self:get_hidden();
334 end
335 function room_mt:set_public(public)
336 return self:set_hidden(not public);
331 end 337 end
332 function room_mt:set_changesubject(changesubject) 338 function room_mt:set_changesubject(changesubject)
333 changesubject = changesubject and true or nil; 339 changesubject = changesubject and true or nil;
334 if self._data.changesubject ~= changesubject then 340 if self._data.changesubject ~= changesubject then
335 self._data.changesubject = changesubject; 341 self._data.changesubject = changesubject;
602 }, 608 },
603 { 609 {
604 name = 'muc#roomconfig_persistentroom', 610 name = 'muc#roomconfig_persistentroom',
605 type = 'boolean', 611 type = 'boolean',
606 label = 'Make Room Persistent?', 612 label = 'Make Room Persistent?',
607 value = self:is_persistent() 613 value = self:get_persistent()
608 }, 614 },
609 { 615 {
610 name = 'muc#roomconfig_publicroom', 616 name = 'muc#roomconfig_publicroom',
611 type = 'boolean', 617 type = 'boolean',
612 label = 'Make Room Publicly Searchable?', 618 label = 'Make Room Publicly Searchable?',
613 value = not self:is_hidden() 619 value = not self:get_hidden()
614 }, 620 },
615 { 621 {
616 name = 'muc#roomconfig_changesubject', 622 name = 'muc#roomconfig_changesubject',
617 type = 'boolean', 623 type = 'boolean',
618 label = 'Allow Occupants to Change Subject?', 624 label = 'Allow Occupants to Change Subject?',
635 }, 641 },
636 { 642 {
637 name = 'muc#roomconfig_moderatedroom', 643 name = 'muc#roomconfig_moderatedroom',
638 type = 'boolean', 644 type = 'boolean',
639 label = 'Make Room Moderated?', 645 label = 'Make Room Moderated?',
640 value = self:is_moderated() 646 value = self:get_moderated()
641 }, 647 },
642 { 648 {
643 name = 'muc#roomconfig_membersonly', 649 name = 'muc#roomconfig_membersonly',
644 type = 'boolean', 650 type = 'boolean',
645 label = 'Make Room Members-Only?', 651 label = 'Make Room Members-Only?',
646 value = self:is_members_only() 652 value = self:get_members_only()
647 }, 653 },
648 { 654 {
649 name = 'muc#roomconfig_historylength', 655 name = 'muc#roomconfig_historylength',
650 type = 'text-single', 656 type = 'text-single',
651 label = 'Maximum Number of History Messages Returned by Room', 657 label = 'Maximum Number of History Messages Returned by Room',
653 } 659 }
654 }); 660 });
655 return module:fire_event("muc-config-form", { room = self, form = form }) or form; 661 return module:fire_event("muc-config-form", { room = self, form = form }) or form;
656 end 662 end
657 663
658 local valid_whois = { 664 local valid_whois = { moderators = true, anyone = true };
659 moderators = true,
660 anyone = true,
661 }
662 665
663 function room_mt:process_form(origin, stanza) 666 function room_mt:process_form(origin, stanza)
664 local query = stanza.tags[1]; 667 local query = stanza.tags[1];
665 local form; 668 local form;
666 for _, tag in ipairs(query.tags) do if tag.name == "x" and tag.attr.xmlns == "jabber:x:data" then form = tag; break; end end 669 for _, tag in ipairs(query.tags) do if tag.name == "x" and tag.attr.xmlns == "jabber:x:data" then form = tag; break; end end
669 if form.attr.type ~= "submit" then origin.send(st.error_reply(stanza, "cancel", "bad-request", "Not a submitted form")); return; end 672 if form.attr.type ~= "submit" then origin.send(st.error_reply(stanza, "cancel", "bad-request", "Not a submitted form")); return; end
670 673
671 local fields = self:get_form_layout():data(form); 674 local fields = self:get_form_layout():data(form);
672 if fields.FORM_TYPE ~= "http://jabber.org/protocol/muc#roomconfig" then origin.send(st.error_reply(stanza, "cancel", "bad-request", "Form is not of type room configuration")); return; end 675 if fields.FORM_TYPE ~= "http://jabber.org/protocol/muc#roomconfig" then origin.send(st.error_reply(stanza, "cancel", "bad-request", "Form is not of type room configuration")); return; end
673 676
674 local dirty = false 677
675 678 local changed = {};
676 local event = { room = self, fields = fields, changed = dirty }; 679
680 local function handle_option(name, field, allowed)
681 local new = fields[field];
682 if new == nil then return; end
683 if allowed and not allowed[new] then return; end
684 if new == self["get_"..name](self) then return; end
685 changed[name] = true;
686 self["set_"..name](self, new);
687 end
688
689 local event = { room = self, fields = fields, changed = changed, stanza = stanza, origin = origin, update_option = handle_option };
677 module:fire_event("muc-config-submitted", event); 690 module:fire_event("muc-config-submitted", event);
678 dirty = event.changed or dirty; 691
679 692 handle_option("name", "muc#roomconfig_roomname");
680 local name = fields['muc#roomconfig_roomname']; 693 handle_option("description", "muc#roomconfig_roomdesc");
681 if name ~= self:get_name() then 694 handle_option("persistent", "muc#roomconfig_persistentroom");
682 self:set_name(name); 695 handle_option("moderated", "muc#roomconfig_moderatedroom");
683 end 696 handle_option("members_only", "muc#roomconfig_membersonly");
684 697 handle_option("public", "muc#roomconfig_publicroom");
685 local description = fields['muc#roomconfig_roomdesc']; 698 handle_option("changesubject", "muc#roomconfig_changesubject");
686 if description ~= self:get_description() then 699 handle_option("historylength", "muc#roomconfig_historylength");
687 self:set_description(description); 700 handle_option("whois", "muc#roomconfig_whois", valid_whois);
688 end 701 handle_option("password", "muc#roomconfig_roomsecret");
689
690 local persistent = fields['muc#roomconfig_persistentroom'];
691 dirty = dirty or (self:is_persistent() ~= persistent)
692 module:log("debug", "persistent=%s", tostring(persistent));
693
694 local moderated = fields['muc#roomconfig_moderatedroom'];
695 dirty = dirty or (self:is_moderated() ~= moderated)
696 module:log("debug", "moderated=%s", tostring(moderated));
697
698 local membersonly = fields['muc#roomconfig_membersonly'];
699 dirty = dirty or (self:is_members_only() ~= membersonly)
700 module:log("debug", "membersonly=%s", tostring(membersonly));
701
702 local public = fields['muc#roomconfig_publicroom'];
703 dirty = dirty or (self:is_hidden() ~= (not public and true or nil))
704
705 local changesubject = fields['muc#roomconfig_changesubject'];
706 dirty = dirty or (self:get_changesubject() ~= (not changesubject and true or nil))
707 module:log('debug', 'changesubject=%s', changesubject and "true" or "false")
708
709 local historylength = tonumber(fields['muc#roomconfig_historylength']);
710 dirty = dirty or (historylength and (self:get_historylength() ~= historylength));
711 module:log('debug', 'historylength=%s', historylength)
712
713
714 local whois = fields['muc#roomconfig_whois'];
715 if not valid_whois[whois] then
716 origin.send(st.error_reply(stanza, 'cancel', 'bad-request', "Invalid value for 'whois'"));
717 return;
718 end
719 local whois_changed = self._data.whois ~= whois
720 self._data.whois = whois
721 module:log('debug', 'whois=%s', whois)
722
723 local password = fields['muc#roomconfig_roomsecret'];
724 if self:get_password() ~= password then
725 self:set_password(password);
726 end
727 self:set_moderated(moderated);
728 self:set_members_only(membersonly);
729 self:set_persistent(persistent);
730 self:set_hidden(not public);
731 self:set_changesubject(changesubject);
732 self:set_historylength(historylength);
733 702
734 if self.save then self:save(true); end 703 if self.save then self:save(true); end
735 origin.send(st.reply(stanza)); 704 origin.send(st.reply(stanza));
736 705
737 if dirty or whois_changed then 706 if next(changed) then
738 local msg = st.message({type='groupchat', from=self.jid}) 707 local msg = st.message({type='groupchat', from=self.jid})
739 :tag('x', {xmlns='http://jabber.org/protocol/muc#user'}):up() 708 :tag('x', {xmlns='http://jabber.org/protocol/muc#user'}):up()
740 709 :tag('status', {code = '104'}):up();
741 if dirty then 710 if changed.whois then
742 msg.tags[1]:tag('status', {code = '104'}):up();
743 end
744 if whois_changed then
745 local code = (whois == 'moderators') and "173" or "172"; 711 local code = (whois == 'moderators') and "173" or "172";
746 msg.tags[1]:tag('status', {code = code}):up(); 712 msg.tags[1]:tag('status', {code = code}):up();
747 end 713 end
748
749 self:broadcast_message(msg, false) 714 self:broadcast_message(msg, false)
750 end 715 end
751 end 716 end
752 717
753 function room_mt:destroy(newjid, reason, password) 718 function room_mt:destroy(newjid, reason, password)
941 :text(_reason or "") 906 :text(_reason or "")
942 :up() 907 :up()
943 :tag('body') -- Add a plain message for clients which don't support invites 908 :tag('body') -- Add a plain message for clients which don't support invites
944 :text(_from..' invited you to the room '.._to..(_reason and (' ('.._reason..')') or "")) 909 :text(_from..' invited you to the room '.._to..(_reason and (' ('.._reason..')') or ""))
945 :up(); 910 :up();
946 if self:is_members_only() and not self:get_affiliation(_invitee) then 911 if self:get_members_only() and not self:get_affiliation(_invitee) then
947 log("debug", "%s invited %s into members only room %s, granting membership", _from, _invitee, _to); 912 log("debug", "%s invited %s into members only room %s, granting membership", _from, _invitee, _to);
948 self:set_affiliation(_from, _invitee, "member", nil, "Invited by " .. self._jid_nick[_from]) 913 self:set_affiliation(_from, _invitee, "member", nil, "Invited by " .. self._jid_nick[_from])
949 end 914 end
950 self:_route_stanza(invite); 915 self:_route_stanza(invite);
951 else 916 else