Mercurial > prosody-hg
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 |
| parents | 8b09b0d068d4 |
| children | 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 |
