68 const std::string& fileId,
69 const std::string& interactionId,
73 :
FileInfo(channel, fileId, interactionId, info)
78 if (!std::filesystem::is_regular_file(
fpath)) {
84 stream_.open(
fpath, std::ios::binary | std::ios::in);
85 if (!stream_ || !stream_.is_open()) {
197 if (
auto shared = w.lock()) {
199 if (shared->stream_.is_open())
200 shared->stream_.write(reinterpret_cast<const char*>(buf), len);
201 shared->info_.bytesProgress = shared->stream_.tellp();
206 auto shared = w.lock();
210 std::lock_guard<std::mutex>
lk(shared->streamMtx_);
211 if (shared->stream_ && shared->stream_.is_open())
212 shared->stream_.close();
214 auto correct = shared->sha3Sum_.empty();
217 if (shared->isUserCancelled_) {
218 std::filesystem::remove(shared->path_,
ec);
219 }
else if (shared->info_.bytesProgress < shared->info_.totalSize) {
220 JAMI_WARNING(
"Channel for {} shut down before transfer was complete (progress: {}/{})", shared->info_.path, shared->info_.bytesProgress, shared->info_.totalSize);
221 }
else if (shared->info_.totalSize != 0 && shared->info_.bytesProgress > shared->info_.totalSize) {
222 JAMI_WARNING(
"Removing {} larger than announced: {}/{}", shared->path_, shared->info_.bytesProgress, shared->info_.totalSize);
223 std::filesystem::remove(shared->path_,
ec);
226 if (shared->sha3Sum_ ==
sha3Sum) {
227 JAMI_LOG(
"New file received: {}", shared->info_.path);
230 JAMI_WARNING(
"Removing {} with expected size ({} bytes) but invalid sha3sum (expected: {}, actual: {})",
231 shared->path_, shared->info_.totalSize, shared->sha3Sum_,
sha3Sum);
232 std::filesystem::remove(shared->path_,
ec);
236 JAMI_ERROR(
"Failed to remove file {}: {}", shared->path_,
ec.message());
240 std::filesystem::rename(shared->path_, shared->info_.path,
ec);
242 JAMI_ERROR(
"Failed to rename file from {} to {}: {}", shared->path_, shared->info_.path,
ec.message());
246 if (shared->isUserCancelled_)
248 auto code =
correct ? libjami::DataTransferEventCode::finished
249 : libjami::DataTransferEventCode::closed_by_host;
337 const std::string& fileId,
338 const std::string& interactionId,
339 const std::string& path,
344 std::lock_guard
lk {pimpl_->mapMutex_};
345 if (pimpl_->outgoings_.find(channel) != pimpl_->outgoings_.end())
349 info.conversationId = pimpl_->to_;
351 auto f = std::make_shared<OutgoingFile>(channel, fileId, interactionId,
info, start, end);
352 f->onFinished([w = weak(), channel, onFinished = std::move(onFinished)](
uint32_t code) {
353 if (code ==
uint32_t(libjami::DataTransferEventCode::finished) && onFinished) {
357 dht::ThreadPool().computation().run([w, channel] {
358 if (
auto sthis_ = w.lock()) {
359 auto& pimpl = sthis_->pimpl_;
360 std::lock_guard lk {pimpl->mapMutex_};
361 auto itO =
pimpl->outgoings_.find(channel);
367 pimpl_->outgoings_.emplace(channel,
f);
368 dht::ThreadPool::io().run([w = std::weak_ptr<OutgoingFile>(
f)] {
369 if (
auto of = w.lock())
393TransferManager::info(
const std::string& fileId,
396 int64_t& progress)
const noexcept
398 std::unique_lock lk {pimpl_->mapMutex_};
399 if (pimpl_->to_.empty())
402 auto itI = pimpl_->incomings_.find(fileId);
403 auto itW = pimpl_->waitingIds_.find(fileId);
404 path = this->path(fileId).string();
405 if (itI != pimpl_->incomings_.end()) {
406 total = itI->second->info().totalSize;
407 progress = itI->second->info().bytesProgress;
409 }
else if (std::filesystem::is_regular_file(path)) {
410 std::ifstream transfer(path, std::ios::binary);
411 transfer.seekg(0, std::ios::end);
412 progress = transfer.tellg();
413 if (itW != pimpl_->waitingIds_.end()) {
414 total = itW->second.totalSize;
420 }
else if (itW != pimpl_->waitingIds_.end()) {
421 total = itW->second.totalSize;
431TransferManager::waitForTransfer(
const std::string& fileId,
432 const std::string& interactionId,
433 const std::string& sha3sum,
434 const std::string& path,
437 std::unique_lock lk(pimpl_->mapMutex_);
438 auto itW = pimpl_->waitingIds_.find(fileId);
439 if (itW != pimpl_->waitingIds_.end())
441 pimpl_->waitingIds_[fileId] = {fileId, interactionId, sha3sum, path, total};
442 pimpl_->saveWaiting();
446TransferManager::onIncomingFileTransfer(
const std::string& fileId,
447 const std::shared_ptr<dhtnet::ChannelSocket>& channel,
450 std::lock_guard lk(pimpl_->mapMutex_);
452 auto itC = pimpl_->incomings_.find(fileId);
453 if (itC != pimpl_->incomings_.end()) {
457 auto itW = pimpl_->waitingIds_.find(fileId);
458 if (itW == pimpl_->waitingIds_.end()) {
459 dht::ThreadPool().io().run([channel] {
466 info.accountId = pimpl_->accountId_;
467 info.conversationId = pimpl_->to_;
468 info.path = itW->second.path;
469 info.totalSize = itW->second.totalSize;
470 info.bytesProgress = start;
475 auto filePath = path(fileId);
476 if (info.path.empty()) {
477 info.path = filePath.string();
481 fileutils::createFileLink(filePath, info.path);
484 auto ifile = std::make_shared<IncomingFile>(std::move(channel),
487 itW->second.interactionId,
488 itW->second.sha3sum);
489 auto res = pimpl_->incomings_.emplace(fileId, std::move(ifile));
491 res.first->second->onFinished([w = weak(), fileId](uint32_t code) {
493 dht::ThreadPool().computation().run([w, fileId, code] {
494 if (
auto sthis_ = w.lock()) {
495 auto& pimpl = sthis_->pimpl_;
496 std::lock_guard lk {pimpl->mapMutex_};
497 auto itO = pimpl->incomings_.find(fileId);
498 if (itO != pimpl->incomings_.end())
499 pimpl->incomings_.erase(itO);
500 if (code == uint32_t(libjami::DataTransferEventCode::finished)) {
501 auto itW = pimpl->waitingIds_.find(fileId);
502 if (itW != pimpl->waitingIds_.end()) {
503 pimpl->waitingIds_.erase(itW);
504 pimpl->saveWaiting();
510 res.first->second->process();
521TransferManager::onIncomingProfile(
const std::shared_ptr<dhtnet::ChannelSocket>& channel,
522 const std::string& sha3Sum)
527 auto chName = channel->name();
528 std::string_view name = chName;
529 auto sep = name.find_last_of(
'?');
530 if (sep != std::string::npos)
531 name = name.substr(0, sep);
533 auto lastSep = name.find_last_of(
'/');
534 auto fileId = name.substr(lastSep + 1);
536 auto deviceId = channel->deviceId().toString();
537 auto cert = channel->peerCertificate();
538 if (!cert || !cert->issuer || fileId.find(
".vcf") == std::string::npos)
541 auto uri = fileId ==
"profile.vcf" ? cert->issuer->getId().toString()
542 : std::string(fileId.substr(0, fileId.size() - 4 ));
544 std::lock_guard lk(pimpl_->mapMutex_);
545 auto idx = std::make_pair(deviceId, uri);
547 auto itV = pimpl_->vcards_.find(idx);
548 if (itV != pimpl_->vcards_.end()) {
555 info.accountId = pimpl_->accountId_;
556 info.conversationId = pimpl_->to_;
558 auto recvDir = fileutils::get_cache_dir() / pimpl_->accountId_ /
"vcard";
559 dhtnet::fileutils::recursive_mkdir(recvDir);
560 info.path = (recvDir / fmt::format(
"{:s}_{:s}_{}", deviceId, uri, tid)).
string();
562 auto ifile = std::make_shared<IncomingFile>(std::move(channel), info,
"profile.vcf",
"", sha3Sum);
563 auto res = pimpl_->vcards_.emplace(idx, std::move(ifile));
565 res.first->second->onFinished([w = weak(),
566 uri = std::move(uri),
567 deviceId = std::move(deviceId),
568 accountId = pimpl_->accountId_,
569 cert = std::move(cert),
570 path = info.path](uint32_t code) {
571 dht::ThreadPool().computation().run([w,
572 uri = std::move(uri),
573 deviceId = std::move(deviceId),
574 accountId = std::move(accountId),
575 path = std::move(path),
577 if (auto sthis_ = w.lock()) {
578 auto& pimpl = sthis_->pimpl_;
580 auto destPath = sthis_->profilePath(uri);
583 std::lock_guard lock(dhtnet::fileutils::getFileLock(destPath));
584 dhtnet::fileutils::recursive_mkdir(destPath.parent_path());
585 std::filesystem::rename(path, destPath);
586 if (!pimpl->accountUri_.empty() && uri == pimpl->accountUri_) {
588 if (!fileutils::createFileLink(pimpl->accountProfilePath_, destPath)) {
590 std::filesystem::copy_file(destPath, pimpl->accountProfilePath_, ec);
593 } catch (const std::exception& e) {
594 JAMI_ERROR(
"{}", e.what());
597 std::lock_guard lk {pimpl->mapMutex_};
598 auto itO = pimpl->vcards_.find({deviceId, uri});
599 if (itO != pimpl->vcards_.end())
600 pimpl->vcards_.erase(itO);
601 if (code == uint32_t(libjami::DataTransferEventCode::finished)) {
602 emitSignal<libjami::ConfigurationSignal::ProfileReceived>(accountId,
609 res.first->second->process();
void transferFile(const std::shared_ptr< dhtnet::ChannelSocket > &channel, const std::string &fileId, const std::string &interactionId, const std::string &path, size_t start=0, size_t end=0, OnFinishedCb onFinished={})
Send a file to a channel.