LCOV - code coverage report
Current view: top level - src - data_transfer.h (source / functions) Coverage Total Hit
Test: jami-coverage-filtered.info Lines: 83.3 % 6 5
Test Date: 2026-09-13 09:08:58 Functions: 62.5 % 8 5

            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              : #pragma once
      18              : 
      19              : #include "jami/datatransfer_interface.h"
      20              : #include "noncopyable.h"
      21              : 
      22              : #include <dhtnet/multiplexed_socket.h>
      23              : 
      24              : #include <fstream>
      25              : #include <functional>
      26              : #include <memory>
      27              : #include <string>
      28              : 
      29              : namespace jami {
      30              : 
      31              : libjami::DataTransferId generateUID(std::mt19937_64& engine);
      32              : 
      33              : /**
      34              :  * Build the identifier (and on-disk name) of a transferred file from its commit.
      35              :  * @return "<commitId>_<tid>[.<extension>]", or an empty string when tid is not the decimal
      36              :  *         transfer id generated by the sender, so a forged commit can never name a file.
      37              :  */
      38              : std::string getFileId(const std::string& commitId, const std::string& tid, const std::string& displayName);
      39              : 
      40              : /**
      41              :  * Check that a file transfer identifier, whether built locally or received from a
      42              :  * peer, is a plain file name of the form produced by getFileId(): it is joined to
      43              :  * the conversation data directory so it MUST NOT contain any path component.
      44              :  */
      45              : bool isValidFileId(std::string_view fileId) noexcept;
      46              : 
      47              : class Stream;
      48              : 
      49              : struct IncomingFileInfo
      50              : {
      51              :     libjami::DataTransferId id;
      52              :     std::shared_ptr<Stream> stream;
      53              : };
      54              : 
      55              : struct WaitingRequest
      56              : {
      57              :     std::string fileId;
      58              :     std::string interactionId;
      59              :     std::string sha3sum;
      60              :     std::string path;
      61              :     std::size_t totalSize;
      62           14 :     MSGPACK_DEFINE(fileId, interactionId, sha3sum, path, totalSize)
      63              : };
      64              : 
      65              : typedef std::function<void(const std::string&)> InternalCompletionCb;
      66              : typedef std::function<void()> OnFinishedCb;
      67              : 
      68              : class FileInfo
      69              : {
      70              : public:
      71              :     FileInfo(const std::shared_ptr<dhtnet::ChannelSocket>& channel,
      72              :              const std::string& fileId,
      73              :              const std::string& interactionId,
      74              :              const libjami::DataTransferInfo& info);
      75          156 :     virtual ~FileInfo() {}
      76              :     virtual void process() = 0;
      77              :     std::shared_ptr<dhtnet::ChannelSocket> channel() const { return channel_; }
      78            0 :     libjami::DataTransferInfo info() const { return info_; }
      79              :     virtual void cancel() = 0;
      80          156 :     void onFinished(std::function<void(uint32_t)>&& cb) { finishedCb_ = std::move(cb); }
      81              :     void emit(libjami::DataTransferEventCode code);
      82              : 
      83              : protected:
      84              :     std::atomic_bool isUserCancelled_ {false};
      85              :     std::string fileId_ {};
      86              :     std::string interactionId_ {};
      87              :     libjami::DataTransferInfo info_ {};
      88              :     std::shared_ptr<dhtnet::ChannelSocket> channel_ {};
      89              :     std::function<void(uint32_t)> finishedCb_ {};
      90              : };
      91              : 
      92              : class IncomingFile : public FileInfo, public std::enable_shared_from_this<IncomingFile>
      93              : {
      94              : public:
      95              :     /** Moves the verified partial file into place; false if the destination is unusable. */
      96              :     using InstallCb = std::function<bool(const std::filesystem::path& partial)>;
      97              : 
      98              :     IncomingFile(const std::shared_ptr<dhtnet::ChannelSocket>& channel,
      99              :                  const libjami::DataTransferInfo& info,
     100              :                  const std::string& fileId,
     101              :                  const std::string& interactionId,
     102              :                  const std::string& sha3Sum,
     103              :                  const std::filesystem::path& temporaryPath);
     104              :     ~IncomingFile();
     105              :     void process() override;
     106              :     void cancel() override;
     107           13 :     void onInstall(InstallCb&& cb) { installCb_ = std::move(cb); }
     108              : 
     109              : private:
     110              :     std::mutex streamMtx_;
     111              :     std::ofstream stream_;
     112              :     std::string sha3Sum_ {};
     113              :     std::filesystem::path path_;
     114              :     InstallCb installCb_ {};
     115              : };
     116              : 
     117              : class OutgoingFile : public FileInfo
     118              : {
     119              : public:
     120              :     OutgoingFile(const std::shared_ptr<dhtnet::ChannelSocket>& channel,
     121              :                  const std::string& fileId,
     122              :                  const std::string& interactionId,
     123              :                  const libjami::DataTransferInfo& info,
     124              :                  size_t start = 0,
     125              :                  size_t end = 0);
     126              :     ~OutgoingFile();
     127              :     void process() override;
     128              :     void cancel() override;
     129              : 
     130              : private:
     131              :     std::ifstream stream_;
     132              :     size_t start_ {0};
     133              :     size_t end_ {0};
     134              : };
     135              : 
     136              : class TransferManager : public std::enable_shared_from_this<TransferManager>
     137              : {
     138              : public:
     139              :     enum class WaitResult { waiting, complete, conflict };
     140              : 
     141              :     TransferManager(const std::string& accountId,
     142              :                     const std::string& accountUri,
     143              :                     const std::string& to,
     144              :                     const std::mt19937_64& rand);
     145              :     ~TransferManager();
     146              : 
     147              :     /**
     148              :      * Send a file to a channel
     149              :      * @param channel       channel to use
     150              :      * @param fileId        fileId of the transfer
     151              :      * @param interactionId interactionId of the transfer
     152              :      * @param path          path of the file
     153              :      * @param start         start offset
     154              :      * @param end           end
     155              :      */
     156              :     void transferFile(const std::shared_ptr<dhtnet::ChannelSocket>& channel,
     157              :                       const std::string& fileId,
     158              :                       const std::string& interactionId,
     159              :                       const std::string& path,
     160              :                       size_t start = 0,
     161              :                       size_t end = 0,
     162              :                       OnFinishedCb onFinished = {});
     163              : 
     164              :     /**
     165              :      * Refuse a transfer
     166              :      * @param id        of the transfer
     167              :      */
     168              :     bool cancel(const std::string& fileId);
     169              : 
     170              :     /**
     171              :      * Get current transfer info
     172              :      * @param id        of the transfer
     173              :      * @param total     size
     174              :      * @param path      path of the file
     175              :      * @param progress  current progress
     176              :      * @return if found
     177              :      */
     178              :     bool info(const std::string& fileId, std::string& path, int64_t& total, int64_t& progress) const noexcept;
     179              : 
     180              :     /**
     181              :      * Make the transfer index resolve to the expected content, indexing `candidate` if needed.
     182              :      * @param fileId        id of the transfer
     183              :      * @param candidate     file already verified by the caller to hold the expected content
     184              :      * @param sha3sum       expected SHA3-512 digest
     185              :      * @param total         expected file size
     186              :      * @param independent   require independent storage using a hard link or copy
     187              :      * @return true if the index resolves to the expected file
     188              :      */
     189              :     bool indexFile(const std::string& fileId,
     190              :                    const std::filesystem::path& candidate,
     191              :                    const std::string& sha3sum,
     192              :                    std::size_t total,
     193              :                    bool independent = false);
     194              : 
     195              :     /**
     196              :      * Request a file at a destination.
     197              :      * @param id              of the transfer
     198              :      * @param interactionId   linked interaction
     199              :      * @param sha3sum         attended sha3sum
     200              :      * @param path            where the file will be downloaded, the index entry if empty
     201              :      * @param total           total size of the file
     202              :      * @return complete if the destination already holds the file, conflict if it cannot,
     203              :      *         waiting if the transfer is now waited (and will be automatically accepted)
     204              :      */
     205              :     WaitResult waitForTransfer(const std::string& fileId,
     206              :                                const std::string& interactionId,
     207              :                                const std::string& sha3sum,
     208              :                                const std::string& path,
     209              :                                std::size_t total);
     210              : 
     211              :     /**
     212              :      * Handle incoming transfer
     213              :      * @param id        Related id
     214              :      * @param channel   Related channel
     215              :      * @param start     Offset in the file from which the transfer will start.
     216              :      */
     217              :     void onIncomingFileTransfer(const std::string& fileId,
     218              :                                 const std::shared_ptr<dhtnet::ChannelSocket>& channel,
     219              :                                 size_t start);
     220              : 
     221              :     /**
     222              :      * Retrieve path of a file
     223              :      * @param id
     224              :      */
     225              :     std::filesystem::path path(const std::string& fileId) const;
     226              : 
     227              :     /**
     228              :      * Retrieve the private partial-download path for a transfer destination.
     229              :      */
     230              :     std::filesystem::path temporaryPath(const std::string& fileId, const std::filesystem::path& destination) const;
     231              : 
     232              :     /**
     233              :      * Retrieve waiting files
     234              :      * @return waiting list
     235              :      */
     236              :     std::vector<WaitingRequest> waitingRequests() const;
     237              :     bool isWaiting(const std::string& fileId) const;
     238              :     void onIncomingProfile(const std::shared_ptr<dhtnet::ChannelSocket>& channel, const std::string& sha3Sum = "");
     239              : 
     240              :     /**
     241              :      * @param contactId     contact's id
     242              :      * @return where profile.vcf is stored
     243              :      */
     244              :     std::filesystem::path profilePath(const std::string& contactId) const;
     245              : 
     246              : private:
     247          169 :     std::weak_ptr<TransferManager> weak() { return std::static_pointer_cast<TransferManager>(shared_from_this()); }
     248              :     bool installIndex(const std::string& fileId,
     249              :                       const std::filesystem::path& candidate,
     250              :                       const std::string& sha3sum,
     251              :                       std::size_t total,
     252              :                       bool independent,
     253              :                       bool verifyCandidate);
     254              :     /** Make `destination` hold the indexed content. */
     255              :     bool exportFile(const std::string& fileId,
     256              :                     const std::filesystem::path& destination,
     257              :                     const std::string& sha3sum,
     258              :                     std::size_t total);
     259              :     /** Install a verified partial download at `destination` and index it. */
     260              :     bool installTransfer(const std::string& fileId,
     261              :                          const std::filesystem::path& partial,
     262              :                          const std::filesystem::path& destination,
     263              :                          const std::string& sha3sum,
     264              :                          std::size_t total);
     265              :     NON_COPYABLE(TransferManager);
     266              :     class Impl;
     267              :     std::unique_ptr<Impl> pimpl_;
     268              : };
     269              : 
     270              : } // namespace jami
        

Generated by: LCOV version 2.0-1