/**************************************************************************** * Copyright (C) 2017-2024 Savoir-faire Linux Inc. * * Author: Nicolas Jäger * * Author: Sébastien Blin * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ #include "callbackshandler.h" // Models and database #include "api/account.h" #include "api/lrc.h" #include "api/accountmodel.h" #include "api/datatransfer.h" #include "api/datatransfermodel.h" #include "api/behaviorcontroller.h" // Lrc #include "dbus/callmanager.h" #include "dbus/configurationmanager.h" #include "dbus/presencemanager.h" #include "dbus/videomanager.h" #include "typedefs.h" // libjami #include #include #include #include #ifdef ENABLE_LIBWRAP // For the debugMessageReceived connection that queues const std::string refs // when not using dbus Q_DECLARE_METATYPE(std::string) #endif namespace lrc { using namespace api; static inline datatransfer::Status convertDataTransferEvent(libjami::DataTransferEventCode event) { switch (event) { case libjami::DataTransferEventCode::invalid: return datatransfer::Status::INVALID; case libjami::DataTransferEventCode::created: return datatransfer::Status::on_connection; case libjami::DataTransferEventCode::unsupported: return datatransfer::Status::unsupported; case libjami::DataTransferEventCode::wait_peer_acceptance: return datatransfer::Status::on_connection; case libjami::DataTransferEventCode::wait_host_acceptance: return datatransfer::Status::on_connection; case libjami::DataTransferEventCode::ongoing: return datatransfer::Status::on_progress; case libjami::DataTransferEventCode::finished: return datatransfer::Status::success; case libjami::DataTransferEventCode::closed_by_host: return datatransfer::Status::stop_by_host; case libjami::DataTransferEventCode::closed_by_peer: return datatransfer::Status::stop_by_peer; case libjami::DataTransferEventCode::invalid_pathname: return datatransfer::Status::invalid_pathname; case libjami::DataTransferEventCode::unjoinable_peer: return datatransfer::Status::unjoinable_peer; case libjami::DataTransferEventCode::timeout_expired: return datatransfer::Status::timeout_expired; } throw std::runtime_error("BUG: broken convertDataTransferEvent() switch"); } CallbacksHandler::CallbacksHandler(const Lrc& parent) : QObject() , parent(parent) { // Get signals from daemon connect(&ConfigurationManager::instance(), &ConfigurationManagerInterface::incomingAccountMessage, this, &CallbacksHandler::slotNewAccountMessage, Qt::QueuedConnection); connect(&PresenceManager::instance(), &PresenceManagerInterface::newBuddyNotification, this, &CallbacksHandler::slotNewBuddySubscription, Qt::QueuedConnection); connect(&PresenceManager::instance(), &PresenceManagerInterface::nearbyPeerNotification, this, &CallbacksHandler::slotNearbyPeerSubscription, Qt::QueuedConnection); connect(&ConfigurationManager::instance(), &ConfigurationManagerInterface::contactAdded, this, &CallbacksHandler::slotContactAdded, Qt::QueuedConnection); connect(&ConfigurationManager::instance(), &ConfigurationManagerInterface::contactRemoved, this, &CallbacksHandler::slotContactRemoved, Qt::QueuedConnection); connect(&ConfigurationManager::instance(), &ConfigurationManagerInterface::accountMessageStatusChanged, this, &CallbacksHandler::slotAccountMessageStatusChanged, Qt::QueuedConnection); connect(&ConfigurationManager::instance(), &ConfigurationManagerInterface::needsHost, this, &CallbacksHandler::slotNeedsHost, Qt::QueuedConnection); connect(&ConfigurationManager::instance(), &ConfigurationManagerInterface::accountDetailsChanged, this, &CallbacksHandler::slotAccountDetailsChanged, Qt::QueuedConnection); connect(&ConfigurationManager::instance(), &ConfigurationManagerInterface::volatileAccountDetailsChanged, this, &CallbacksHandler::slotVolatileAccountDetailsChanged, Qt::QueuedConnection); connect(&ConfigurationManager::instance(), &ConfigurationManagerInterface::accountsChanged, this, &CallbacksHandler::slotAccountsChanged); connect(&ConfigurationManager::instance(), &ConfigurationManagerInterface::registrationStateChanged, this, &CallbacksHandler::slotRegistrationStateChanged, Qt::QueuedConnection); connect(&CallManager::instance(), &CallManagerInterface::mediaChangeRequested, this, &CallbacksHandler::slotMediaChangeRequested, Qt::QueuedConnection); connect(&CallManager::instance(), &CallManagerInterface::callStateChanged, this, &CallbacksHandler::slotCallStateChanged, Qt::QueuedConnection); connect(&CallManager::instance(), &CallManagerInterface::mediaNegotiationStatus, this, &CallbacksHandler::slotMediaNegotiationStatus, Qt::QueuedConnection); connect(&CallManager::instance(), &CallManagerInterface::conferenceCreated, this, &CallbacksHandler::slotConferenceCreated, Qt::QueuedConnection); connect(&CallManager::instance(), &CallManagerInterface::conferenceRemoved, this, &CallbacksHandler::slotConferenceRemoved, Qt::QueuedConnection); connect(&CallManager::instance(), &CallManagerInterface::conferenceChanged, this, &CallbacksHandler::slotConferenceChanged, Qt::QueuedConnection); connect(&CallManager::instance(), &CallManagerInterface::recordingStateChanged, this, &CallbacksHandler::recordingStateChanged, Qt::QueuedConnection); connect(&CallManager::instance(), &CallManagerInterface::incomingMessage, this, &CallbacksHandler::slotIncomingMessage, Qt::QueuedConnection); connect(&CallManager::instance(), &CallManagerInterface::recordPlaybackStopped, this, &CallbacksHandler::slotRecordPlaybackStopped, Qt::QueuedConnection); connect(&CallManager::instance(), &CallManagerInterface::voiceMailNotify, this, &CallbacksHandler::slotVoiceMailNotify, Qt::QueuedConnection); connect(&CallManager::instance(), &CallManagerInterface::remoteRecordingChanged, this, &CallbacksHandler::slotRemoteRecordingChanged, Qt::QueuedConnection); connect(&ConfigurationManager::instance(), &ConfigurationManagerInterface::dataTransferEvent, this, &CallbacksHandler::slotDataTransferEvent, Qt::QueuedConnection); connect(&ConfigurationManager::instance(), &ConfigurationManagerInterface::knownDevicesChanged, this, &CallbacksHandler::slotKnownDevicesChanged, Qt::QueuedConnection); connect(&ConfigurationManager::instance(), &ConfigurationManagerInterface::deviceRevocationEnded, this, &CallbacksHandler::slotDeviceRevokationEnded, Qt::QueuedConnection); connect(&ConfigurationManager::instance(), &ConfigurationManagerInterface::accountProfileReceived, this, &CallbacksHandler::slotAccountProfileReceived, Qt::QueuedConnection); connect(&ConfigurationManager::instance(), &ConfigurationManagerInterface::exportOnRingEnded, this, &CallbacksHandler::slotExportOnRingEnded, Qt::QueuedConnection); connect(&ConfigurationManager::instance(), &ConfigurationManagerInterface::nameRegistrationEnded, this, &CallbacksHandler::slotNameRegistrationEnded, Qt::QueuedConnection); connect(&ConfigurationManager::instance(), &ConfigurationManagerInterface::registeredNameFound, this, &CallbacksHandler::slotRegisteredNameFound, Qt::QueuedConnection); connect(&ConfigurationManager::instance(), &ConfigurationManagerInterface::migrationEnded, this, &CallbacksHandler::slotMigrationEnded, Qt::QueuedConnection); connect(&VideoManager::instance(), &VideoManagerInterface::decodingStarted, this, &CallbacksHandler::decodingStarted, Qt::DirectConnection); connect(&VideoManager::instance(), &VideoManagerInterface::decodingStopped, this, &CallbacksHandler::decodingStopped, Qt::DirectConnection); connect(&VideoManager::instance(), &VideoManagerInterface::deviceEvent, this, &CallbacksHandler::deviceEvent, Qt::QueuedConnection); connect(&VideoManager::instance(), &VideoManagerInterface::fileOpened, this, &CallbacksHandler::fileOpened, Qt::DirectConnection); connect(&ConfigurationManager::instance(), &ConfigurationManagerInterface::audioDeviceEvent, this, &CallbacksHandler::slotAudioDeviceEvent, Qt::QueuedConnection); connect(&ConfigurationManager::instance(), &ConfigurationManagerInterface::audioMeter, this, &CallbacksHandler::slotAudioMeterReceived, Qt::QueuedConnection); connect(&ConfigurationManager::instance(), &ConfigurationManagerInterface::swarmLoaded, this, &CallbacksHandler::slotSwarmLoaded, Qt::QueuedConnection); connect(&ConfigurationManager::instance(), &ConfigurationManagerInterface::messagesFound, this, &CallbacksHandler::slotMessagesFound, Qt::QueuedConnection); connect(&ConfigurationManager::instance(), &ConfigurationManagerInterface::swarmMessageReceived, this, &CallbacksHandler::slotMessageReceived, Qt::QueuedConnection); connect(&ConfigurationManager::instance(), &ConfigurationManagerInterface::swarmMessageUpdated, this, &CallbacksHandler::slotMessageUpdated, Qt::QueuedConnection); connect(&ConfigurationManager::instance(), &ConfigurationManagerInterface::reactionAdded, this, &CallbacksHandler::slotReactionAdded, Qt::QueuedConnection); connect(&ConfigurationManager::instance(), &ConfigurationManagerInterface::reactionRemoved, this, &CallbacksHandler::slotReactionRemoved, Qt::QueuedConnection); connect(&ConfigurationManager::instance(), &ConfigurationManagerInterface::conversationProfileUpdated, this, &CallbacksHandler::slotConversationProfileUpdated, Qt::QueuedConnection); connect(&ConfigurationManager::instance(), &ConfigurationManagerInterface::conversationRequestReceived, this, &CallbacksHandler::slotConversationRequestReceived, Qt::QueuedConnection); connect(&ConfigurationManager::instance(), &ConfigurationManagerInterface::conversationRequestDeclined, this, &CallbacksHandler::slotConversationRequestDeclined, Qt::QueuedConnection); connect(&ConfigurationManager::instance(), &ConfigurationManagerInterface::conversationReady, this, &CallbacksHandler::slotConversationReady, Qt::QueuedConnection); connect(&ConfigurationManager::instance(), &ConfigurationManagerInterface::conversationRemoved, this, &CallbacksHandler::slotConversationRemoved, Qt::QueuedConnection); connect(&ConfigurationManager::instance(), &ConfigurationManagerInterface::conversationMemberEvent, this, &CallbacksHandler::slotConversationMemberEvent, Qt::QueuedConnection); connect(&ConfigurationManager::instance(), &ConfigurationManagerInterface::onConversationError, this, &CallbacksHandler::slotOnConversationError, Qt::QueuedConnection); connect(&ConfigurationManager::instance(), &ConfigurationManagerInterface::activeCallsChanged, this, &CallbacksHandler::slotActiveCallsChanged, Qt::QueuedConnection); connect(&ConfigurationManager::instance(), &ConfigurationManagerInterface::conversationPreferencesUpdated, this, &CallbacksHandler::slotConversationPreferencesUpdated, Qt::QueuedConnection); } CallbacksHandler::~CallbacksHandler() {} void CallbacksHandler::subscribeToDebugReceived() { connect(&ConfigurationManager::instance(), &ConfigurationManagerInterface::messageSend, this, &CallbacksHandler::slotDebugMessageReceived, Qt::QueuedConnection); } void CallbacksHandler::slotNewAccountMessage(const QString& accountId, const QString& peerId, const QString& msgId, const MapStringString& payloads) { auto peerId2 = QString(peerId).replace("@ring.dht", ""); for (const auto& payload : payloads.keys()) { if (payload.contains(APPLICATION_GEO)) { Q_EMIT newPosition(accountId, peerId, payloads.value(payload), 0, msgId); return; } } Q_EMIT newAccountMessage(accountId, peerId2, msgId, payloads); } void CallbacksHandler::slotNewBuddySubscription(const QString& accountId, const QString& uri, int status, const QString& message) { Q_UNUSED(message) Q_EMIT newBuddySubscription(accountId, uri, status); } void CallbacksHandler::slotNearbyPeerSubscription(const QString& accountId, const QString& contactUri, int state, const QString& displayname) { Q_EMIT newPeerSubscription(accountId, contactUri, state, displayname); } void CallbacksHandler::slotVoiceMailNotify(const QString& accountId, int newCount, int oldCount, int urgentCount) { Q_EMIT voiceMailNotify(accountId, newCount, oldCount, urgentCount); } void CallbacksHandler::slotRecordPlaybackStopped(const QString& filePath) { Q_EMIT recordPlaybackStopped(filePath); } void CallbacksHandler::slotContactAdded(const QString& accountId, const QString& contactUri, bool confirmed) { Q_EMIT contactAdded(accountId, contactUri, confirmed); } void CallbacksHandler::slotContactRemoved(const QString& accountId, const QString& contactUri, bool banned) { Q_EMIT contactRemoved(accountId, contactUri, banned); } void CallbacksHandler::slotMediaChangeRequested(const QString& accountId, const QString& callId, const VectorMapStringString& mediaList) { Q_EMIT mediaChangeRequested(accountId, callId, mediaList); } void CallbacksHandler::slotCallStateChanged(const QString& accountId, const QString& callId, const QString& state, int code) { Q_EMIT callStateChanged(accountId, callId, state, code); } void CallbacksHandler::slotMediaNegotiationStatus(const QString& callId, const QString& event, const VectorMapStringString& mediaList) { Q_EMIT mediaNegotiationStatus(callId, event, mediaList); } void CallbacksHandler::slotAccountDetailsChanged(const QString& accountId, const MapStringString& details) { Q_EMIT accountDetailsChanged(accountId, details); } void CallbacksHandler::slotVolatileAccountDetailsChanged(const QString& accountId, const MapStringString& details) { Q_EMIT volatileAccountDetailsChanged(accountId, details); } void CallbacksHandler::slotAccountsChanged() { Q_EMIT accountsChanged(); } void CallbacksHandler::slotRegistrationStateChanged(const QString& accountId, const QString& registration_state, unsigned detail_code, const QString& detail_str) { (void) detail_code; (void) detail_str; Q_EMIT accountStatusChanged(accountId, lrc::api::account::to_status(registration_state)); } void CallbacksHandler::slotIncomingMessage(const QString& accountId, const QString& callId, const QString& from, const MapStringString& interaction) { QString from2; if (from.contains("@ring.dht")) { from2 = QString(from).replace("@ring.dht", ""); } else { auto left = from.indexOf(":") + 1; auto right = from.indexOf("@"); from2 = from.mid(left, right - left); } for (auto& e : interaction.toStdMap()) { if (e.first.contains("x-ring/ring.profile.vcard")) { auto decodedHead = QUrl::fromPercentEncoding(e.first.toLatin1()); QRegularExpression re( "x-ring/ring.profile.vcard;id=([A-z0-9]+),part=([0-9]+),of=([0-9]+)"); auto match = re.match(decodedHead); if (!match.hasMatch()) continue; Q_EMIT incomingVCardChunk(accountId, callId, from2, match.captured(2).toInt(), match.captured(3).toInt(), e.second); } else if (e.first.contains(TEXT_PLAIN)) { // we consider it as an usual message interaction Q_EMIT incomingCallMessage(accountId, callId, from2, e.second); } } } void CallbacksHandler::slotConferenceCreated(const QString& accountId, const QString& convId, const QString& callId) { Q_EMIT conferenceCreated(accountId, convId, callId); } void CallbacksHandler::slotConferenceRemoved(const QString& accountId, const QString& callId) { Q_EMIT conferenceRemoved(accountId, callId); } void CallbacksHandler::slotConferenceChanged(const QString& accountId, const QString& callId, const QString& state) { Q_EMIT conferenceChanged(accountId, callId, state); slotCallStateChanged(accountId, callId, state, 0); } void CallbacksHandler::slotAccountMessageStatusChanged(const QString& accountId, const QString& conversationId, const QString& peer, const QString& messageId, int status) { Q_EMIT accountMessageStatusChanged(accountId, conversationId, peer, messageId, status); } void CallbacksHandler::slotNeedsHost(const QString& accountId, const QString& conversationId) { Q_EMIT needsHost(accountId, conversationId); } void CallbacksHandler::slotDataTransferEvent(const QString& accountId, const QString& conversationId, const QString&, const QString& fileId, uint codeStatus) { auto event = libjami::DataTransferEventCode(codeStatus); api::datatransfer::Info info; if (conversationId.isEmpty()) { return; } else { info.uid = fileId; info.status = convertDataTransferEvent(event); info.conversationId = conversationId; info.accountId = accountId; qlonglong totalSize, progress; QString path; try { parent.getAccountModel().getAccountInfo(accountId).dataTransferModel->fileTransferInfo( accountId, conversationId, fileId, path, totalSize, progress); } catch (...) { return; } auto fi = QFileInfo(path); if (fi.isSymLink()) { path = fi.symLinkTarget(); } info.path = path; info.totalSize = totalSize; info.progress = progress; } // WARNING: info.status could be INVALID in case of async signaling // So listeners must only take account of fileId in such case. // Is useful for "termination" status like unjoinable_peer. switch (event) { case libjami::DataTransferEventCode::created: Q_EMIT transferStatusCreated(fileId, info); break; case libjami::DataTransferEventCode::closed_by_host: case libjami::DataTransferEventCode::closed_by_peer: Q_EMIT transferStatusCanceled(fileId, info); break; case libjami::DataTransferEventCode::wait_peer_acceptance: Q_EMIT transferStatusAwaitingPeer(fileId, info); break; case libjami::DataTransferEventCode::wait_host_acceptance: Q_EMIT transferStatusAwaitingHost(fileId, info); break; case libjami::DataTransferEventCode::ongoing: Q_EMIT transferStatusOngoing(fileId, info); break; case libjami::DataTransferEventCode::finished: Q_EMIT transferStatusFinished(fileId, info); break; case libjami::DataTransferEventCode::invalid_pathname: case libjami::DataTransferEventCode::unsupported: Q_EMIT transferStatusError(fileId, info); break; case libjami::DataTransferEventCode::timeout_expired: Q_EMIT transferStatusTimeoutExpired(fileId, info); break; case libjami::DataTransferEventCode::unjoinable_peer: Q_EMIT transferStatusUnjoinable(fileId, info); break; case libjami::DataTransferEventCode::invalid: break; } } void CallbacksHandler::slotKnownDevicesChanged(const QString& accountId, const MapStringString& devices) { Q_EMIT knownDevicesChanged(accountId, devices); } void CallbacksHandler::slotAccountProfileReceived(const QString& accountId, const QString& displayName, const QString& userPhoto) { Q_EMIT accountProfileReceived(accountId, displayName, userPhoto); } void CallbacksHandler::slotDeviceRevokationEnded(const QString& accountId, const QString& deviceId, const int status) { Q_EMIT deviceRevocationEnded(accountId, deviceId, status); } void CallbacksHandler::slotExportOnRingEnded(const QString& accountId, int status, const QString& pin) { Q_EMIT exportOnRingEnded(accountId, status, pin); } void CallbacksHandler::slotNameRegistrationEnded(const QString& accountId, int status, const QString& name) { Q_EMIT nameRegistrationEnded(accountId, status, name); } void CallbacksHandler::slotRegisteredNameFound(const QString& accountId, int status, const QString& address, const QString& name) { Q_EMIT registeredNameFound(accountId, status, address, name); } void CallbacksHandler::slotMigrationEnded(const QString& accountId, const QString& status) { Q_EMIT migrationEnded(accountId, status == "SUCCESS"); } void CallbacksHandler::slotDebugMessageReceived(const QString& message) { Q_EMIT parent.getBehaviorController().debugMessageReceived(message); } void CallbacksHandler::slotAudioDeviceEvent() { Q_EMIT audioDeviceEvent(); } void CallbacksHandler::slotAudioMeterReceived(const QString& id, float level) { Q_EMIT audioMeter(id, level); } void CallbacksHandler::slotRemoteRecordingChanged(const QString& callId, const QString& peerNumber, bool state) { Q_EMIT remoteRecordingChanged(callId, peerNumber, state); } void CallbacksHandler::slotSwarmLoaded(uint32_t requestId, const QString& accountId, const QString& conversationId, const VectorSwarmMessage& messages) { Q_EMIT swarmLoaded(requestId, accountId, conversationId, messages); } void CallbacksHandler::slotMessagesFound(uint32_t requestId, const QString& accountId, const QString& conversationId, const VectorMapStringString& messages) { Q_EMIT messagesFound(requestId, accountId, conversationId, messages); } void CallbacksHandler::slotMessageReceived(const QString& accountId, const QString& conversationId, const SwarmMessage& message) { Q_EMIT messageReceived(accountId, conversationId, message); } void CallbacksHandler::slotMessageUpdated(const QString& accountId, const QString& conversationId, const SwarmMessage& message) { Q_EMIT messageUpdated(accountId, conversationId, message); } void CallbacksHandler::slotReactionAdded(const QString& accountId, const QString& conversationId, const QString& messageId, const MapStringString& reaction) { Q_EMIT reactionAdded(accountId, conversationId, messageId, reaction); } void CallbacksHandler::slotReactionRemoved(const QString& accountId, const QString& conversationId, const QString& messageId, const QString& reactionId) { Q_EMIT reactionRemoved(accountId, conversationId, messageId, reactionId); } void CallbacksHandler::slotConversationProfileUpdated(const QString& accountId, const QString& conversationId, const MapStringString& profile) { Q_EMIT conversationProfileUpdated(accountId, conversationId, profile); } void CallbacksHandler::slotConversationRequestReceived(const QString& accountId, const QString& conversationId, const MapStringString& metadatas) { Q_EMIT conversationRequestReceived(accountId, conversationId, metadatas); } void CallbacksHandler::slotConversationRequestDeclined(const QString& accountId, const QString& conversationId) { Q_EMIT conversationRequestDeclined(accountId, conversationId); } void CallbacksHandler::slotConversationReady(const QString& accountId, const QString& conversationId) { Q_EMIT conversationReady(accountId, conversationId); } void CallbacksHandler::slotConversationRemoved(const QString& accountId, const QString& conversationId) { Q_EMIT conversationRemoved(accountId, conversationId); } void CallbacksHandler::slotConversationMemberEvent(const QString& accountId, const QString& conversationId, const QString& memberId, int event) { Q_EMIT conversationMemberEvent(accountId, conversationId, memberId, event); } void CallbacksHandler::slotOnConversationError(const QString& accountId, const QString& conversationId, int code, const QString& what) { Q_EMIT conversationError(accountId, conversationId, code, what); } void CallbacksHandler::slotActiveCallsChanged(const QString& accountId, const QString& conversationId, const VectorMapStringString& activeCalls) { Q_EMIT activeCallsChanged(accountId, conversationId, activeCalls); } void CallbacksHandler::slotConversationPreferencesUpdated(const QString& accountId, const QString& conversationId, const MapStringString& preferences) { Q_EMIT conversationPreferencesUpdated(accountId, conversationId, preferences); } } // namespace lrc