1 local jid_bare
= require
"util.jid".bare
;
2 local jid_resource
= require
"util.jid".resource
;
3 local resourceprep
= require
"util.encodings".stringprep
.resourceprep
;
4 local st
= require
"util.stanza";
5 local dataforms
= require
"util.dataforms";
7 local allow_unaffiliated
= module
:get_option_boolean("allow_unaffiliated_register", false);
9 local enforce_nick
= module
:get_option_boolean("enforce_registered_nickname", false);
11 -- reserved_nicks[nick] = jid
12 local function get_reserved_nicks(room
)
13 if room
._reserved_nicks
then
14 return room
._reserved_nicks
;
16 module
:log("debug", "Refreshing reserved nicks...");
17 local reserved_nicks
= {};
18 for jid
, _
, data
in room
:each_affiliation() do
19 local nick
= data
and data
.reserved_nickname
;
20 module
:log("debug", "Refreshed for %s: %s", jid
, nick
);
22 reserved_nicks
[nick
] = jid
;
25 room
._reserved_nicks
= reserved_nicks
;
26 return reserved_nicks
;
29 -- Returns the registered nick, if any, for a JID
30 -- Note: this is just the *nick* part, i.e. the resource of the in-room JID
31 local function get_registered_nick(room
, jid
)
32 local registered_data
= room
._affiliation_data
[jid
];
33 if not registered_data
then
36 return registered_data
.reserved_nickname
;
39 -- Returns the JID, if any, that registered a nick (not in-room JID)
40 local function get_registered_jid(room
, nick
)
41 local reserved_nicks
= get_reserved_nicks(room
);
42 return reserved_nicks
[nick
];
45 module
:hook("muc-set-affiliation", function (event
)
46 -- Clear reserved nick cache
47 event
.room
._reserved_nicks
= nil;
50 module
:add_feature("jabber:iq:register");
52 module
:hook("muc-disco#info", function (event
)
53 event
.reply
:tag("feature", { var
= "jabber:iq:register" }):up();
56 local registration_form
= dataforms
.new
{
57 { name
= "FORM_TYPE", type = "hidden", value
= "http://jabber.org/protocol/muc#register" },
58 { name
= "muc#register_roomnick", type = "text-single", label
= "Nickname"},
61 local function enforce_nick_policy(event
)
62 local origin
, stanza
= event
.origin
, event
.stanza
;
63 local room
= assert(event
.room
); -- FIXME
64 if not room
then return; end
66 -- Check if the chosen nickname is reserved
67 local requested_nick
= jid_resource(stanza
.attr
.to
);
68 local reserved_by
= get_registered_jid(room
, requested_nick
);
69 if reserved_by
and reserved_by
~= jid_bare(stanza
.attr
.from
) then
70 module
:log("debug", "%s attempted to use nick %s reserved by %s", stanza
.attr
.from
, requested_nick
, reserved_by
);
71 local reply
= st
.error_reply(stanza
, "cancel", "conflict"):up();
72 origin
.send(reply
:tag("x", {xmlns
= "http://jabber.org/protocol/muc"}));
76 -- Check if the occupant has a reservation they must use
78 local nick
= get_registered_nick(room
, jid_bare(stanza
.attr
.from
));
80 if event
.occupant
then
81 event
.occupant
.nick
= jid_bare(event
.occupant
.nick
) .. "/" .. nick
;
82 elseif event
.dest_occupant
.nick
~= jid_bare(event
.dest_occupant
.nick
) .. "/" .. nick
then
83 module
:log("debug", "Attempt by %s to join as %s, but their reserved nick is %s", stanza
.attr
.from
, requested_nick
, nick
);
84 local reply
= st
.error_reply(stanza
, "cancel", "not-acceptable"):up();
85 origin
.send(reply
:tag("x", {xmlns
= "http://jabber.org/protocol/muc"}));
92 module
:hook("muc-occupant-pre-join", enforce_nick_policy
);
93 module
:hook("muc-occupant-pre-change", enforce_nick_policy
);
95 -- Discovering Reserved Room Nickname
96 -- http://xmpp.org/extensions/xep-0045.html#reservednick
97 module
:hook("muc-disco#info/x-roomuser-item", function (event
)
98 local nick
= get_registered_nick(event
.room
, jid_bare(event
.stanza
.attr
.from
));
100 event
.reply
:tag("identity", { category
= "conference", type = "text", name
= nick
})
104 local function handle_register_iq(room
, origin
, stanza
)
105 local user_jid
= jid_bare(stanza
.attr
.from
)
106 local affiliation
= room
:get_affiliation(user_jid
);
107 if affiliation
== "outcast" then
108 origin
.send(st
.error_reply(stanza
, "auth", "forbidden"));
110 elseif not (affiliation
or allow_unaffiliated
) then
111 origin
.send(st
.error_reply(stanza
, "auth", "registration-required"));
114 local reply
= st
.reply(stanza
);
115 local registered_nick
= get_registered_nick(room
, user_jid
);
116 if stanza
.attr
.type == "get" then
117 reply
:query("jabber:iq:register");
118 if registered_nick
then
119 reply
:tag("registered"):up();
120 reply
:tag("username"):text(registered_nick
);
124 reply
:add_child(registration_form
:form());
125 else -- type == set -- handle registration form
126 local query
= stanza
.tags
[1];
127 if query
:get_child("remove") then
128 -- Remove "member" affiliation, but preserve if any other
129 local new_affiliation
= affiliation
~= "member" and affiliation
;
130 local ok
, err_type
, err_condition
= room
:set_affiliation(true, user_jid
, new_affiliation
, nil, false);
132 origin
.send(st
.error_reply(stanza
, err_type
, err_condition
));
138 local form_tag
= query
:get_child("x", "jabber:x:data");
139 local reg_data
= form_tag
and registration_form
:data(form_tag
);
141 origin
.send(st
.error_reply(stanza
, "modify", "bad-request", "Error in form"));
144 -- Is the nickname valid?
145 local desired_nick
= resourceprep(reg_data
["muc#register_roomnick"]);
146 if not desired_nick
then
147 origin
.send(st
.error_reply(stanza
, "modify", "bad-request", "Invalid Nickname"));
150 -- Is the nickname currently in use by another user?
151 local current_occupant
= room
:get_occupant_by_nick(room
.jid
.."/"..desired_nick
);
152 if current_occupant
and current_occupant
.bare_jid
~= user_jid
then
153 origin
.send(st
.error_reply(stanza
, "cancel", "conflict"));
156 -- Is the nickname currently reserved by another user?
157 local reserved_by
= get_registered_jid(room
, desired_nick
);
158 if reserved_by
and reserved_by
~= user_jid
then
159 origin
.send(st
.error_reply(stanza
, "cancel", "conflict"));
164 -- Kick any sessions that are not using this nick before we register it
165 local required_room_nick
= room
.jid
.."/"..desired_nick
;
166 for room_nick
, occupant
in room
:each_occupant() do
167 if occupant
.bare_jid
== user_jid
and room_nick
~= required_room_nick
then
168 room
:set_role(true, room_nick
, nil); -- Kick (TODO: would be nice to use 333 code)
173 -- Checks passed, save the registration
174 if registered_nick
~= desired_nick
then
175 local registration_data
= { reserved_nickname
= desired_nick
};
176 local ok
, err_type
, err_condition
= room
:set_affiliation(true, user_jid
, affiliation
or "member", nil, registration_data
);
178 origin
.send(st
.error_reply(stanza
, err_type
, err_condition
));
181 module
:log("debug", "Saved nick registration for %s: %s", user_jid
, desired_nick
);
191 get_registered_nick
= get_registered_nick
;
192 get_registered_jid
= get_registered_jid
;
193 handle_register_iq
= handle_register_iq
;