LCOV - code coverage report
Current view: top level - src/jamidht - transfer_channel_handler.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 88 107 82.2 %
Date: 2024-12-21 08:56:24 Functions: 6 9 66.7 %

          Line data    Source code
       1             : /*
       2             :  *  Copyright (C) 2004-2024 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             : 
      18             : #include "jamidht/transfer_channel_handler.h"
      19             : 
      20             : #include <charconv>
      21             : 
      22             : #include "fileutils.h"
      23             : 
      24             : namespace jami {
      25             : 
      26         678 : TransferChannelHandler::TransferChannelHandler(const std::shared_ptr<JamiAccount>& account,
      27         678 :                                                dhtnet::ConnectionManager& cm)
      28             :     : ChannelHandlerInterface()
      29         678 :     , account_(account)
      30         678 :     , connectionManager_(cm)
      31             : {
      32         678 :     if (auto acc = account_.lock())
      33         678 :         idPath_ = fileutils::get_data_dir() / acc->getAccountID();
      34         678 : }
      35             : 
      36        1356 : TransferChannelHandler::~TransferChannelHandler() {}
      37             : 
      38             : void
      39           0 : TransferChannelHandler::connect(const DeviceId& deviceId,
      40             :                                 const std::string& channelName,
      41             :                                 ConnectCb&& cb,
      42             :                                 const std::string& connectionType,
      43             :                                 bool forceNewConnection)
      44           0 : {}
      45             : 
      46             : bool
      47          67 : TransferChannelHandler::onRequest(const std::shared_ptr<dht::crypto::Certificate>& cert,
      48             :                                   const std::string& name)
      49             : {
      50          67 :     auto acc = account_.lock();
      51          67 :     if (!acc || !cert || !cert->issuer)
      52           0 :         return false;
      53          67 :     auto cm = acc->convModule(true);
      54          67 :     if (!cm)
      55           0 :         return false;
      56          67 :     auto uri = cert->issuer->getId().toString();
      57             :     // Else, check if it's a profile or file in a conversation.
      58          67 :     auto idstr = std::string_view(name).substr(DATA_TRANSFER_SCHEME.size());
      59             :     // Remove arguments for now
      60          67 :     auto sep = idstr.find_last_of('?');
      61          67 :     idstr = idstr.substr(0, sep);
      62          67 :     if (idstr == "profile.vcf") {
      63             :         // If it's our profile from another device
      64           2 :         return uri == acc->getUsername();
      65             :     }
      66          65 :     sep = idstr.find('/');
      67          65 :     auto lastSep = idstr.find_last_of('/');
      68          65 :     auto conversationId = std::string(idstr.substr(0, sep));
      69          65 :     auto fileHost = idstr.substr(sep + 1, lastSep - sep - 1);
      70          65 :     auto fileId = idstr.substr(lastSep + 1);
      71          65 :     if (fileHost == acc->currentDeviceId())
      72           0 :         return false;
      73             : 
      74             :     // Check if peer is member of the conversation
      75         130 :     if (fileId == fmt::format("{}.vcf", acc->getUsername()) || fileId == "profile.vcf") {
      76             :         // Or a member from the conversation
      77          42 :         auto members = cm->getConversationMembers(conversationId);
      78         120 :         return std::find_if(members.begin(), members.end(), [&](auto m) { return m["uri"] == uri; })
      79          84 :                != members.end();
      80          65 :     } else if (fileHost == "profile") {
      81             :         // If a profile is sent, check if it's from another device
      82          10 :         return uri == acc->getUsername();
      83             :     }
      84             : 
      85          13 :     return cm->onFileChannelRequest(conversationId, uri, std::string(fileId), acc->sha3SumVerify());
      86          67 : }
      87             : 
      88             : void
      89         126 : TransferChannelHandler::onReady(const std::shared_ptr<dht::crypto::Certificate>&,
      90             :                                 const std::string& name,
      91             :                                 std::shared_ptr<dhtnet::ChannelSocket> channel)
      92             : {
      93         126 :     auto acc = account_.lock();
      94         126 :     if (!acc)
      95           0 :         return;
      96             : 
      97             :     // Remove scheme
      98         126 :     auto idstr = name.substr(DATA_TRANSFER_SCHEME.size());
      99             :     // Parse arguments
     100         126 :     auto sep = idstr.find_last_of('?');
     101         126 :     std::string arguments;
     102         126 :     if (sep != std::string::npos) {
     103          14 :         arguments = idstr.substr(sep + 1);
     104          14 :         idstr = idstr.substr(0, sep);
     105             :     }
     106             : 
     107         126 :     auto start = 0u, end = 0u;
     108         126 :     uint64_t lastModified = 0;
     109         126 :     std::string sha3Sum;
     110         154 :     for (const auto arg : split_string(arguments, '&')) {
     111          28 :         auto keyVal = split_string(arg, '=');
     112          28 :         if (keyVal.size() == 2) {
     113          28 :             if (keyVal[0] == "start") {
     114           0 :                 start = to_int<unsigned>(keyVal[1]);
     115          28 :             } else if (keyVal[0] == "end") {
     116           0 :                 end = to_int<unsigned>(keyVal[1]);
     117          28 :             } else if (keyVal[0] == "sha3") {
     118          14 :                 sha3Sum = keyVal[1];
     119          14 :             } else if (keyVal[0] == "modified") {
     120             :                 try {
     121          14 :                     lastModified = jami::to_int<uint64_t>(keyVal[1]);
     122           0 :                 } catch (const std::exception& e) {
     123           0 :                     JAMI_WARNING("TransferChannel: Unable to parse modified date: {}: {}",
     124             :                                  keyVal[1],
     125             :                                  e.what());
     126           0 :                 }
     127             :             }
     128             :         }
     129         154 :     }
     130             : 
     131             :     // Check if profile
     132         126 :     if (idstr == "profile.vcf") {
     133           4 :         if (!channel->isInitiator()) {
     134             :             // Only accept newest profiles
     135           0 :             if (lastModified == 0
     136           4 :                 || lastModified > fileutils::lastWriteTimeInSeconds(acc->profilePath()))
     137           0 :                 acc->dataTransfer()->onIncomingProfile(channel, sha3Sum);
     138             :             else
     139           0 :                 channel->shutdown();
     140             :         } else {
     141             :             // If it's a profile from sync
     142           2 :             auto path = idPath_ / "profile.vcf";
     143           2 :             acc->dataTransfer()->transferFile(channel, idstr, "", path.string());
     144           2 :         }
     145           2 :         return;
     146             :     }
     147             : 
     148         122 :     auto splitted_id = split_string(idstr, '/');
     149         122 :     if (splitted_id.size() < 3) {
     150           0 :         JAMI_ERR() << "Unsupported ID detected " << name;
     151           0 :         channel->shutdown();
     152           0 :         return;
     153             :     }
     154             : 
     155             :     // convId/fileHost/fileId or convId/profile/fileId
     156         122 :     auto conversationId = std::string(splitted_id[0]);
     157         122 :     auto fileHost = std::string(splitted_id[1]);
     158         122 :     auto isContactProfile = splitted_id[1] == "profile";
     159         122 :     auto fileId = std::string(splitted_id[splitted_id.size() - 1]);
     160         122 :     if (channel->isInitiator())
     161          61 :         return;
     162             : 
     163             :     // Profile for a member in the conversation
     164         122 :     if (fileId == fmt::format("{}.vcf", acc->getUsername())) {
     165          36 :         auto path = idPath_ / "profile.vcf";
     166          36 :         acc->dataTransfer()->transferFile(channel, fileId, "", path.string());
     167          36 :         return;
     168          61 :     } else if (isContactProfile && fileId.find(".vcf") != std::string::npos) {
     169          18 :         auto path = acc->dataTransfer()->profilePath(fileId.substr(0, fileId.size() - 4));
     170           9 :         acc->dataTransfer()->transferFile(channel, fileId, "", path.string());
     171           9 :         return;
     172          25 :     } else if (fileId == "profile.vcf") {
     173           5 :         acc->dataTransfer()->onIncomingProfile(channel, sha3Sum);
     174           5 :         return;
     175             :     }
     176             :     // Check if it's a file in a conversation
     177          11 :     auto dt = acc->dataTransfer(conversationId);
     178          11 :     sep = fileId.find('_');
     179          11 :     if (!dt or sep == std::string::npos) {
     180           0 :         channel->shutdown();
     181           0 :         return;
     182             :     }
     183          11 :     auto interactionId = fileId.substr(0, sep);
     184          11 :     auto path = dt->path(fileId);
     185          11 :     dt->transferFile(channel, fileId, interactionId, path.string(), start, end);
     186         915 : }
     187             : 
     188             : } // namespace jami

Generated by: LCOV version 1.14