From a064b5deff13e5ffbf9c59c32676f9822bc53618 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Wed, 2 Sep 2026 03:18:58 +0200 Subject: [PATCH] imsd: validate an incoming INVITE before announcing it to the UI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OnIncomingInvite() posted CallAdded, then ran the engine's OnInvite(), which is where the offer is validated. For a refused offer (488) the bus saw CallAdded -> CallStateChanged(terminated) -> CallDeleted within one Execute() — a 24 ms burst — while the engine comment three lines above the 488 promised the refusal lands 'before the UI ever rings'. Field report 2026-09-01: kde-telephony-daemon reacted to the CallAdded by launching Plasma Dialer as a lock-screen overlay, which came up half a second later to an empty call list and never left; the user rebooted. Run OnInvite() first and announce the call only if the machine survived it. A refused call is answered and dropped with no bus events at all (Execute(actions, announce=false)); the journal keeps the record — caller and reason are already logged. --- implementations/main.cpp | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/implementations/main.cpp b/implementations/main.cpp index 38c6623..43b77f1 100644 --- a/implementations/main.cpp +++ b/implementations/main.cpp @@ -1201,16 +1201,32 @@ private: call_.emplace(ctx_, rng_, uni, imsd::engine::IncomingInvite{msg}, rtpPort_); callReply_ = reply_; Log(std::format("incoming call {} from {}", uni, call_->Number())); + // Validate the offer BEFORE the UI hears of the call. A refused + // INVITE (488: nothing we can play) is answered and dropped without + // ever announcing it — announcing first produced a 24 ms + // added/terminated/deleted burst that left Plasma Dialer stuck as a + // lock-screen overlay with no call to show (field report 2026-09-01). + // The journal keeps the record: caller and reason are logged above + // and by the engine's teardown line. + auto actions = call_->OnInvite(); + if (call_->Terminated()) { + Execute(actions, /*announce=*/false); + MaybeDropCall(); + return; + } CallInfo info{uni, call_->Number(), "incoming", "incoming", NowEpoch(), 0, "incoming"}; PostAdded(info); - Execute(call_->OnInvite()); + Execute(actions); MaybeDropCall(); } // ---- action execution ------------------------------------------------- // Returns false if a client-flow send failed (used to fail a dial's INVITE). - bool Execute(const std::vector& actions) { + // `announce` false suppresses the D-Bus state/deleted events — for a call + // that was never announced (refused before ringing), so the bus never + // sees a call it cannot show. + bool Execute(const std::vector& actions, bool announce = true) { using T = imsd::engine::Action::Type; bool sendOk = true; for (const auto& a : actions) { @@ -1224,8 +1240,13 @@ private: break; case T::StartMedia: StartMedia(a.media); break; case T::StopMedia: StopMedia(); break; - case T::State: PostState(call_->Uni(), a.state, a.reason); break; - case T::Deleted: PostDeleted(call_->Uni()); dropCall_ = true; break; + case T::State: + if (announce) PostState(call_->Uni(), a.state, a.reason); + break; + case T::Deleted: + if (announce) PostDeleted(call_->Uni()); + dropCall_ = true; + break; case T::SetDeadline: deadline_ = Mono() + a.seconds; break; case T::Log: Log(a.text); break; }