Software /
code /
prosody
Comparison
plugins/mod_smacks.lua @ 12688:36ba170c4fd0
mod_smacks: Split enable handling to stages, to allow easier SASL2 integration
author | Matthew Wild <mwild1@gmail.com> |
---|---|
date | Mon, 29 Aug 2022 15:45:52 +0100 |
parent | 12682:464a22f2751c |
child | 12689:1bc2220cd6ec |
comparison
equal
deleted
inserted
replaced
12687:5b69ecaf3427 | 12688:36ba170c4fd0 |
---|---|
105 tail = { condition = "undefined-condition"; text = "Client acknowledged less stanzas than already acknowledged" }; | 105 tail = { condition = "undefined-condition"; text = "Client acknowledged less stanzas than already acknowledged" }; |
106 pop = { condition = "internal-server-error"; text = "Something went wrong with Stream Management" }; | 106 pop = { condition = "internal-server-error"; text = "Something went wrong with Stream Management" }; |
107 overflow = { condition = "resource-constraint", text = "Too many unacked stanzas remaining, session can't be resumed" } | 107 overflow = { condition = "resource-constraint", text = "Too many unacked stanzas remaining, session can't be resumed" } |
108 }); | 108 }); |
109 | 109 |
110 local resume_errors = require "util.error".init("mod_smacks", xmlns_sm3, { | 110 local enable_errors = require "util.error".init("mod_smacks", xmlns_sm3, { |
111 already_enabled = { condition = "unexpected-request", text = "Stream management is already enabled" }; | |
112 bind_required = { condition = "unexpected-request", text = "Client must bind a resource before enabling stream management" }; | |
113 unavailable = { condition = "service-unavailable", text = "Stream management is not available for this stream" }; | |
114 -- Resumption | |
111 expired = { condition = "item-not-found", text = "Session expired, and cannot be resumed" }; | 115 expired = { condition = "item-not-found", text = "Session expired, and cannot be resumed" }; |
112 already_bound = { condition = "unexpected-request", text = "Cannot resume another session after a resource is bound" }; | 116 already_bound = { condition = "unexpected-request", text = "Cannot resume another session after a resource is bound" }; |
113 unknown_session = { condition = "item-not-found", text = "Unknown session" }; | 117 unknown_session = { condition = "item-not-found", text = "Unknown session" }; |
114 }); | 118 }); |
115 | 119 |
125 end | 129 end |
126 session.delayed_ack_timer = nil; | 130 session.delayed_ack_timer = nil; |
127 end | 131 end |
128 | 132 |
129 local function can_do_smacks(session, advertise_only) | 133 local function can_do_smacks(session, advertise_only) |
130 if session.smacks then return false, "unexpected-request", "Stream management is already enabled"; end | 134 if session.smacks then return false, enable_errors.new("already_enabled"); end |
131 | 135 |
132 local session_type = session.type; | 136 local session_type = session.type; |
133 if session.username then | 137 if session.username then |
134 if not(advertise_only) and not(session.resource) then -- Fail unless we're only advertising sm | 138 if not(advertise_only) and not(session.resource) then -- Fail unless we're only advertising sm |
135 return false, "unexpected-request", "Client must bind a resource before enabling stream management"; | 139 return false, enable_errors.new("bind_required"); |
136 end | 140 end |
137 return true; | 141 return true; |
138 elseif s2s_smacks and (session_type == "s2sin" or session_type == "s2sout") then | 142 elseif s2s_smacks and (session_type == "s2sin" or session_type == "s2sout") then |
139 return true; | 143 return true; |
140 end | 144 end |
141 return false, "service-unavailable", "Stream management is not available for this stream"; | 145 return false, enable_errors.new("unavailable"); |
142 end | 146 end |
143 | 147 |
144 module:hook("stream-features", | 148 module:hook("stream-features", |
145 function (event) | 149 function (event) |
146 if can_do_smacks(event.origin, true) then | 150 if can_do_smacks(event.origin, true) then |
292 wrap_session_out(session, resume); | 296 wrap_session_out(session, resume); |
293 wrap_session_in(session, resume); | 297 wrap_session_in(session, resume); |
294 return session; | 298 return session; |
295 end | 299 end |
296 | 300 |
297 function handle_enable(session, stanza, xmlns_sm) | 301 function do_enable(session, stanza) |
298 local ok, err, err_text = can_do_smacks(session); | 302 local ok, err = can_do_smacks(session); |
299 if not ok then | 303 if not ok then |
300 session.log("warn", "Failed to enable smacks: %s", err_text); -- TODO: XEP doesn't say we can send error text, should it? | 304 session.log("warn", "Failed to enable smacks: %s", err.text); -- TODO: XEP doesn't say we can send error text, should it? |
301 (session.sends2s or session.send)(st.stanza("failed", { xmlns = xmlns_sm }):tag(err, { xmlns = xmlns_errors})); | 305 return nil, err; |
302 return true; | |
303 end | 306 end |
304 | 307 |
305 if session.username then | 308 if session.username then |
306 local old_sessions, err = all_old_sessions:get(session.username); | 309 local old_sessions, err = all_old_sessions:get(session.username); |
307 session.log("debug", "Old sessions: %q", old_sessions) | 310 session.log("debug", "Old sessions: %q", old_sessions) |
318 elseif err then | 321 elseif err then |
319 session.log("error", "Unable to retrieve old resumption counters: %s", err); | 322 session.log("error", "Unable to retrieve old resumption counters: %s", err); |
320 end | 323 end |
321 end | 324 end |
322 | 325 |
323 session.log("debug", "Enabling stream management"); | |
324 session.smacks = xmlns_sm; | |
325 | |
326 wrap_session(session, false); | |
327 | |
328 local resume_max; | |
329 local resume_token; | 326 local resume_token; |
330 local resume = stanza.attr.resume; | 327 local resume = stanza.attr.resume; |
331 if resume == "true" or resume == "1" then | 328 if resume == "true" or resume == "1" then |
332 resume_token = new_id(); | 329 resume_token = new_id(); |
333 track_session(session, resume_token); | 330 end |
334 resume_max = tostring(resume_timeout); | 331 |
335 end | 332 return { |
336 (session.sends2s or session.send)(st.stanza("enabled", { xmlns = xmlns_sm, id = resume_token, resume = resume, max = resume_max })); | 333 id = resume_token; |
334 resume_max = resume_token and tostring(resume_timeout) or nil; | |
335 session = session; | |
336 finish = function () | |
337 session.log("debug", "Enabling stream management"); | |
338 | |
339 track_session(session, resume_token); | |
340 wrap_session(session, false); | |
341 | |
342 end; | |
343 }; | |
344 end | |
345 | |
346 function handle_enable(session, stanza, xmlns_sm) | |
347 local enabled, err = do_enable(session, stanza); | |
348 if not enabled then | |
349 (session.sends2s or session.send)(st.stanza("failed", { xmlns = xmlns_sm }):add_error(err)); | |
350 return true; | |
351 end | |
352 | |
353 session.smacks = xmlns_sm; | |
354 | |
355 (session.sends2s or session.send)(st.stanza("enabled", { | |
356 xmlns = xmlns_sm; | |
357 id = enabled.id; | |
358 resume = enabled.id and "1" or nil; | |
359 max = enabled.resume_max; | |
360 })); | |
361 | |
362 enabled.finish(); | |
363 | |
337 return true; | 364 return true; |
338 end | 365 end |
339 module:hook_tag(xmlns_sm2, "enable", function (session, stanza) return handle_enable(session, stanza, xmlns_sm2); end, 100); | 366 module:hook_tag(xmlns_sm2, "enable", function (session, stanza) return handle_enable(session, stanza, xmlns_sm2); end, 100); |
340 module:hook_tag(xmlns_sm3, "enable", function (session, stanza) return handle_enable(session, stanza, xmlns_sm3); end, 100); | 367 module:hook_tag(xmlns_sm3, "enable", function (session, stanza) return handle_enable(session, stanza, xmlns_sm3); end, 100); |
341 | 368 |
534 module:hook("s2sin-destroyed", handle_s2s_destroyed); | 561 module:hook("s2sin-destroyed", handle_s2s_destroyed); |
535 | 562 |
536 function do_resume(session, stanza) | 563 function do_resume(session, stanza) |
537 if session.full_jid then | 564 if session.full_jid then |
538 session.log("warn", "Tried to resume after resource binding"); | 565 session.log("warn", "Tried to resume after resource binding"); |
539 return nil, resume_errors.new("already_bound"); | 566 return nil, enable_errors.new("already_bound"); |
540 end | 567 end |
541 | 568 |
542 local id = stanza.attr.previd; | 569 local id = stanza.attr.previd; |
543 local original_session = session_registry[jid.join(session.username, session.host, id)]; | 570 local original_session = session_registry[jid.join(session.username, session.host, id)]; |
544 if not original_session then | 571 if not original_session then |
545 local old_session = old_session_registry:get(session.username, id); | 572 local old_session = old_session_registry:get(session.username, id); |
546 if old_session then | 573 if old_session then |
547 session.log("debug", "Tried to resume old expired session with id %s", id); | 574 session.log("debug", "Tried to resume old expired session with id %s", id); |
548 clear_old_session(session, id); | 575 clear_old_session(session, id); |
549 resumption_expired(1); | 576 resumption_expired(1); |
550 return nil, resume_errors.new("expired", { h = old_session.h }); | 577 return nil, enable_errors.new("expired", { h = old_session.h }); |
551 end | 578 end |
552 session.log("debug", "Tried to resume non-existent session with id %s", id); | 579 session.log("debug", "Tried to resume non-existent session with id %s", id); |
553 return nil, resume_errors.new("unknown_session"); | 580 return nil, enable_errors.new("unknown_session"); |
554 end | 581 end |
555 | 582 |
556 if original_session.hibernating_watchdog then | 583 if original_session.hibernating_watchdog then |
557 original_session.log("debug", "Letting the watchdog go"); | 584 original_session.log("debug", "Letting the watchdog go"); |
558 original_session.hibernating_watchdog:cancel(); | 585 original_session.hibernating_watchdog:cancel(); |