Software /
code /
prosody
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 |
parent | 6092:16d5049fe842 |
child | 6094: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); |