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(); |