LCOV - code coverage report
Current view: top level - src/jamidht - message_channel_handler.cpp (source / functions) Coverage Total Hit
Test: jami-coverage-filtered.info Lines: 94.5 % 127 120
Test Date: 2026-08-23 08:52:56 Functions: 100.0 % 14 14

            Line data    Source code
       1              : /*
       2              :  *  Copyright (C) 2004-2026 Savoir-faire Linux Inc.
       3              :  *
       4              :  *  This program is free software: you can redistribute it and/or modify
       5              :  *  it under the terms of the GNU General Public License as published by
       6              :  *  the Free Software Foundation, either version 3 of the License, or
       7              :  *  (at your option) any later version.
       8              :  *
       9              :  *  This program is distributed in the hope that it will be useful,
      10              :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      11              :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
      12              :  *  GNU General Public License for more details.
      13              :  *
      14              :  *  You should have received a copy of the GNU General Public License
      15              :  *  along with this program. If not, see <https://www.gnu.org/licenses/>.
      16              :  */
      17              : #include "jamidht/message_channel_handler.h"
      18              : 
      19              : #include <dhtnet/channel_utils.h>
      20              : #include <string_view>
      21              : 
      22              : using namespace std::literals;
      23              : 
      24              : static constexpr auto MESSAGE_SCHEME = "msg:"sv;
      25              : 
      26              : namespace jami {
      27              : 
      28              : using Key = std::pair<std::string, DeviceId>;
      29              : 
      30              : struct MessageChannelHandler::Impl : public std::enable_shared_from_this<Impl>
      31              : {
      32              :     dhtnet::ConnectionManager& connectionManager_;
      33              :     OnMessage onMessage_;
      34              :     OnPeerStateChanged onPeerStateChanged_;
      35              :     OnDeviceConnected onDeviceConnected_;
      36              :     std::recursive_mutex connectionsMtx_;
      37              :     std::map<std::string, std::map<DeviceId, std::vector<std::shared_ptr<dhtnet::ChannelSocket>>>> connections_;
      38              : 
      39          721 :     Impl(dhtnet::ConnectionManager& cm, OnMessage onMessage, OnPeerStateChanged onPeer, OnDeviceConnected onDevice)
      40         1442 :         : connectionManager_(cm)
      41          721 :         , onMessage_(std::move(onMessage))
      42          721 :         , onPeerStateChanged_(std::move(onPeer))
      43         1442 :         , onDeviceConnected_(std::move(onDevice))
      44          721 :     {}
      45              : 
      46              :     void onChannelShutdown(const std::shared_ptr<dhtnet::ChannelSocket>& socket,
      47              :                            const std::string& peerId,
      48              :                            const DeviceId& device);
      49              : };
      50              : 
      51          721 : MessageChannelHandler::MessageChannelHandler(dhtnet::ConnectionManager& cm,
      52              :                                              OnMessage onMessage,
      53              :                                              OnPeerStateChanged onPeer,
      54          721 :                                              OnDeviceConnected onDeviceConnected)
      55              :     : ChannelHandlerInterface()
      56          721 :     , pimpl_(std::make_shared<Impl>(cm, std::move(onMessage), std::move(onPeer), std::move(onDeviceConnected)))
      57          721 : {}
      58              : 
      59         1442 : MessageChannelHandler::~MessageChannelHandler()
      60              : {
      61          721 :     std::unique_lock lk(pimpl_->connectionsMtx_);
      62         1334 :     for (const auto& [peerId, _] : pimpl_->connections_) {
      63          613 :         pimpl_->onPeerStateChanged_(peerId, false);
      64              :     }
      65          721 :     auto connections = std::move(pimpl_->connections_);
      66          721 :     pimpl_->connections_.clear();
      67          721 :     lk.unlock();
      68         1442 : }
      69              : 
      70              : void
      71         3617 : MessageChannelHandler::connect(const DeviceId& deviceId,
      72              :                                const std::string&,
      73              :                                ConnectCb&& cb,
      74              :                                const std::string& connectionType,
      75              :                                bool forceNewConnection)
      76              : {
      77         3617 :     auto channelName = concat(MESSAGE_SCHEME, deviceId.to_view());
      78         3617 :     if (pimpl_->connectionManager_.isConnecting(deviceId, channelName)) {
      79         1278 :         JAMI_LOG("Already connecting to {}", deviceId);
      80         1278 :         return;
      81              :     }
      82         2340 :     pimpl_->connectionManager_
      83         2340 :         .connectDevice(deviceId, channelName, std::move(cb), false, forceNewConnection, connectionType);
      84         3618 : }
      85              : 
      86              : void
      87         1352 : MessageChannelHandler::Impl::onChannelShutdown(const std::shared_ptr<dhtnet::ChannelSocket>& socket,
      88              :                                                const std::string& peerId,
      89              :                                                const DeviceId& device)
      90              : {
      91         1352 :     std::lock_guard lk(connectionsMtx_);
      92         1352 :     auto peerIt = connections_.find(peerId);
      93         1352 :     if (peerIt == connections_.end()) {
      94           82 :         JAMI_WARNING("onChannelShutdown: No connections found for peer {}", peerId);
      95           82 :         return;
      96              :     }
      97         1270 :     auto connectionsIt = peerIt->second.find(device);
      98         1270 :     if (connectionsIt == peerIt->second.end()) {
      99            0 :         JAMI_WARNING("onChannelShutdown: No connections found for device {} of peer {}", device.toString(), peerId);
     100            0 :         return;
     101              :     }
     102         1270 :     auto& connections = connectionsIt->second;
     103         1270 :     auto conn = std::find(connections.begin(), connections.end(), socket);
     104         1270 :     if (conn != connections.end())
     105         1270 :         connections.erase(conn);
     106         1270 :     if (connections.empty()) {
     107          636 :         peerIt->second.erase(connectionsIt);
     108              :     }
     109         1270 :     if (peerIt->second.empty()) {
     110          632 :         connections_.erase(peerIt);
     111          632 :         onPeerStateChanged_(peerId, false);
     112              :     }
     113         1352 : }
     114              : 
     115              : std::shared_ptr<dhtnet::ChannelSocket>
     116        16066 : MessageChannelHandler::getChannel(const std::string& peer, const DeviceId& deviceId) const
     117              : {
     118        16066 :     std::lock_guard lk(pimpl_->connectionsMtx_);
     119        16065 :     auto it = pimpl_->connections_.find(peer);
     120        16066 :     if (it == pimpl_->connections_.end())
     121         3608 :         return nullptr;
     122        12458 :     auto deviceIt = it->second.find(deviceId);
     123        12458 :     if (deviceIt == it->second.end())
     124           19 :         return nullptr;
     125        12439 :     if (deviceIt->second.empty())
     126            0 :         return nullptr;
     127        12439 :     return deviceIt->second.back();
     128        16066 : }
     129              : 
     130              : std::vector<std::shared_ptr<dhtnet::ChannelSocket>>
     131         3096 : MessageChannelHandler::getChannels(const std::string& peer) const
     132              : {
     133         3096 :     std::vector<std::shared_ptr<dhtnet::ChannelSocket>> sockets;
     134         3096 :     std::lock_guard lk(pimpl_->connectionsMtx_);
     135         3096 :     auto it = pimpl_->connections_.find(peer);
     136         3096 :     if (it == pimpl_->connections_.end())
     137         1902 :         return sockets;
     138         1194 :     sockets.reserve(it->second.size());
     139         2389 :     for (auto& [deviceId, channels] : it->second) {
     140         3345 :         for (auto& channel : channels) {
     141         2150 :             sockets.push_back(channel);
     142              :         }
     143              :     }
     144         1194 :     return sockets;
     145         3096 : }
     146              : 
     147              : bool
     148         1258 : MessageChannelHandler::onRequest(const std::shared_ptr<dht::crypto::Certificate>& cert, const std::string& /* name */)
     149              : {
     150         1258 :     if (!cert || !cert->issuer)
     151            0 :         return false;
     152         1256 :     return true;
     153              : }
     154              : 
     155              : void
     156         2506 : MessageChannelHandler::onReady(const std::shared_ptr<dht::crypto::Certificate>& cert,
     157              :                                const std::string&,
     158              :                                std::shared_ptr<dhtnet::ChannelSocket> socket)
     159              : {
     160         2506 :     if (!cert || !cert->issuer)
     161            0 :         return;
     162         2505 :     auto peerId = cert->issuer->getId().toString();
     163         2508 :     auto device = cert->getLongId();
     164         2505 :     std::lock_guard lk(pimpl_->connectionsMtx_);
     165         2508 :     auto& connections = pimpl_->connections_[peerId];
     166         2508 :     bool newPeerConnection = connections.empty();
     167         2504 :     auto& deviceConnections = connections[device];
     168         2509 :     bool newDeviceConnection = deviceConnections.empty();
     169         2507 :     deviceConnections.push_back(socket);
     170         2503 :     if (newPeerConnection)
     171         1242 :         pimpl_->onPeerStateChanged_(peerId, true);
     172         2509 :     if (newDeviceConnection)
     173         1259 :         pimpl_->onDeviceConnected_(peerId, device);
     174              : 
     175         2509 :     socket->setOnRecv(dhtnet::buildMsgpackReader<Message>([onMessage = pimpl_->onMessage_, cert](Message&& msg) {
     176        12392 :         onMessage(cert, msg.t, msg.c);
     177        12392 :         return std::error_code();
     178              :     }));
     179              : 
     180         5015 :     socket->onShutdown(
     181         5015 :         [w = pimpl_->weak_from_this(), peerId, device, s = std::weak_ptr(socket)](const std::error_code& /*ec*/) {
     182         2509 :             if (auto shared = w.lock())
     183         2509 :                 shared->onChannelShutdown(s.lock(), peerId, device);
     184         2509 :         });
     185         2504 : }
     186              : 
     187              : void
     188            4 : MessageChannelHandler::closeChannel(const std::string& peer,
     189              :                                     const DeviceId& device,
     190              :                                     const std::shared_ptr<dhtnet::ChannelSocket>& conn)
     191              : {
     192            4 :     if (!conn)
     193            0 :         return;
     194            4 :     std::unique_lock lk(pimpl_->connectionsMtx_);
     195            4 :     auto it = pimpl_->connections_.find(peer);
     196            4 :     if (it != pimpl_->connections_.end()) {
     197            2 :         auto deviceIt = it->second.find(device);
     198            2 :         if (deviceIt != it->second.end()) {
     199            2 :             auto& channels = deviceIt->second;
     200            2 :             channels.erase(std::remove(channels.begin(), channels.end(), conn), channels.end());
     201            2 :             if (channels.empty()) {
     202            1 :                 it->second.erase(deviceIt);
     203            1 :                 if (it->second.empty()) {
     204            1 :                     pimpl_->connections_.erase(it);
     205            1 :                     pimpl_->onPeerStateChanged_(peer, false);
     206              :                 }
     207              :             }
     208              :         }
     209              :     }
     210            4 :     lk.unlock();
     211            4 :     conn->stop();
     212            4 : }
     213              : 
     214              : bool
     215        12404 : MessageChannelHandler::sendMessage(const std::shared_ptr<dhtnet::ChannelSocket>& socket, const Message& message)
     216              : {
     217        12404 :     if (!socket)
     218            0 :         return false;
     219        12404 :     msgpack::sbuffer buffer(UINT16_MAX); // Use max
     220        12404 :     msgpack::pack(buffer, message);
     221        12403 :     std::error_code ec;
     222        12403 :     auto sent = socket->write(reinterpret_cast<const uint8_t*>(buffer.data()), buffer.size(), ec);
     223        12403 :     if (ec) {
     224            4 :         JAMI_WARNING("Error sending message: {:s}", ec.message());
     225              :     }
     226        12403 :     return !ec && sent == buffer.size();
     227        12403 : }
     228              : 
     229              : } // namespace jami
        

Generated by: LCOV version 2.0-1