Mercurial > prosody-hg
comparison plugins/muc/muc.lib.lua @ 6093:9a7eaf0a35b6
plugins/muc/muc.lib: Split up `handle_to_room` into smaller handlers (thanks sysko)
| author | daurnimator <quae@daurnimator.com> |
|---|---|
| date | Thu, 20 Feb 2014 14:36:49 -0500 |
| parents | 16d5049fe842 |
| children | db2faeb151b6 |
comparison
equal
deleted
inserted
replaced
| 6092:16d5049fe842 | 6093:9a7eaf0a35b6 |
|---|---|
| 769 end | 769 end |
| 770 self:set_persistent(false); | 770 self:set_persistent(false); |
| 771 module:fire_event("muc-room-destroyed", { room = self }); | 771 module:fire_event("muc-room-destroyed", { room = self }); |
| 772 end | 772 end |
| 773 | 773 |
| 774 function room_mt:handle_to_room(origin, stanza) -- presence changes and groupchat messages, along with disco/etc | 774 function room_mt:handle_iq_to_room(origin, stanza) |
| 775 local type = stanza.attr.type; | 775 local type = stanza.attr.type; |
| 776 local xmlns = stanza.tags[1] and stanza.tags[1].attr.xmlns; | 776 local xmlns = stanza.tags[1] and stanza.tags[1].attr.xmlns; |
| 777 if stanza.name == "iq" then | 777 if xmlns == "http://jabber.org/protocol/disco#info" and type == "get" and not stanza.tags[1].attr.node then |
| 778 if xmlns == "http://jabber.org/protocol/disco#info" and type == "get" and not stanza.tags[1].attr.node then | 778 origin.send(self:get_disco_info(stanza)); |
| 779 origin.send(self:get_disco_info(stanza)); | 779 elseif xmlns == "http://jabber.org/protocol/disco#items" and type == "get" and not stanza.tags[1].attr.node then |
| 780 elseif xmlns == "http://jabber.org/protocol/disco#items" and type == "get" and not stanza.tags[1].attr.node then | 780 origin.send(self:get_disco_items(stanza)); |
| 781 origin.send(self:get_disco_items(stanza)); | 781 elseif xmlns == "http://jabber.org/protocol/muc#admin" then |
| 782 elseif xmlns == "http://jabber.org/protocol/muc#admin" then | 782 local actor = stanza.attr.from; |
| 783 local actor = stanza.attr.from; | 783 local affiliation = self:get_affiliation(actor); |
| 784 local affiliation = self:get_affiliation(actor); | 784 local current_nick = self._jid_nick[actor]; |
| 785 local current_nick = self._jid_nick[actor]; | 785 local role = current_nick and self._occupants[current_nick].role or self:get_default_role(affiliation); |
| 786 local role = current_nick and self._occupants[current_nick].role or self:get_default_role(affiliation); | 786 local item = stanza.tags[1].tags[1]; |
| 787 local item = stanza.tags[1].tags[1]; | 787 if item and item.name == "item" then |
| 788 if item and item.name == "item" then | 788 if type == "set" then |
| 789 if type == "set" then | 789 local callback = function() origin.send(st.reply(stanza)); end |
| 790 local callback = function() origin.send(st.reply(stanza)); end | 790 if item.attr.jid then -- Validate provided JID |
| 791 if item.attr.jid then -- Validate provided JID | 791 item.attr.jid = jid_prep(item.attr.jid); |
| 792 item.attr.jid = jid_prep(item.attr.jid); | 792 if not item.attr.jid then |
| 793 if not item.attr.jid then | 793 origin.send(st.error_reply(stanza, "modify", "jid-malformed")); |
| 794 origin.send(st.error_reply(stanza, "modify", "jid-malformed")); | 794 return; |
| 795 return; | 795 end |
| 796 end | |
| 797 if not item.attr.jid and item.attr.nick then -- COMPAT Workaround for Miranda sending 'nick' instead of 'jid' when changing affiliation | |
| 798 local occupant = self._occupants[self.jid.."/"..item.attr.nick]; | |
| 799 if occupant then item.attr.jid = occupant.jid; end | |
| 800 elseif not item.attr.nick and item.attr.jid then | |
| 801 local nick = self._jid_nick[item.attr.jid]; | |
| 802 if nick then item.attr.nick = select(3, jid_split(nick)); end | |
| 803 end | |
| 804 local reason = item.tags[1] and item.tags[1].name == "reason" and #item.tags[1] == 1 and item.tags[1][1]; | |
| 805 if item.attr.affiliation and item.attr.jid and not item.attr.role then | |
| 806 local success, errtype, err = self:set_affiliation(actor, item.attr.jid, item.attr.affiliation, callback, reason); | |
| 807 if not success then origin.send(st.error_reply(stanza, errtype, err)); end | |
| 808 elseif item.attr.role and item.attr.nick and not item.attr.affiliation then | |
| 809 local success, errtype, err = self:set_role(actor, self.jid.."/"..item.attr.nick, item.attr.role, callback, reason); | |
| 810 if not success then origin.send(st.error_reply(stanza, errtype, err)); end | |
| 811 else | |
| 812 origin.send(st.error_reply(stanza, "cancel", "bad-request")); | |
| 813 end | |
| 814 elseif type == "get" then | |
| 815 local _aff = item.attr.affiliation; | |
| 816 local _rol = item.attr.role; | |
| 817 if _aff and not _rol then | |
| 818 if affiliation == "owner" or (affiliation == "admin" and _aff ~= "owner" and _aff ~= "admin") then | |
| 819 local reply = st.reply(stanza):query("http://jabber.org/protocol/muc#admin"); | |
| 820 for jid, affiliation in pairs(self._affiliations) do | |
| 821 if affiliation == _aff then | |
| 822 reply:tag("item", {affiliation = _aff, jid = jid}):up(); | |
| 823 end | |
| 796 end | 824 end |
| 825 origin.send(reply); | |
| 826 else | |
| 827 origin.send(st.error_reply(stanza, "auth", "forbidden")); | |
| 797 end | 828 end |
| 798 if not item.attr.jid and item.attr.nick then -- COMPAT Workaround for Miranda sending 'nick' instead of 'jid' when changing affiliation | 829 elseif _rol and not _aff then |
| 799 local occupant = self._occupants[self.jid.."/"..item.attr.nick]; | 830 if role == "moderator" then |
| 800 if occupant then item.attr.jid = occupant.jid; end | 831 -- TODO allow admins and owners not in room? Provide read-only access to everyone who can see the participants anyway? |
| 801 elseif not item.attr.nick and item.attr.jid then | 832 if _rol == "none" then _rol = nil; end |
| 802 local nick = self._jid_nick[item.attr.jid]; | 833 local reply = st.reply(stanza):query("http://jabber.org/protocol/muc#admin"); |
| 803 if nick then item.attr.nick = select(3, jid_split(nick)); end | 834 for occupant_jid, occupant in pairs(self._occupants) do |
| 835 if occupant.role == _rol then | |
| 836 reply:tag("item", { | |
| 837 nick = select(3, jid_split(occupant_jid)), | |
| 838 role = _rol or "none", | |
| 839 affiliation = occupant.affiliation or "none", | |
| 840 jid = occupant.jid | |
| 841 }):up(); | |
| 842 end | |
| 843 end | |
| 844 origin.send(reply); | |
| 845 else | |
| 846 origin.send(st.error_reply(stanza, "auth", "forbidden")); | |
| 804 end | 847 end |
| 805 local reason = item.tags[1] and item.tags[1].name == "reason" and #item.tags[1] == 1 and item.tags[1][1]; | 848 else |
| 806 if item.attr.affiliation and item.attr.jid and not item.attr.role then | 849 origin.send(st.error_reply(stanza, "cancel", "bad-request")); |
| 807 local success, errtype, err = self:set_affiliation(actor, item.attr.jid, item.attr.affiliation, callback, reason); | 850 end |
| 808 if not success then origin.send(st.error_reply(stanza, errtype, err)); end | 851 end |
| 809 elseif item.attr.role and item.attr.nick and not item.attr.affiliation then | 852 elseif type == "set" or type == "get" then |
| 810 local success, errtype, err = self:set_role(actor, self.jid.."/"..item.attr.nick, item.attr.role, callback, reason); | 853 origin.send(st.error_reply(stanza, "cancel", "bad-request")); |
| 811 if not success then origin.send(st.error_reply(stanza, errtype, err)); end | 854 end |
| 812 else | 855 elseif xmlns == "http://jabber.org/protocol/muc#owner" and (type == "get" or type == "set") and stanza.tags[1].name == "query" then |
| 813 origin.send(st.error_reply(stanza, "cancel", "bad-request")); | 856 if self:get_affiliation(stanza.attr.from) ~= "owner" then |
| 857 origin.send(st.error_reply(stanza, "auth", "forbidden", "Only owners can configure rooms")); | |
| 858 elseif stanza.attr.type == "get" then | |
| 859 self:send_form(origin, stanza); | |
| 860 elseif stanza.attr.type == "set" then | |
| 861 local child = stanza.tags[1].tags[1]; | |
| 862 if not child then | |
| 863 origin.send(st.error_reply(stanza, "modify", "bad-request")); | |
| 864 elseif child.name == "destroy" then | |
| 865 local newjid = child.attr.jid; | |
| 866 local reason, password; | |
| 867 for _,tag in ipairs(child.tags) do | |
| 868 if tag.name == "reason" then | |
| 869 reason = #tag.tags == 0 and tag[1]; | |
| 870 elseif tag.name == "password" then | |
| 871 password = #tag.tags == 0 and tag[1]; | |
| 814 end | 872 end |
| 815 elseif type == "get" then | 873 end |
| 816 local _aff = item.attr.affiliation; | 874 self:destroy(newjid, reason, password); |
| 817 local _rol = item.attr.role; | 875 origin.send(st.reply(stanza)); |
| 818 if _aff and not _rol then | 876 else |
| 819 if affiliation == "owner" or (affiliation == "admin" and _aff ~= "owner" and _aff ~= "admin") then | 877 self:process_form(origin, stanza); |
| 820 local reply = st.reply(stanza):query("http://jabber.org/protocol/muc#admin"); | 878 end |
| 821 for jid, affiliation in pairs(self._affiliations) do | 879 end |
| 822 if affiliation == _aff then | 880 elseif type == "set" or type == "get" then |
| 823 reply:tag("item", {affiliation = _aff, jid = jid}):up(); | 881 origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); |
| 824 end | 882 end |
| 825 end | 883 end |
| 826 origin.send(reply); | 884 |
| 827 else | 885 function room_mt:handle_groupchat_to_room(origin, stanza) |
| 828 origin.send(st.error_reply(stanza, "auth", "forbidden")); | 886 local from = stanza.attr.from; |
| 829 end | 887 local current_nick = self._jid_nick[from]; |
| 830 elseif _rol and not _aff then | 888 local occupant = self._occupants[current_nick]; |
| 831 if role == "moderator" then | 889 if not occupant then -- not in room |
| 832 -- TODO allow admins and owners not in room? Provide read-only access to everyone who can see the participants anyway? | 890 origin.send(st.error_reply(stanza, "cancel", "not-acceptable")); |
| 833 if _rol == "none" then _rol = nil; end | 891 elseif occupant.role == "visitor" then |
| 834 local reply = st.reply(stanza):query("http://jabber.org/protocol/muc#admin"); | 892 origin.send(st.error_reply(stanza, "auth", "forbidden")); |
| 835 for occupant_jid, occupant in pairs(self._occupants) do | 893 else |
| 836 if occupant.role == _rol then | |
| 837 reply:tag("item", { | |
| 838 nick = select(3, jid_split(occupant_jid)), | |
| 839 role = _rol or "none", | |
| 840 affiliation = occupant.affiliation or "none", | |
| 841 jid = occupant.jid | |
| 842 }):up(); | |
| 843 end | |
| 844 end | |
| 845 origin.send(reply); | |
| 846 else | |
| 847 origin.send(st.error_reply(stanza, "auth", "forbidden")); | |
| 848 end | |
| 849 else | |
| 850 origin.send(st.error_reply(stanza, "cancel", "bad-request")); | |
| 851 end | |
| 852 end | |
| 853 elseif type == "set" or type == "get" then | |
| 854 origin.send(st.error_reply(stanza, "cancel", "bad-request")); | |
| 855 end | |
| 856 elseif xmlns == "http://jabber.org/protocol/muc#owner" and (type == "get" or type == "set") and stanza.tags[1].name == "query" then | |
| 857 if self:get_affiliation(stanza.attr.from) ~= "owner" then | |
| 858 origin.send(st.error_reply(stanza, "auth", "forbidden", "Only owners can configure rooms")); | |
| 859 elseif stanza.attr.type == "get" then | |
| 860 self:send_form(origin, stanza); | |
| 861 elseif stanza.attr.type == "set" then | |
| 862 local child = stanza.tags[1].tags[1]; | |
| 863 if not child then | |
| 864 origin.send(st.error_reply(stanza, "modify", "bad-request")); | |
| 865 elseif child.name == "destroy" then | |
| 866 local newjid = child.attr.jid; | |
| 867 local reason, password; | |
| 868 for _,tag in ipairs(child.tags) do | |
| 869 if tag.name == "reason" then | |
| 870 reason = #tag.tags == 0 and tag[1]; | |
| 871 elseif tag.name == "password" then | |
| 872 password = #tag.tags == 0 and tag[1]; | |
| 873 end | |
| 874 end | |
| 875 self:destroy(newjid, reason, password); | |
| 876 origin.send(st.reply(stanza)); | |
| 877 else | |
| 878 self:process_form(origin, stanza); | |
| 879 end | |
| 880 end | |
| 881 elseif type == "set" or type == "get" then | |
| 882 origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); | |
| 883 end | |
| 884 elseif stanza.name == "message" and type == "groupchat" then | |
| 885 local from = stanza.attr.from; | 894 local from = stanza.attr.from; |
| 886 local current_nick = self._jid_nick[from]; | 895 stanza.attr.from = current_nick; |
| 887 local occupant = self._occupants[current_nick]; | 896 local subject = stanza:get_child_text("subject"); |
| 888 if not occupant then -- not in room | 897 if subject then |
| 889 origin.send(st.error_reply(stanza, "cancel", "not-acceptable")); | 898 if occupant.role == "moderator" or |
| 890 elseif occupant.role == "visitor" then | 899 ( self._data.changesubject and occupant.role == "participant" ) then -- and participant |
| 891 origin.send(st.error_reply(stanza, "auth", "forbidden")); | 900 self:set_subject(current_nick, subject); |
| 901 else | |
| 902 stanza.attr.from = from; | |
| 903 origin.send(st.error_reply(stanza, "auth", "forbidden")); | |
| 904 end | |
| 892 else | 905 else |
| 893 local from = stanza.attr.from; | 906 self:broadcast_message(stanza, self:get_historylength() > 0 and stanza:get_child("body")); |
| 894 stanza.attr.from = current_nick; | 907 end |
| 895 local subject = stanza:get_child_text("subject"); | 908 stanza.attr.from = from; |
| 896 if subject then | 909 end |
| 897 if occupant.role == "moderator" or | 910 end |
| 898 ( self._data.changesubject and occupant.role == "participant" ) then -- and participant | 911 |
| 899 self:set_subject(current_nick, subject); | 912 |
| 900 else | 913 function room_mt:handle_kickable_to_room(origin, stanza) |
| 901 stanza.attr.from = from; | 914 local current_nick = self._jid_nick[stanza.attr.from]; |
| 902 origin.send(st.error_reply(stanza, "auth", "forbidden")); | 915 log("debug", "%s kicked from %s for sending an error message", current_nick, self.jid); |
| 903 end | 916 self:handle_to_occupant(origin, build_unavailable_presence_from_error(stanza)); -- send unavailable |
| 904 else | 917 end |
| 905 self:broadcast_message(stanza, self:get_historylength() > 0 and stanza:get_child("body")); | 918 |
| 906 end | 919 -- hack - some buggy clients send presence updates to the room rather than their nick |
| 907 stanza.attr.from = from; | 920 function room_mt:handle_presence_to_room(origin, stanza) |
| 908 end | 921 local type = stanza.attr.type; |
| 909 elseif stanza.name == "message" and type == "error" and is_kickable_error(stanza) then | 922 local current_nick = self._jid_nick[stanza.attr.from]; |
| 910 local current_nick = self._jid_nick[stanza.attr.from]; | 923 if current_nick then |
| 911 log("debug", "%s kicked from %s for sending an error message", current_nick, self.jid); | |
| 912 self:handle_to_occupant(origin, build_unavailable_presence_from_error(stanza)); -- send unavailable | |
| 913 elseif stanza.name == "presence" then -- hack - some buggy clients send presence updates to the room rather than their nick | |
| 914 local to = stanza.attr.to; | 924 local to = stanza.attr.to; |
| 915 local current_nick = self._jid_nick[stanza.attr.from]; | 925 stanza.attr.to = current_nick; |
| 916 if current_nick then | 926 self:handle_to_occupant(origin, stanza); |
| 917 stanza.attr.to = current_nick; | 927 stanza.attr.to = to; |
| 918 self:handle_to_occupant(origin, stanza); | 928 elseif type ~= "error" and type ~= "result" then |
| 919 stanza.attr.to = to; | 929 origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); |
| 920 elseif type ~= "error" and type ~= "result" then | 930 end |
| 921 origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); | 931 end |
| 922 end | 932 |
| 923 elseif stanza.name == "message" and not(type == "chat" or type == "error" or type == "groupchat" or type == "headline") and #stanza.tags == 1 | 933 function room_mt:handle_invite_to_room(origin, stanza, payload) |
| 934 local _from, _to = stanza.attr.from, stanza.attr.to; | |
| 935 local _invitee = jid_prep(payload.attr.to); | |
| 936 if _invitee then | |
| 937 local _reason = payload.tags[1] and payload.tags[1].name == 'reason' and #payload.tags[1].tags == 0 and payload.tags[1][1]; | |
| 938 local invite = st.message({from = _to, to = _invitee, id = stanza.attr.id}) | |
| 939 :tag('x', {xmlns='http://jabber.org/protocol/muc#user'}) | |
| 940 :tag('invite', {from=_from}) | |
| 941 :tag('reason'):text(_reason or ""):up() | |
| 942 :up(); | |
| 943 if self:get_password() then | |
| 944 invite:tag("password"):text(self:get_password()):up(); | |
| 945 end | |
| 946 invite:up() | |
| 947 :tag('x', {xmlns="jabber:x:conference", jid=_to}) -- COMPAT: Some older clients expect this | |
| 948 :text(_reason or "") | |
| 949 :up() | |
| 950 :tag('body') -- Add a plain message for clients which don't support invites | |
| 951 :text(_from..' invited you to the room '.._to..(_reason and (' ('.._reason..')') or "")) | |
| 952 :up(); | |
| 953 if self:get_members_only() and not self:get_affiliation(_invitee) then | |
| 954 log("debug", "%s invited %s into members only room %s, granting membership", _from, _invitee, _to); | |
| 955 self:set_affiliation(_from, _invitee, "member", nil, "Invited by " .. self._jid_nick[_from]) | |
| 956 end | |
| 957 self:_route_stanza(invite); | |
| 958 else | |
| 959 origin.send(st.error_reply(stanza, "cancel", "jid-malformed")); | |
| 960 end | |
| 961 end | |
| 962 | |
| 963 function room_mt:handle_message_to_room(origin, stanza) | |
| 964 local type = stanza.attr.type; | |
| 965 if type == "groupchat" then | |
| 966 return self:handle_groupchat_to_room(origin, stanza) | |
| 967 elseif type == "error" and is_kickable_error(stanza) then | |
| 968 return self:handle_kickable_to_room(origin, stanza) | |
| 969 elseif not(type == "chat" or type == "error" or type == "groupchat" or type == "headline") and #stanza.tags == 1 | |
| 924 and self._jid_nick[stanza.attr.from] and stanza.tags[1].name == "x" and stanza.tags[1].attr.xmlns == "http://jabber.org/protocol/muc#user" then | 970 and self._jid_nick[stanza.attr.from] and stanza.tags[1].name == "x" and stanza.tags[1].attr.xmlns == "http://jabber.org/protocol/muc#user" then |
| 925 local x = stanza.tags[1]; | 971 local x = stanza.tags[1]; |
| 926 local payload = (#x.tags == 1 and x.tags[1]); | 972 local payload = (#x.tags == 1 and x.tags[1]); |
| 927 if payload and payload.name == "invite" and payload.attr.to then | 973 if payload and payload.name == "invite" and payload.attr.to then |
| 928 local _from, _to = stanza.attr.from, stanza.attr.to; | 974 return self:handle_invite_to_room(origin, stanza, payload) |
| 929 local _invitee = jid_prep(payload.attr.to); | |
| 930 if _invitee then | |
| 931 local _reason = payload.tags[1] and payload.tags[1].name == 'reason' and #payload.tags[1].tags == 0 and payload.tags[1][1]; | |
| 932 local invite = st.message({from = _to, to = _invitee, id = stanza.attr.id}) | |
| 933 :tag('x', {xmlns='http://jabber.org/protocol/muc#user'}) | |
| 934 :tag('invite', {from=_from}) | |
| 935 :tag('reason'):text(_reason or ""):up() | |
| 936 :up(); | |
| 937 if self:get_password() then | |
| 938 invite:tag("password"):text(self:get_password()):up(); | |
| 939 end | |
| 940 invite:up() | |
| 941 :tag('x', {xmlns="jabber:x:conference", jid=_to}) -- COMPAT: Some older clients expect this | |
| 942 :text(_reason or "") | |
| 943 :up() | |
| 944 :tag('body') -- Add a plain message for clients which don't support invites | |
| 945 :text(_from..' invited you to the room '.._to..(_reason and (' ('.._reason..')') or "")) | |
| 946 :up(); | |
| 947 if self:get_members_only() and not self:get_affiliation(_invitee) then | |
| 948 log("debug", "%s invited %s into members only room %s, granting membership", _from, _invitee, _to); | |
| 949 self:set_affiliation(_from, _invitee, "member", nil, "Invited by " .. self._jid_nick[_from]) | |
| 950 end | |
| 951 self:_route_stanza(invite); | |
| 952 else | |
| 953 origin.send(st.error_reply(stanza, "cancel", "jid-malformed")); | |
| 954 end | |
| 955 else | 975 else |
| 956 origin.send(st.error_reply(stanza, "cancel", "bad-request")); | 976 origin.send(st.error_reply(stanza, "cancel", "bad-request")); |
| 957 end | 977 end |
| 958 else | 978 else |
| 959 if type == "error" or type == "result" then return; end | 979 if type == "error" or type == "result" then return; end |
| 960 origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); | 980 origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); |
| 981 end | |
| 982 end | |
| 983 | |
| 984 function room_mt:handle_to_room(origin, stanza) -- presence changes and groupchat messages, along with disco/etc | |
| 985 if stanza.name == "iq" then | |
| 986 return self:handle_iq_to_room(origin, stanza) | |
| 987 elseif stanza.name == "message" then | |
| 988 return self:handle_message_to_room(origin, stanza) | |
| 989 elseif stanza.name == "presence" then | |
| 990 return self:handle_presence_to_room(origin, stanza) | |
| 961 end | 991 end |
| 962 end | 992 end |
| 963 | 993 |
| 964 function room_mt:handle_stanza(origin, stanza) | 994 function room_mt:handle_stanza(origin, stanza) |
| 965 local to_node, to_host, to_resource = jid_split(stanza.attr.to); | 995 local to_node, to_host, to_resource = jid_split(stanza.attr.to); |
