/************************************************************************** | |
* Copyright (C) 2016 by Savoir-faire Linux * | |
* Author: Jäger Nicolas <nicolas.jager@savoirfairelinux.com> * | |
* Author: Traczyk Andreas <traczyk.andreas@savoirfairelinux.com> * | |
* * | |
* This program is free software; you can redistribute it and/or modify * | |
* it under the terms of the GNU General Public License as published by * | |
* the Free Software Foundation; either version 3 of the License, or * | |
* (at your option) any later version. * | |
* * | |
* This program 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 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 <http://www.gnu.org/licenses/>. * | |
**************************************************************************/ | |
#include "pch.h" | |
/* daemon */ | |
#include <dring.h> | |
#include "dring/call_const.h" | |
#include "callmanager_interface.h" | |
#include "configurationmanager_interface.h" | |
#include "presencemanager_interface.h" | |
#include "videomanager_interface.h" | |
#include "fileutils.h" | |
#include "account_const.h" | |
#include "string_utils.h" | |
#include "gnutls\gnutls.h" | |
#include "media_const.h" | |
using namespace Windows::ApplicationModel::Core; | |
using namespace Windows::Storage; | |
using namespace Windows::UI::Core; | |
using namespace Windows::Media; | |
using namespace Windows::Media::MediaProperties; | |
using namespace Windows::Media::Capture; | |
using namespace Windows::System::Threading; | |
using namespace Windows::Globalization::DateTimeFormatting; | |
using namespace Windows::UI::ViewManagement; | |
using namespace Windows::System; | |
using namespace RingClientUWP; | |
using namespace RingClientUWP::Utils; | |
using namespace RingClientUWP::ViewModel; | |
void | |
RingD::InternetConnectionChanged(Platform::Object^ sender) | |
{ | |
hasInternet_ = Utils::hasInternet(); | |
networkChanged(); | |
connectivityChanged(); | |
} | |
RingD::AccountDetailsBlob | |
RingD::getAllAccountDetails() | |
{ | |
RingD::AccountDetailsBlob allAccountDetails; | |
std::vector<std::string> accountList = DRing::getAccountList(); | |
std::for_each(std::cbegin(accountList), std::cend(accountList), [&](const std::string& acc) { | |
allAccountDetails[acc] = DRing::getAccountDetails(acc); | |
}); | |
return allAccountDetails; | |
} | |
void | |
RingD::subscribeBuddies() | |
{ | |
for (auto account : AccountsViewModel::instance->accountsList) { | |
if (Utils::hasInternet()) { | |
auto contactListModel = AccountsViewModel::instance->getContactListModel(Utils::toString(account->accountID_)); | |
for (auto contact : contactListModel->_contactsList) { | |
if (!contact->subscribed_) { | |
MSG_("account: " + account->accountID_ + " subscribing to buddy presence for ringID: " + contact->ringID_); | |
subscribeBuddy(Utils::toString(account->accountID_), Utils::toString(contact->ringID_), true); | |
contact->subscribed_ = true; | |
} | |
} | |
} | |
} | |
} | |
void | |
RingD::parseAccountDetails(const AccountDetailsBlob& allAccountDetails) | |
{ | |
std::for_each(std::cbegin(allAccountDetails), std::cend(allAccountDetails), | |
[=](std::pair<std::string, AccountDetails> acc) { | |
auto accountId = acc.first; | |
auto accountDetails = acc.second; | |
auto type = accountDetails.find(DRing::Account::ConfProperties::TYPE)->second; | |
if (type == "RING") { | |
auto ringID = accountDetails.find(DRing::Account::ConfProperties::USERNAME)->second; | |
if (!ringID.empty()) | |
ringID = ringID.substr(5); | |
bool active = (accountDetails.find(DRing::Account::ConfProperties::ENABLED)->second == ring::TRUE_STR) | |
? true | |
: false; | |
bool upnpState = (accountDetails.find(DRing::Account::ConfProperties::UPNP_ENABLED)->second == ring::TRUE_STR) | |
? true | |
: false; | |
bool autoAnswer = (accountDetails.find(DRing::Account::ConfProperties::AUTOANSWER)->second == ring::TRUE_STR) | |
? true | |
: false; | |
bool dhtPublicInCalls = (accountDetails.find(DRing::Account::ConfProperties::DHT::PUBLIC_IN_CALLS)->second == ring::TRUE_STR) | |
? true | |
: false; | |
bool turnEnabled = (accountDetails.find(DRing::Account::ConfProperties::TURN::ENABLED)->second == ring::TRUE_STR) | |
? true | |
: false; | |
auto turnAddress = accountDetails.find(DRing::Account::ConfProperties::TURN::SERVER)->second; | |
auto alias = accountDetails.find(DRing::Account::ConfProperties::ALIAS)->second; | |
auto deviceId = accountDetails.find(DRing::Account::ConfProperties::RING_DEVICE_ID)->second; | |
auto deviceName = accountDetails.find(DRing::Account::ConfProperties::RING_DEVICE_NAME)->second; | |
auto hasArchivePassword = (accountDetails.find(DRing::Account::ConfProperties::ARCHIVE_HAS_PASSWORD)->second == ring::TRUE_STR) | |
? true | |
: false; | |
auto account = AccountsViewModel::instance->findItem(Utils::toPlatformString(accountId)); | |
if (account) { | |
account->name_ = Utils::toPlatformString(alias); | |
account->_active = active; | |
account->_upnpState = upnpState; | |
account->accountType_ = Utils::toPlatformString(type); | |
account->ringID_ = Utils::toPlatformString(ringID); | |
account->_autoAnswer = autoAnswer; | |
account->_turnEnabled = turnEnabled; | |
account->_turnAddress = Utils::toPlatformString(turnAddress); | |
account->_deviceId = Utils::toPlatformString(deviceId); | |
account->_deviceName = Utils::toPlatformString(deviceName); | |
// load contact requests for the account | |
auto contactRequests = DRing::getTrustRequests(accountId); | |
if (auto contactListModel = AccountsViewModel::instance->getContactListModel(std::string(accountId))) { | |
for (auto& cr : contactRequests) { | |
auto ringId = cr.at("from"); | |
auto timeReceived = cr.at("received"); | |
auto payload = cr.at("payload"); | |
auto fromP = Utils::toPlatformString(ringId); | |
auto contact = contactListModel->findContactByRingId(fromP); | |
if (contact == nullptr) { | |
contact = contactListModel->addNewContact(fromP, fromP, TrustStatus::INCOMING_CONTACT_REQUEST, false); | |
contactListModel->saveContactsToFile(); | |
AccountsViewModel::instance->raiseUnreadContactRequest(); | |
SmartPanelItemsViewModel::instance->refreshFilteredItemsList(); | |
SmartPanelItemsViewModel::instance->update(ViewModel::NotifyStrings::notifySmartPanelItem); | |
} | |
else if (ContactRequestItemsViewModel::instance->findItem(contact) == nullptr) { | |
// The name is the ring id for now | |
contact->_name = Utils::toPlatformString(ringId); | |
// The visible ring id will potentially be replaced by a username after a lookup | |
RingD::instance->lookUpAddress(accountId, Utils::toPlatformString(ringId)); | |
auto vcard = contact->getVCard(); | |
auto parsedPayload = VCardUtils::parseContactRequestPayload(payload); | |
std::string vcpart; | |
try { | |
vcpart.assign(parsedPayload.at("VCARD")); | |
} | |
catch (const std::exception& e) { | |
MSG_(e.what()); | |
} | |
if (!vcpart.empty()) { | |
vcard->setData(vcpart); | |
vcard->completeReception(); | |
contact->_displayName = Utils::toPlatformString(vcard->getPart("FN")); | |
} | |
auto newContactRequest = ref new ContactRequestItem(); | |
newContactRequest->_contact = contact; | |
ContactRequestItemsViewModel::instance->itemsList->InsertAt(0, newContactRequest); | |
ContactRequestItemsViewModel::instance->refreshFilteredItemsList(); | |
ContactRequestItemsViewModel::instance->update(ViewModel::NotifyStrings::notifyContactRequestItem); | |
} | |
} | |
} | |
accountUpdated(account); | |
} | |
else { | |
if (!ringID.empty()) { | |
AccountsViewModel::instance->addRingAccount(alias, | |
ringID, | |
accountId, | |
deviceId, | |
deviceName, | |
active, | |
upnpState, | |
autoAnswer, | |
dhtPublicInCalls, | |
turnEnabled, | |
turnAddress); | |
archive_has_password->Insert(Utils::toPlatformString(accountId), hasArchivePassword); | |
} | |
} | |
} | |
else { /* SIP */ | |
auto alias = accountDetails.find(DRing::Account::ConfProperties::ALIAS)->second; | |
bool active = (accountDetails.find(DRing::Account::ConfProperties::ENABLED)->second == ring::TRUE_STR) | |
? true | |
: false; | |
auto sipHostname = accountDetails.find(DRing::Account::ConfProperties::HOSTNAME)->second; | |
auto sipUsername = accountDetails.find(DRing::Account::ConfProperties::USERNAME)->second; | |
auto sipPassword = accountDetails.find(DRing::Account::ConfProperties::PASSWORD)->second; | |
auto account = AccountsViewModel::instance->findItem(Utils::toPlatformString(accountId)); | |
if (account) { | |
account->name_ = Utils::toPlatformString(alias); | |
account->accountType_ = Utils::toPlatformString(type); | |
account->_sipHostname = Utils::toPlatformString(sipHostname); | |
account->_sipUsername = Utils::toPlatformString(sipUsername); | |
account->_sipPassword = Utils::toPlatformString(sipPassword); | |
accountUpdated(account); | |
} | |
else { | |
AccountsViewModel::instance->addSipAccount(alias, | |
accountId, | |
active, | |
sipHostname, | |
sipUsername, | |
sipPassword); | |
} | |
} | |
}); | |
} | |
void | |
RingD::connectivityChanged() | |
{ | |
if (daemonRunning_) { | |
DRing::connectivityChanged(); | |
} | |
} | |
void | |
RingD::sendAccountTextMessage(String^ message) | |
{ | |
tasks_.add_task([this, message]() { | |
if (auto item = SmartPanelItemsViewModel::instance->_selectedItem) { | |
auto accountId = AccountListItemsViewModel::instance->_selectedItem->_account->accountID_; | |
auto contact = item->_contact; | |
auto uri = contact->ringID_; | |
auto _accountId = Utils::toString(accountId); | |
auto _uri = Utils::toString(uri); | |
/* payload(s) */ | |
IBuffer^ buffUTF8 = CryptographicBuffer::ConvertStringToBinary(message, BinaryStringEncoding::Utf8); | |
auto _message = Utils::getData(buffUTF8); | |
std::map<std::string, std::string> _payload; | |
_payload["text/plain"] = _message; | |
auto sentToken = DRing::sendAccountTextMessage(_accountId, _uri, _payload); | |
Utils::runOnUIThread([this, sentToken, contact, message]() { | |
if (sentToken) { | |
contact->_conversation->addMessage(MSG_FROM_ME, message, std::time(nullptr), false, sentToken.ToString()); | |
contact->saveConversationToFile(); | |
} | |
}); | |
} | |
}); | |
} | |
void | |
RingD::sendSIPTextMessage(String^ message) | |
{ | |
tasks_.add_task([this, message]() { | |
if (auto item = SmartPanelItemsViewModel::instance->_selectedItem) { | |
auto accountId = AccountListItemsViewModel::instance->_selectedItem->_account->accountID_; | |
auto callId = item->_callId; | |
auto _accountId = Utils::toString(accountId); | |
auto _callId = Utils::toString(callId); | |
auto _message = Utils::toString(message); | |
std::map<std::string, std::string> _payload; | |
_payload["text/plain"] = _message; | |
DRing::sendTextMessage(_callId, _payload, _accountId, true /*not used*/); | |
Utils::runOnUIThread([this, item, message]() { | |
// No id generated for sip messages within a conversation, so let's generate one | |
// so we can track message order. | |
auto contact = item->_contact; | |
auto messageId = Utils::toPlatformString(Utils::genID(0LL, 9999999999999999999LL)); | |
contact->_conversation->addMessage(MSG_FROM_ME, message, std::time(nullptr), false, messageId); | |
contact->saveConversationToFile(); | |
}); | |
} | |
}); | |
} | |
void | |
RingD::sendSIPTextMessageVCF(std::string callID, std::map<std::string, std::string> message) | |
{ | |
tasks_.add_task([this, callID, message]() { | |
auto accountId = AccountListItemsViewModel::instance->_selectedItem->_account->accountID_; | |
auto _accountId = Utils::toString(accountId); | |
auto _callId = callID; | |
DRing::sendTextMessage(_callId, message, _accountId, true /*not used*/); | |
}); | |
} | |
void | |
RingD::createRINGAccount(String^ alias, String^ archivePassword, bool upnp, String^ registeredName) | |
{ | |
tasks_.add_task([this, alias, archivePassword, registeredName]() { | |
auto _alias = Utils::toString(alias); | |
auto _password = Utils::toString(archivePassword); | |
auto _registeredName = Utils::toString(registeredName); | |
std::map<std::string, std::string> accountDetails; | |
accountDetails.insert(std::make_pair(DRing::Account::ConfProperties::ALIAS, _alias)); | |
accountDetails.insert(std::make_pair(DRing::Account::ConfProperties::ARCHIVE_PASSWORD, _password)); | |
accountDetails.insert(std::make_pair(DRing::Account::ConfProperties::TYPE, "RING")); | |
accountDetails.insert(std::make_pair(DRing::Account::ConfProperties::UPNP_ENABLED, ring::TRUE_STR)); | |
Utils::runOnUIThread([this, registeredName]() { | |
showLoadingOverlay(ResourceMananger::instance->getStringResource("_m_creating_account_"), SuccessColor); | |
isAddingAccount = true; | |
if (registeredName != "") { | |
shouldRegister = true; | |
nameToRegister = registeredName; | |
} | |
}); | |
DRing::addAccount(accountDetails); | |
}); | |
} | |
void | |
RingD::createSIPAccount(String^ alias, String^ sipPassword, String^ sipHostname, String^ sipusername) | |
{ | |
tasks_.add_task([this, alias, sipPassword, sipHostname, sipusername]() { | |
auto _alias = Utils::toString(alias); | |
auto _sipPassword = Utils::toString(sipPassword); | |
auto _sipHostname = Utils::toString(sipHostname); | |
auto _sipUsername = Utils::toString(sipusername); | |
std::map<std::string, std::string> accountDetails; | |
accountDetails.insert(std::make_pair(DRing::Account::ConfProperties::ALIAS, _alias)); | |
accountDetails.insert(std::make_pair(DRing::Account::ConfProperties::TYPE, "SIP")); | |
accountDetails.insert(std::make_pair(DRing::Account::ConfProperties::PASSWORD, _sipPassword)); | |
accountDetails.insert(std::make_pair(DRing::Account::ConfProperties::HOSTNAME, _sipHostname)); | |
accountDetails.insert(std::make_pair(DRing::Account::ConfProperties::USERNAME, _sipUsername)); | |
Utils::runOnUIThread([this]() { | |
showLoadingOverlay(ResourceMananger::instance->getStringResource("_m_creating_account_"), SuccessColor); | |
isAddingAccount = true; | |
}); | |
DRing::addAccount(accountDetails); | |
}); | |
} | |
void | |
RingD::refuseIncommingCall(String^ callId) | |
{ | |
tasks_.add_task([this, callId]() { | |
auto _callId = Utils::toString(callId); | |
DRing::refuse(_callId); | |
}); | |
} | |
void | |
RingD::acceptIncommingCall(String^ callId) | |
{ | |
tasks_.add_task([this, callId]() { | |
auto _callId = Utils::toString(callId); | |
DRing::accept(_callId); | |
}); | |
} | |
void | |
RingD::placeCall(Contact^ contact) | |
{ | |
tasks_.add_task([this, contact]() { | |
auto _accountId = Utils::toString(contact->_accountIdAssociated); | |
auto _ringId = Utils::toString(contact->ringID_); | |
auto _sipUsername = Utils::toString(contact->_name); | |
auto selectedAccount = AccountListItemsViewModel::instance->_selectedItem->_account; | |
std::string callId; | |
if (selectedAccount->accountType_ == "RING") | |
callId = DRing::placeCall(_accountId, "ring:" + _ringId); | |
else if (selectedAccount->accountType_ == "SIP") | |
callId = DRing::placeCall(_accountId, "sip:" + _sipUsername); | |
// TODO: this UI response should be called at state change? | |
Utils::runOnUIThread([this, callId, _accountId, _ringId, _sipUsername, selectedAccount]() { | |
if (auto contactListModel = AccountsViewModel::instance->getContactListModel(std::string(_accountId))) { | |
Contact^ contact; | |
if (selectedAccount->accountType_ == "RING") | |
contact = contactListModel->findContactByRingId(Utils::toPlatformString(_ringId)); | |
else if (selectedAccount->accountType_ == "SIP") | |
contact = contactListModel->findContactByName(Utils::toPlatformString(_sipUsername)); | |
if (auto item = SmartPanelItemsViewModel::instance->findItem(contact)) { | |
item->_callId = Utils::toPlatformString(callId); | |
if (!callId.empty()) | |
callPlaced(Utils::toPlatformString(callId)); | |
} | |
} | |
}); | |
}); | |
} | |
void | |
RingD::pauseCall(const std::string & callId) | |
{ | |
tasks_.add_task([this, callId]() { | |
DRing::hold(callId); | |
}); | |
} | |
void | |
RingD::pauseCall(String ^ callId) | |
{ | |
tasks_.add_task([this, callId]() { | |
auto _callId = Utils::toString(callId); | |
DRing::hold(_callId); | |
}); | |
} | |
void | |
RingD::unPauseCall(const std::string & callId) | |
{ | |
tasks_.add_task([this, callId]() { | |
DRing::unhold(callId); | |
}); | |
} | |
void | |
RingD::unPauseCall(String ^ callId) | |
{ | |
tasks_.add_task([this, callId]() { | |
auto _callId = Utils::toString(callId); | |
DRing::unhold(_callId); | |
}); | |
} | |
void | |
RingD::hangUpCall2(String ^ callId) | |
{ | |
tasks_.add_task([this, callId]() { | |
auto _callId = Utils::toString(callId); | |
DRing::hangUp(_callId); | |
}); | |
} | |
void | |
RingD::cancelOutGoingCall2(String ^ callId) | |
{ | |
hangUpCall2(callId); | |
} | |
void | |
RingD::getKnownDevices(String^ accountId) | |
{ | |
tasks_.add_task([this, accountId]() { | |
auto _accountId = Utils::toString(accountId); | |
auto devicesList = DRing::getKnownRingDevices(_accountId); | |
if (devicesList.empty()) | |
return; | |
Utils::runOnUIThread([=]() { | |
devicesListRefreshed(Utils::convertMap(devicesList)); | |
}); | |
}); | |
} | |
void | |
RingD::ExportOnRing(String ^ accountId, String ^ password) | |
{ | |
tasks_.add_task([this, accountId, password]() { | |
auto _accountId = Utils::toString(accountId); | |
auto _password = Utils::toString(password); | |
DRing::exportOnRing(_accountId, _password); | |
}); | |
} | |
void | |
RingD::updateAccount(String^ accountId) | |
{ | |
tasks_.add_task([this, accountId]() { | |
auto _accountId = Utils::toString(accountId); | |
auto account = AccountListItemsViewModel::instance->findItem(accountId)->_account; | |
std::map<std::string, std::string> accountDetails = DRing::getAccountDetails(_accountId); | |
std::map<std::string, std::string> accountDetailsOld(accountDetails); | |
std::vector<std::map<std::string, std::string>> credentials = DRing::getCredentials(_accountId); | |
std::vector<std::map<std::string, std::string>> credentialsOld(credentials); | |
accountDetails[DRing::Account::ConfProperties::ALIAS] = Utils::toString(account->name_); | |
accountDetails[DRing::Account::ConfProperties::ENABLED] = (account->_active) ? ring::TRUE_STR : ring::FALSE_STR; | |
accountDetails[DRing::Account::ConfProperties::AUTOANSWER] = (account->_autoAnswer) ? ring::TRUE_STR : ring::FALSE_STR; | |
bool userNameAdded = false; | |
auto newUsername = Utils::toString(account->_username); | |
if (accountDetails[DRing::Account::ConfProperties::TYPE] == "RING") { | |
if (account->_username != "") { | |
auto oldUsername = registeredName(account); | |
userNameAdded = newUsername.compare(oldUsername) != 0; | |
} | |
accountDetails[DRing::Account::ConfProperties::RING_DEVICE_NAME] = Utils::toString(account->_deviceName); | |
accountDetails[DRing::Account::ConfProperties::UPNP_ENABLED] = (account->_upnpState) ? ring::TRUE_STR : ring::FALSE_STR; | |
accountDetails[DRing::Account::ConfProperties::DHT::PUBLIC_IN_CALLS] = (account->_dhtPublicInCalls) ? ring::TRUE_STR : ring::FALSE_STR; | |
accountDetails[DRing::Account::ConfProperties::TURN::ENABLED] = (account->_turnEnabled) ? ring::TRUE_STR : ring::FALSE_STR; | |
accountDetails[DRing::Account::ConfProperties::TURN::SERVER] = Utils::toString(account->_turnAddress); | |
} | |
else { | |
accountDetails[DRing::Account::ConfProperties::HOSTNAME] = Utils::toString(account->_sipHostname); | |
credentials.at(0)[DRing::Account::ConfProperties::PASSWORD] = Utils::toString(account->_sipPassword); | |
credentials.at(0)[DRing::Account::ConfProperties::USERNAME] = Utils::toString(account->_sipUsername); | |
} | |
bool detailsChanged = (accountDetails != accountDetailsOld || userNameAdded); | |
bool credentialsChanged = (credentials != credentialsOld); | |
if (!detailsChanged && !credentialsChanged) { | |
OnaccountUpdated(); | |
return; | |
} | |
Utils::runOnUIThread([this]() { | |
isUpdatingAccount = true; | |
setOverlayStatusText(ResourceMananger::instance->getStringResource("_m_updating_account_"), SuccessColor); | |
mainPage->showLoadingOverlay(true, true); | |
}); | |
if (credentialsChanged) { | |
DRing::setCredentials(_accountId, credentials); | |
} | |
DRing::setAccountDetails(_accountId, accountDetails); | |
if (userNameAdded) { | |
registerName(account->accountID_, "", account->_username); | |
} | |
Configuration::UserPreferences::instance->save(); | |
}); | |
} | |
void | |
RingD::deleteAccount(String ^ accountId) | |
{ | |
Utils::runOnUIThread([this]() { | |
isDeletingAccount = true; | |
setOverlayStatusText(ResourceMananger::instance->getStringResource("_m_deleting_account_"), "#ffff0000"); | |
mainPage->showLoadingOverlay(true, true); | |
}); | |
tasks_.add_task([this, accountId]() { | |
auto _accountId = Utils::toString(accountId); | |
DRing::removeAccount(_accountId); | |
}); | |
} | |
void | |
RingD::registerThisDevice(String ^ pin, String ^ archivePassword) | |
{ | |
Utils::runOnUIThread([this]() { | |
showLoadingOverlay(ResourceMananger::instance->getStringResource("_m_creating_account_"), SuccessColor); | |
isAddingAccount = true; | |
}); | |
tasks_.add_task([this, pin, archivePassword]() { | |
auto _pin = Utils::toString(pin); | |
auto _password = Utils::toString(archivePassword); | |
std::map<std::string, std::string> deviceDetails; | |
deviceDetails.insert(std::make_pair(DRing::Account::ConfProperties::TYPE, "RING")); | |
deviceDetails.insert(std::make_pair(DRing::Account::ConfProperties::ARCHIVE_PIN, _pin)); | |
deviceDetails.insert(std::make_pair(DRing::Account::ConfProperties::ARCHIVE_PASSWORD, _password)); | |
DRing::addAccount(deviceDetails); | |
}); | |
} | |
void | |
RingD::muteVideo(String ^ callId, bool muted) | |
{ | |
tasks_.add_task([this, callId, muted]() { | |
auto _callId = Utils::toString(callId); | |
DRing::muteLocalMedia(_callId, DRing::Media::Details::MEDIA_TYPE_VIDEO, muted); | |
}); | |
} | |
void | |
RingD::muteAudio(const std::string& callId, bool muted) | |
{ | |
tasks_.add_task([this, callId, muted]() { | |
auto _callId = callId; | |
DRing::muteLocalMedia(_callId, DRing::Media::Details::MEDIA_TYPE_AUDIO, muted); | |
}); | |
} | |
void | |
RingD::lookUpName(const std::string& accountId, String^ name) | |
{ | |
tasks_.add_task([this, accountId, name]() { | |
auto _accountId = accountId; | |
auto _name = Utils::toString(name); | |
DRing::lookupName(_accountId, "", _name); | |
}); | |
} | |
void | |
RingD::lookUpAddress(const std::string& accountId, String^ address) | |
{ | |
tasks_.add_task([this, accountId, address]() { | |
auto _accountId = accountId; | |
auto _address = Utils::toString(address); | |
DRing::lookupAddress(_accountId, "", _address); | |
}); | |
} | |
void | |
RingD::revokeDevice(const std::string& accountId, const std::string& password, const std::string& deviceId) | |
{ | |
Utils::runOnUIThread([this, deviceId]() { | |
auto msg = ResourceMananger::instance->getStringResource("_m_revoking_device_"); | |
showLoadingOverlay(msg + Utils::toPlatformString(deviceId), SuccessColor); | |
}); | |
tasks_.add_task([this, accountId, password, deviceId]() { | |
auto _accountId = accountId; | |
auto _password = password; | |
auto _deviceId = deviceId; | |
DRing::revokeDevice(_accountId, _password, _deviceId); | |
}); | |
} | |
void | |
RingD::registerName(String^ accountId, String^ password, String^ username) | |
{ | |
auto account = AccountsViewModel::instance->findItem(accountId); | |
if (!account || account->accountType_ != "RING" || !account->ringID_) | |
return; | |
tasks_.add_task([this, accountId, password, username]() { | |
auto _accountId = Utils::toString(accountId); | |
auto _password = Utils::toString(password); | |
auto _username = Utils::toString(username); | |
DRing::registerName(_accountId, _password, _username); | |
}); | |
} | |
void | |
RingD::subscribeBuddy(const std::string& accountId, const std::string& uri, bool flag) | |
{ | |
tasks_.add_task([this, accountId, uri, flag]() { | |
auto _accountId = accountId; | |
auto _uri = uri; | |
DRing::subscribeBuddy(_accountId, _uri, true); | |
}); | |
} | |
void | |
RingD::removeContact(const std::string& accountId, const std::string& uri) | |
{ | |
tasks_.add_task([this, accountId, uri]() { | |
auto _accountId = accountId; | |
auto _uri = uri; | |
DRing::removeContact(_accountId, _uri, false); | |
}); | |
} | |
void | |
RingD::sendContactRequest(const std::string& accountId, const std::string& uri, const std::string& payload) | |
{ | |
tasks_.add_task([this, accountId, uri, payload]() { | |
auto _accountId = accountId; | |
auto _uri = uri; | |
auto _payload = payload; | |
std::vector<uint8_t> payload(_payload.begin(), _payload.end()); | |
DRing::sendTrustRequest(_accountId, _uri, payload); | |
}); | |
} | |
void | |
RingD::ShowCallToast(bool background, String^ callId, String^ name) | |
{ | |
auto item = SmartPanelItemsViewModel::instance->findItem(callId); | |
if (!item) | |
return; | |
auto bestName = name != nullptr ? name : item->_contact->_bestName2; | |
String^ avatarUri = item->_contact->_avatarImage; | |
if (avatarUri == nullptr || avatarUri == L" ") | |
avatarUri = ref new String(L"ms-appx:///Assets/TESTS/contactAvatar.png"); | |
String^ xml = | |
"<toast scenario='incomingCall'>" | |
"<visual> " | |
"<binding template='ToastGeneric'>" | |
"<image placement='appLogoOverride' hint-crop='circle' src='"; | |
xml += avatarUri + "'/>"; | |
xml += "<text>" + bestName + "</text>"; | |
auto acceptArgString = "a:" + callId; | |
auto refuseArgString = "r:" + callId; | |
xml += | |
"</binding>" | |
"</visual>" | |
"<actions>" | |
"<action activationType='foreground' arguments= '" + acceptArgString + "' content='Accept' />" | |
"<action activationType='foreground' arguments= '" + refuseArgString + "' content='Refuse' />" | |
"</actions>" | |
"<audio silent='true' loop='true'/></toast>"; | |
auto doc = ref new XmlDocument(); | |
doc->LoadXml(xml); | |
toastCall = ref new ToastNotification(doc); | |
toastCall->Dismissed += ref new TypedEventHandler<ToastNotification ^, ToastDismissedEventArgs ^>( | |
[this](ToastNotification ^sender, ToastDismissedEventArgs ^args) {callToastPopped_ = false; }); | |
toastCall->Failed += ref new TypedEventHandler<ToastNotification ^, ToastFailedEventArgs ^>( | |
[this](ToastNotification ^sender, ToastFailedEventArgs ^args) {callToastPopped_ = false; }); | |
toaster->Show(toastCall); | |
callToastPopped_ = true; | |
} | |
void | |
RingD::ShowIMToast(bool background, String^ from, String^ payload, String^ name) | |
{ | |
auto item = SmartPanelItemsViewModel::instance->findItemByRingID(from); | |
if (!item) | |
return; | |
IBuffer^ buffUTF8 = CryptographicBuffer::ConvertStringToBinary(payload, BinaryStringEncoding::Utf8); | |
auto binMessage = Utils::toPlatformString(Utils::getData(buffUTF8)); | |
auto bestName = name != nullptr ? name : item->_contact->_bestName2; | |
String^ avatarUri = item->_contact->_avatarImage; | |
if (avatarUri == nullptr || avatarUri == L" ") | |
avatarUri = ref new String(L"ms-appx:///Assets/TESTS/contactAvatar.png"); | |
String^ xml = | |
"<toast scenario='incomingMessage' duration='short'>" | |
"<visual> " | |
"<binding template='ToastGeneric'>" | |
"<image placement='appLogoOverride' hint-crop='circle' src='"; | |
xml += avatarUri + "'/>"; | |
xml += "<text>" + bestName + "</text>"; | |
xml += "<text>" + binMessage + "</text>"; | |
xml += | |
"</binding>" | |
"</visual>"; | |
/*"<actions>" | |
"<input id='replyTextBox' type='text' placeholderContent='Type a reply'/>" | |
"<action content='Reply' arguments='action=reply&convId=9318' activationType='background' hint-inputId='replyTextBox'/>" | |
"</actions>";*/ | |
xml += "<audio src='ms-appx:///Assets/ringtone_notify.wav' loop='false'/></toast>"; | |
auto doc = ref new XmlDocument(); | |
doc->LoadXml(xml); | |
toastText = ref new ToastNotification(doc); | |
toaster->Show(toastText); | |
} | |
void | |
RingD::HideToast(ToastNotification^ toast) | |
{ | |
toaster->Hide(toast); | |
} | |
void | |
RingD::handleIncomingMessage( const std::string& callId, | |
const std::string& accountId, | |
const std::string& from, | |
const std::map<std::string, std::string>& payloads) | |
{ | |
auto callId2 = toPlatformString(callId); | |
auto accountId2 = toPlatformString(accountId); | |
auto from2 = toPlatformString(from); | |
from2 = Utils::TrimRingId2(from2); | |
auto item = SmartPanelItemsViewModel::instance->findItemByRingID(from2); | |
Contact^ contact; | |
static const unsigned int profileSize = VCardUtils::PROFILE_VCF.size(); | |
for (auto i : payloads) { | |
if (i.first.compare(0, profileSize, VCardUtils::PROFILE_VCF) == 0) { | |
if (item) { | |
contact = item->_contact; | |
contact->getVCard()->receiveChunk(i.first, i.second); | |
} | |
else | |
WNG_("item not found!"); | |
return; | |
} | |
auto payload = Utils::toPlatformString(i.second); | |
CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync( | |
CoreDispatcherPriority::High, ref new DispatchedHandler([=]() | |
{ | |
if (!accountId2->IsEmpty()) | |
incomingAccountMessage(accountId2, from2, payload); | |
else if (!callId2->IsEmpty()) | |
incomingMessage(callId2, payload); | |
})); | |
} | |
} | |
void | |
RingD::registerCallbacks() | |
{ | |
dispatcher = CoreApplication::MainView->CoreWindow->Dispatcher; | |
callHandlers = { | |
DRing::exportable_callback<DRing::CallSignal::IncomingCall>([this]( | |
const std::string& accountId, | |
const std::string& callId, | |
const std::string& from) | |
{ | |
MSG_("<IncomingCall>"); | |
MSG_("accountId = " + accountId); | |
MSG_("callId = " + callId); | |
MSG_("from = " + from); | |
auto accountId2 = toPlatformString(accountId); | |
auto callId2 = toPlatformString(callId); | |
auto from2 = toPlatformString(from); | |
/* fix some issue in the daemon --> <...@...> */ | |
from2 = Utils::TrimRingId(from2); | |
CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync( | |
CoreDispatcherPriority::High, ref new DispatchedHandler([=]() | |
{ | |
auto account = AccountListItemsViewModel::instance->findItem(accountId2)->_account; | |
if (auto contactListModel = AccountsViewModel::instance->getContactListModel(std::string(accountId))) { | |
Contact^ contact; | |
if (account->accountType_ == "RING") | |
contact = contactListModel->findContactByRingId(from2); | |
else if (account->accountType_ == "SIP") | |
contact = contactListModel->findContactByName(from2); | |
if (contact) { | |
auto item = SmartPanelItemsViewModel::instance->findItem(contact); | |
if (item) | |
item->_callId = callId2; | |
} | |
} | |
if (account->_autoAnswer) { | |
incomingCall(accountId2, callId2, from2); | |
acceptIncommingCall(callId2); | |
stateChange(callId2, CallStatus::AUTO_ANSWERING, 0); | |
} | |
else { | |
incomingCall(accountId2, callId2, from2); | |
stateChange(callId2, CallStatus::INCOMING_RINGING, 0); | |
} | |
})); | |
}), | |
DRing::exportable_callback<DRing::CallSignal::SmartInfo>([this]( | |
const std::map<std::string, std::string>& info) | |
{ | |
Utils::runOnUIThread([this, info]() {updateSmartInfo(info); }); | |
}), | |
DRing::exportable_callback<DRing::CallSignal::PeerHold>([this]( | |
const std::string& callId, | |
bool state) | |
{ | |
// why does this callback exist ? why are we not using stateChange ? | |
MSG_("<PeerHold>"); | |
MSG_("callId = " + callId); | |
MSG_("state = " + Utils::toString(state.ToString())); | |
CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync( | |
CoreDispatcherPriority::High, ref new DispatchedHandler([=]() | |
{ | |
if (state) | |
stateChange(Utils::toPlatformString(callId), CallStatus::PEER_PAUSED, 0); | |
else | |
stateChange(Utils::toPlatformString(callId), CallStatus::IN_PROGRESS, 0); | |
})); | |
}), | |
DRing::exportable_callback<DRing::CallSignal::AudioMuted>([this]( | |
const std::string& callId, | |
bool state) | |
{ | |
// why does this callback exist ? why are we not using stateChange ? | |
MSG_("<AudioMuted>"); | |
MSG_("callId = " + callId); | |
MSG_("state = " + Utils::toString(state.ToString())); | |
CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync( | |
CoreDispatcherPriority::High, ref new DispatchedHandler([=]() | |
{ | |
audioMuted(callId, state); | |
})); | |
}), | |
DRing::exportable_callback<DRing::CallSignal::VideoMuted>([this]( | |
const std::string& callId, | |
bool state) | |
{ | |
// why this cllaback exist ? why are we not using stateChange ? | |
MSG_("<VideoMuted>"); | |
MSG_("callId = " + callId); | |
MSG_("state = " + Utils::toString(state.ToString())); | |
CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync( | |
CoreDispatcherPriority::High, ref new DispatchedHandler([=]() | |
{ | |
videoMuted(callId, state); | |
})); | |
}), | |
DRing::exportable_callback<DRing::CallSignal::StateChange>([this]( | |
const std::string& callId, | |
const std::string& state, | |
int code) | |
{ | |
MSG_("<StateChange>"); | |
MSG_("callId = " + callId); | |
MSG_("state = " + state); | |
MSG_("code = " + std::to_string(code)); | |
auto item = SmartPanelItemsViewModel::instance->findItem(Utils::toPlatformString(callId)); | |
if (item == nullptr) | |
return; | |
auto callId2 = toPlatformString(callId); | |
auto state2 = toPlatformString(state); | |
auto state3 = translateCallStatus(state2); | |
CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync( | |
CoreDispatcherPriority::High, ref new DispatchedHandler([=]() | |
{ | |
stateChange(callId2, state3, code); | |
})); | |
}), | |
DRing::exportable_callback<DRing::CallSignal::IncomingMessage>([&]( | |
const std::string& callId, | |
const std::string& from, | |
const std::map<std::string, std::string>& payloads) | |
{ | |
MSG_("<IncomingMessage>"); | |
MSG_("callId = " + callId); | |
MSG_("from = " + from); | |
handleIncomingMessage(callId, "", from, payloads); | |
}), | |
DRing::exportable_callback<DRing::DebugSignal::MessageSend>([&](const std::string& msg) | |
{ | |
if (debugModeOn_) { | |
DMSG_(msg); | |
} | |
}) | |
}; | |
registerCallHandlers(callHandlers); | |
configurationHandlers = { | |
DRing::exportable_callback<DRing::ConfigurationSignal::IncomingAccountMessage>([&]( | |
const std::string& accountId, | |
const std::string& from, | |
const std::map<std::string, std::string>& payloads) | |
{ | |
MSG_("<IncomingAccountMessage>"); | |
MSG_("accountId = " + accountId); | |
MSG_("from = " + from); | |
handleIncomingMessage("", accountId, from, payloads); | |
}), | |
DRing::exportable_callback<DRing::ConfigurationSignal::AccountMessageStatusChanged>([this]( | |
const std::string& account_id, | |
uint64_t message_id, | |
const std::string& to, | |
int state) | |
{ | |
MSG_("<AccountMessageStatusChanged>"); | |
MSG_("account_id = " + account_id); | |
MSG_("message_id = " + message_id); | |
MSG_("to = " + Utils::toPlatformString(to)); | |
MSG_("state = " + state.ToString()); | |
dispatcher->RunAsync(CoreDispatcherPriority::High, | |
ref new DispatchedHandler([=]() { | |
// acquittement de message | |
auto contactListModel = AccountsViewModel::instance->getContactListModel(std::string(account_id)); | |
if (auto contact = contactListModel->findContactByRingId(Utils::toPlatformString(to))) { | |
auto conversation = contact->_conversation; | |
if (conversation) { | |
for (const auto& msg : conversation->_messages) { | |
if (msg->MessageIdInteger == message_id && | |
state == Utils::toUnderlyingValue(DRing::Account::MessageStates::SENT)) { | |
messageStatusUpdated(msg->MessageId, state); | |
msg->IsReceived = true; | |
contact->saveConversationToFile(); | |
} | |
} | |
} | |
} | |
})); | |
}), | |
DRing::exportable_callback<DRing::ConfigurationSignal::RegistrationStateChanged>([this]( | |
const std::string& account_id, const std::string& state, | |
int detailsCode, const std::string& detailsStr) | |
{ | |
MSG_("<RegistrationStateChanged>: ID = " + account_id + " state = " + state); | |
if (state == DRing::Account::States::REGISTERED) { | |
auto allAccountDetails = getAllAccountDetails(); | |
Utils::runOnUIThread([this, account_id, allAccountDetails]() { | |
if (auto account = AccountsViewModel::instance->findItem(Utils::toPlatformString(account_id))) | |
account->_registrationState = RegistrationState::REGISTERED; | |
parseAccountDetails(allAccountDetails); | |
subscribeBuddies(); | |
if (isAddingAccount) | |
OnaccountAdded(account_id); | |
else if (isUpdatingAccount) | |
OnaccountUpdated(); | |
}); | |
} | |
else if (state == DRing::Account::States::UNREGISTERED) { | |
auto allAccountDetails = getAllAccountDetails(); | |
Utils::runOnUIThread([this, account_id, allAccountDetails]() { | |
if (auto account = AccountsViewModel::instance->findItem(Utils::toPlatformString(account_id))) | |
account->_registrationState = RegistrationState::UNREGISTERED; | |
parseAccountDetails(allAccountDetails); | |
registrationStateUnregistered(account_id); | |
if (isAddingAccount) | |
OnaccountAdded(account_id); | |
else if (isUpdatingAccount) | |
OnaccountUpdated(); | |
}); | |
} | |
else if (state == DRing::Account::States::TRYING) { | |
auto allAccountDetails = getAllAccountDetails(); | |
Utils::runOnUIThread([this, account_id, allAccountDetails]() { | |
if (auto account = AccountsViewModel::instance->findItem(Utils::toPlatformString(account_id))) | |
account->_registrationState = RegistrationState::TRYING; | |
parseAccountDetails(allAccountDetails); | |
subscribeBuddies(); | |
Configuration::UserPreferences::instance->load(); | |
registrationStateTrying(account_id); | |
if (isAddingAccount) | |
OnaccountAdded(account_id); | |
else if (isUpdatingAccount) | |
OnaccountUpdated(); | |
}); | |
} | |
else if (state == DRing::Account::States::ERROR_GENERIC | |
|| state == DRing::Account::States::ERROR_AUTH | |
|| state == DRing::Account::States::ERROR_NETWORK | |
|| state == DRing::Account::States::ERROR_HOST | |
|| state == DRing::Account::States::ERROR_CONF_STUN | |
|| state == DRing::Account::States::ERROR_EXIST_STUN | |
|| state == DRing::Account::States::ERROR_SERVICE_UNAVAILABLE | |
|| state == DRing::Account::States::ERROR_NOT_ACCEPTABLE | |
|| state == DRing::Account::States::ERROR_NEED_MIGRATION | |
|| state == DRing::Account::States::REQUEST_TIMEOUT) { | |
auto allAccountDetails = getAllAccountDetails(); | |
Utils::runOnUIThread([this, account_id, allAccountDetails]() { | |
parseAccountDetails(allAccountDetails); | |
registrationStateErrorGeneric(account_id); | |
if (isAddingAccount) | |
OnaccountAdded(account_id); | |
else if (isUpdatingAccount) | |
OnaccountUpdated(); | |
}); | |
} | |
}), | |
DRing::exportable_callback<DRing::ConfigurationSignal::AccountsChanged>([this]() | |
{ | |
MSG_("<AccountsChanged>"); | |
if (isDeletingAccount) { | |
/*if (AccountListItemsViewModel::instance->itemsList->Size != 0) { | |
accountUpdated(nullptr); | |
}*/ | |
OnaccountDeleted(); | |
} | |
else { | |
auto allAccountDetails = getAllAccountDetails(); | |
Utils::runOnUIThread([this, allAccountDetails]() { | |
parseAccountDetails(allAccountDetails); | |
}); | |
} | |
}), | |
DRing::exportable_callback<DRing::ConfigurationSignal::NameRegistrationEnded>([this]( | |
const std::string& account_id, int state, const std::string& name) | |
{ | |
MSG_("<NameRegistrationEnded>"); | |
bool res = state == 0; | |
Utils::runOnUIThread([this, res, account_id]() { | |
nameRegistered(res, Utils::toPlatformString(account_id)); | |
mainPage->showLoadingOverlay(false, false); | |
}); | |
if (!res) | |
return; | |
auto account = AccountsViewModel::instance->findItem(Utils::toPlatformString(account_id)); | |
account->_username = Utils::toPlatformString(name); | |
AccountListItemsViewModel::instance->update({"_username"}); | |
}), | |
DRing::exportable_callback<DRing::ConfigurationSignal::GetAppDataPath>([this]( | |
const std::string& name, std::vector<std::string>* paths) | |
{ | |
paths->emplace_back(localFolder_); | |
}), | |
DRing::exportable_callback<DRing::ConfigurationSignal::GetDeviceName>([this]( | |
std::vector<std::string>* hostNames) | |
{ | |
hostNames->emplace_back(Utils::getHostName()); | |
}), | |
DRing::exportable_callback<DRing::ConfigurationSignal::KnownDevicesChanged>([&]( | |
const std::string& accountId, const std::map<std::string, std::string>& devices) | |
{ | |
dispatcher->RunAsync(CoreDispatcherPriority::High, | |
ref new DispatchedHandler([=]() { | |
if (isAddingDevice) { | |
isAddingDevice = false; | |
hideLoadingOverlay("This device has been successfully added", SuccessColor); | |
} | |
MSG_("<KnownDevicesChanged>"); | |
getKnownDevices(Utils::toPlatformString(accountId)); | |
})); | |
}), | |
DRing::exportable_callback<DRing::ConfigurationSignal::ExportOnRingEnded>([&]( | |
const std::string& accountId, int status, const std::string& pin) | |
{ | |
auto accountId2 = Utils::toPlatformString(accountId); | |
auto pin2 = (pin.empty()) ? "Error bad password" : "Your generated PIN : " + Utils::toPlatformString(pin); | |
dispatcher->RunAsync(CoreDispatcherPriority::High, | |
ref new DispatchedHandler([=]() { | |
exportOnRingEnded(accountId2, pin2); | |
})); | |
}), | |
DRing::exportable_callback<DRing::ConfigurationSignal::RegisteredNameFound>([this]( | |
const std::string &accountId, int status, const std::string &address, const std::string &name) | |
{ | |
MSG_("<RegisteredNameFound>" + name + " : " + address + " status=" +std::to_string(status)); | |
if (accountId.empty() && address.empty() && name.empty()) | |
return; | |
CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(CoreDispatcherPriority::Normal, | |
ref new DispatchedHandler([=]() { | |
switch (status) | |
{ | |
case 0: // everything went fine. Name/address pair was found. | |
registeredNameFound(LookupStatus::SUCCESS, accountId, address, name); | |
break; | |
case 1: // provided name is not valid. | |
registeredNameFound(LookupStatus::INVALID_NAME, accountId, address, name); | |
break; | |
case 2: // everything went fine. Name/address pair was not found. | |
registeredNameFound(LookupStatus::NOT_FOUND, accountId, address, name); | |
break; | |
case 3: // An error happened | |
registeredNameFound(LookupStatus::ERRORR, accountId, address, name); | |
break; | |
} | |
})); | |
}), | |
DRing::exportable_callback<DRing::ConfigurationSignal::VolatileDetailsChanged>([this]( | |
const std::string& accountId, const std::map<std::string, std::string>& details) | |
{ | |
ref new DispatchedHandler([=]() { | |
volatileDetailsChanged(accountId, details); | |
}); | |
}), | |
DRing::exportable_callback<DRing::ConfigurationSignal::IncomingTrustRequest>([&]( | |
const std::string& account_id, | |
const std::string& from, | |
const std::vector<uint8_t>& payload, | |
time_t received) | |
{ | |
dispatcher->RunAsync(CoreDispatcherPriority::High, | |
ref new DispatchedHandler([=]() { | |
auto payloadString = std::string(payload.begin(), payload.end()); | |
MSG_("IncomingTrustRequest"); | |
MSG_("account_id = " + account_id); | |
MSG_("from = " + from); | |
MSG_("received = " + received.ToString()); | |
MSG_("payload = " + payloadString); | |
// First check if this TR has a corresponding contact. If not, add a contact | |
// to the account's contact list with a trust status flag indicating that | |
// it should be treated as a TR, and only appear in the contact request list. | |
if (auto contactListModel = AccountsViewModel::instance->getContactListModel(std::string(account_id))) { | |
auto fromP = Utils::toPlatformString(from); | |
auto contact = contactListModel->findContactByRingId(fromP); | |
// If the contact exists, we should check to see if we have previously | |
// sent a TR to the peer. If, so we can accept this TR immediately. | |
// Otherwise, if it is not already been trusted, we can ignore it completely. | |
if (contact) { | |
if (contact->_trustStatus == TrustStatus::CONTACT_REQUEST_SENT) { | |
if (!payloadString.empty()) { | |
auto vcard = contact->getVCard(); | |
auto parsedPayload = VCardUtils::parseContactRequestPayload(payloadString); | |
vcard->setData(parsedPayload.at("VCARD")); | |
vcard->completeReception(); | |
contact->_displayName = Utils::toPlatformString(vcard->getPart("FN")); | |
} | |
DRing::acceptTrustRequest(account_id, from); | |
MSG_("Auto accepted IncomingTrustRequest"); | |
return; | |
} | |
else if (contact->_trustStatus != TrustStatus::UNKNOWN) { | |
MSG_("IncomingTrustRequest ignored"); | |
return; | |
} | |
} | |
else { | |
// No contact found, so add a new contact with the INCOMNG_CONTACT_REQUEST trust status flag | |
contact = contactListModel->addNewContact("", fromP, TrustStatus::INCOMING_CONTACT_REQUEST, false); | |
// The visible ring id will potentially be replaced by a username after a lookup | |
RingD::instance->lookUpAddress(account_id, Utils::toPlatformString(from)); | |
if (!contact) | |
return; | |
auto vcard = contact->getVCard(); | |
if (!payloadString.empty()) { | |
auto parsedPayload = VCardUtils::parseContactRequestPayload(payloadString); | |
vcard->setData(parsedPayload.at("VCARD")); | |
vcard->completeReception(); | |
contact->_displayName = Utils::toPlatformString(vcard->getPart("FN")); | |
} | |
// The name is the ring id for now | |
contact->_name = Utils::toPlatformString(from); | |
contactListModel->saveContactsToFile(); | |
AccountsViewModel::instance->raiseUnreadContactRequest(); | |
SmartPanelItemsViewModel::instance->refreshFilteredItemsList(); | |
SmartPanelItemsViewModel::instance->update(ViewModel::NotifyStrings::notifySmartPanelItem); | |
// Add a corresponding contact request control item to the list. | |
auto newContactRequest = ref new ContactRequestItem(); | |
newContactRequest->_contact = contact; | |
ContactRequestItemsViewModel::instance->itemsList->InsertAt(0, newContactRequest); | |
ContactRequestItemsViewModel::instance->refreshFilteredItemsList(); | |
ContactRequestItemsViewModel::instance->update(ViewModel::NotifyStrings::notifyContactRequestItem); | |
} | |
} | |
})); | |
}), | |
DRing::exportable_callback<DRing::ConfigurationSignal::ContactAdded>([&]( | |
const std::string& account_id, const std::string& uri, bool confirmed) | |
{ | |
dispatcher->RunAsync(CoreDispatcherPriority::High, | |
ref new DispatchedHandler([=]() { | |
MSG_("ContactAdded"); | |
MSG_("account_id = " + account_id); | |
MSG_("uri = " + uri); | |
MSG_("confirmed = " + confirmed.ToString()); | |
// If confirmed is false, we have just sent the TR and nothing need be done. | |
// If confirmed is true, the sent TR has been accepted and we can change the | |
// TrustStatus flag for the contact under the account_id, that matches the uri | |
if (confirmed) { | |
if (auto contactListModel = AccountsViewModel::instance->getContactListModel(std::string(account_id))) { | |
auto contact = contactListModel->findContactByRingId(Utils::toPlatformString(uri)); | |
if (contact == nullptr) { | |
contact = contactListModel->addNewContact( Utils::toPlatformString(uri), | |
Utils::toPlatformString(uri), | |
TrustStatus::TRUSTED, false); | |
RingD::instance->lookUpAddress(account_id, Utils::toPlatformString(uri)); | |
} | |
else { | |
contact->_trustStatus = TrustStatus::TRUSTED; | |
} | |
SmartPanelItemsViewModel::instance->refreshFilteredItemsList(); | |
SmartPanelItemsViewModel::instance->update(ViewModel::NotifyStrings::notifySmartPanelItem); | |
ContactRequestItemsViewModel::instance->update(ViewModel::NotifyStrings::notifyContactRequestItem); | |
AccountsViewModel::instance->raiseContactDataModified(Utils::toPlatformString(uri), contact); | |
contactListModel->saveContactsToFile(); | |
} | |
} | |
})); | |
}), | |
DRing::exportable_callback<DRing::ConfigurationSignal::ContactRemoved>([&]( | |
const std::string& account_id, const std::string& uri, bool banned) | |
{ | |
dispatcher->RunAsync(CoreDispatcherPriority::High, | |
ref new DispatchedHandler([=]() { | |
MSG_("ContactRemoved"); | |
MSG_("account_id = " + account_id); | |
MSG_("uri = " + uri); | |
MSG_("banned = " + banned.ToString()); | |
// It's currently not clear to me how this signal is pertinent to the UWP client | |
})); | |
}), | |
DRing::exportable_callback<DRing::ConfigurationSignal::DeviceRevocationEnded>([&]( | |
const std::string& accountId, const std::string& device, int status) | |
{ | |
MSG_("<DeviceRevocationEnded>"); | |
MSG_("account_id = " + accountId); | |
MSG_("device = " + device); | |
MSG_("status = " + status.ToString()); | |
switch (Utils::toEnum<DeviceRevocationResult>(status)) { | |
case DeviceRevocationResult::INVALID_CERTIFICATE: | |
hideLoadingOverlay("Certificate error while revoking device ID: " + Utils::toPlatformString(device), ErrorColor); | |
break; | |
case DeviceRevocationResult::INVALID_PASSWORD: | |
hideLoadingOverlay("Incorrect account password. Can't revoke device ID: " + Utils::toPlatformString(device), ErrorColor); | |
break; | |
case DeviceRevocationResult::SUCCESS: | |
getKnownDevices(Utils::toPlatformString(accountId)); | |
hideLoadingOverlay("Device with ID: " + Utils::toPlatformString(device) + " has been successfully revoked", SuccessColor); | |
break; | |
} | |
})/*, | |
DRing::exportable_callback<DRing::ConfigurationSignal::GetAppUserName>([this]( | |
std::vector<std::string>* unames) | |
{ | |
unames->emplace_back(Utils::toString( | |
UserModel::instance->firstName + | |
"." + | |
UserModel::instance->lastName)); | |
})*/ | |
}; | |
registerConfHandlers(configurationHandlers); | |
using namespace Video; | |
videoHandlers = { | |
DRing::exportable_callback<DRing::VideoSignal::DeviceEvent>([this]() | |
{ | |
MSG_("<DeviceEvent>"); | |
}), | |
DRing::exportable_callback<DRing::VideoSignal::DecodingStarted>([&]( | |
const std::string &id, const std::string &shmPath, int width, int height, bool isMixer) | |
{ | |
dispatcher->RunAsync(CoreDispatcherPriority::High, | |
ref new DispatchedHandler([=]() { | |
Video::VideoManager::instance->rendererManager()->startedDecoding( | |
Utils::toPlatformString(id), | |
width, | |
height); | |
auto callId2 = Utils::toPlatformString(id); | |
incomingVideoMuted(callId2, false); | |
})); | |
}), | |
DRing::exportable_callback<DRing::VideoSignal::DecodingStopped>([&]( | |
const std::string &id, const std::string &shmPath, bool isMixer) | |
{ | |
dispatcher->RunAsync(CoreDispatcherPriority::High, | |
ref new DispatchedHandler([=]() { | |
Video::VideoManager::instance->rendererManager()->removeRenderer(Utils::toPlatformString(id)); | |
auto callId2 = Utils::toPlatformString(id); | |
incomingVideoMuted(callId2, true); | |
})); | |
}), | |
DRing::exportable_callback<DRing::VideoSignal::DeviceAdded>([this]( | |
const std::string& device) | |
{ | |
MSG_("<DeviceAdded>"); | |
}), | |
DRing::exportable_callback<DRing::VideoSignal::ParametersChanged>([&]( | |
const std::string& device) | |
{ | |
dispatcher->RunAsync(CoreDispatcherPriority::High, | |
ref new DispatchedHandler([=]() { | |
MSG_("<ParametersChanged>"); | |
auto settings = DRing::getDeviceParams(device); | |
VideoManager::instance->captureManager()->activeDevice->SetDeviceProperties( | |
Utils::toPlatformString(settings["format"]), | |
stoi(settings["width"]), | |
stoi(settings["height"]), | |
stoi(settings["rate"])); | |
})); | |
}), | |
DRing::exportable_callback<DRing::VideoSignal::StartCapture>([&]( | |
const std::string& device) | |
{ | |
dispatcher->RunAsync(CoreDispatcherPriority::High, | |
ref new DispatchedHandler([=]() { | |
VideoManager::instance->captureManager()->InitializeCameraAsync(false); | |
VideoManager::instance->captureManager()->videoFrameCopyInvoker->Start(); | |
})); | |
}), | |
DRing::exportable_callback<DRing::VideoSignal::StopCapture>([&]() | |
{ | |
dispatcher->RunAsync(CoreDispatcherPriority::High, | |
ref new DispatchedHandler([=]() { | |
VideoManager::instance->captureManager()->StopPreviewAsync(); | |
if (VideoManager::instance->captureManager()->captureTaskTokenSource) | |
VideoManager::instance->captureManager()->captureTaskTokenSource->cancel(); | |
VideoManager::instance->captureManager()->videoFrameCopyInvoker->Stop(); | |
})); | |
}) | |
}; | |
registerVideoHandlers(videoHandlers); | |
presenceHandlers = { | |
DRing::exportable_callback<DRing::PresenceSignal::NewBuddyNotification>([&]( | |
const std::string& account_id, const std::string& buddy_uri, int status, const std::string& /*line_status*/) | |
{ | |
dispatcher->RunAsync(CoreDispatcherPriority::High, | |
ref new DispatchedHandler([=]() { | |
MSG_("NewBuddyNotification"); | |
MSG_("account_id = " + account_id); | |
MSG_("uri = " + buddy_uri); | |
MSG_("status = " + status); | |
// react to presence | |
dispatcher->RunAsync(CoreDispatcherPriority::High, | |
ref new DispatchedHandler([=]() { | |
newBuddyNotification(account_id, buddy_uri, status); | |
})); | |
})); | |
}) | |
}; | |
registerPresHandlers(presenceHandlers); | |
} | |
void | |
RingD::OnaccountAdded(const std::string& accountId) | |
{ | |
if (shouldRegister) { | |
shouldRegister = false; | |
registerName(Utils::toPlatformString(accountId), "", nameToRegister); | |
} | |
isAddingAccount = false; | |
hideLoadingOverlay("Account created successfully", SuccessColor, 2000); | |
Configuration::UserPreferences::instance->raiseSelectIndex(0); | |
Configuration::UserPreferences::instance->save(); | |
} | |
void | |
RingD::OnaccountUpdated() | |
{ | |
isUpdatingAccount = false; | |
hideLoadingOverlay("Account updated successfully", SuccessColor, 500); | |
} | |
void | |
RingD::OnaccountDeleted() | |
{ | |
isDeletingAccount = false; | |
if (AccountListItemsViewModel::instance->itemsList->Size == 0) { | |
auto configFile = RingD::instance->getLocalFolder() + ".config\\dring.yml"; | |
Utils::fileDelete(configFile); | |
Utils::runOnUIThreadDelayed(100,[this]() {summonWizard(); }); | |
} | |
else { | |
hideLoadingOverlay("Account deleted successfully", SuccessColor, 500); | |
} | |
} | |
void | |
RingD::init() | |
{ | |
if (daemonInitialized_) { | |
CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(CoreDispatcherPriority::Normal, | |
ref new DispatchedHandler([=]() { | |
finishCaptureDeviceEnumeration(); | |
})); | |
return; | |
} | |
setOverlayStatusText("Starting Ring...", "#ff000000"); | |
gnutls_global_init(); | |
RingD::instance->registerCallbacks(); | |
RingD::instance->initDaemon( DRing::DRING_FLAG_CONSOLE_LOG | DRing::DRING_FLAG_DEBUG ); | |
Video::VideoManager::instance->captureManager()->EnumerateWebcamsAsync(); | |
daemonInitialized_ = true; | |
} | |
void | |
RingD::deinit() | |
{ | |
DRing::fini(); | |
gnutls_global_deinit(); | |
} | |
void | |
RingD::initDaemon(int flags) | |
{ | |
DRing::init(static_cast<DRing::InitFlag>(flags)); | |
} | |
void | |
RingD::startDaemon() | |
{ | |
if (daemonRunning_) { | |
ERR_("daemon already runnging"); | |
return; | |
} | |
IAsyncAction^ action = ThreadPool::RunAsync(ref new WorkItemHandler([=](IAsyncAction^ spAction) | |
{ | |
if (!isInWizard) { | |
CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(CoreDispatcherPriority::Normal, | |
ref new DispatchedHandler([=]() { | |
setOverlayStatusText("Loading from config...", "#ff000000"); | |
})); | |
} | |
daemonRunning_ = DRing::start(); | |
auto vcm = Video::VideoManager::instance->captureManager(); | |
if (vcm->deviceList->Size > 0) { | |
std::string deviceName = DRing::getDefaultDevice(); | |
std::map<std::string, std::string> settings = DRing::getSettings(deviceName); | |
int rate = stoi(settings["rate"]); | |
std::string size = settings["size"]; | |
std::string::size_type pos = size.find('x'); | |
int width = std::stoi(size.substr(0, pos)); | |
int height = std::stoi(size.substr(pos + 1, size.length())); | |
for (auto dev : vcm->deviceList) { | |
if (!Utils::toString(dev->name()).compare(deviceName)) | |
vcm->activeDevice = dev; | |
} | |
vcm->activeDevice->SetDeviceProperties("", width, height, rate); | |
CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(CoreDispatcherPriority::Normal, | |
ref new DispatchedHandler([=]() { | |
finishCaptureDeviceEnumeration(); | |
})); | |
} | |
if (!daemonRunning_) { | |
ERR_("\ndaemon didn't start.\n"); | |
return; | |
} | |
else { | |
switch (_startingStatus) { | |
case StartingStatus::REGISTERING_ON_THIS_PC: | |
case StartingStatus::REGISTERING_THIS_DEVICE: | |
{ | |
break; | |
} | |
case StartingStatus::NORMAL: | |
default: | |
{ | |
break; | |
} | |
} | |
/* at this point the config.yml is safe. */ | |
std::string tokenFile = localFolder_ + "\\creation.token"; | |
if (fileExists(tokenFile)) { | |
fileDelete(tokenFile); | |
} | |
if (!isInWizard) { | |
hideLoadingOverlay("Ring started successfully", SuccessColor, 1000); | |
} | |
while (daemonRunning) { | |
DRing::pollEvents(); | |
tasks_.dequeue_tasks(); | |
Sleep(5); | |
} | |
} | |
},Platform::CallbackContext::Any), WorkItemPriority::High); | |
} | |
RingD::RingD() | |
{ | |
archive_has_password = ref new Map<String^, bool>(); | |
toaster = ToastNotificationManager::CreateToastNotifier(); | |
NetworkInformation::NetworkStatusChanged += ref new NetworkStatusChangedEventHandler(this, &RingD::InternetConnectionChanged); | |
this->stateChange += ref new StateChange(this, &RingD::onStateChange); | |
ringtone_ = ref new Ringtone("default.wav"); | |
localFolder_ = Utils::toString(ApplicationData::Current->LocalFolder->Path); | |
callIdsList_ = ref new Vector<String^>(); | |
currentCallId = nullptr; | |
} | |
void | |
RingD::onStateChange(Platform::String ^callId, RingClientUWP::CallStatus state, int code) | |
{ | |
CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync( | |
CoreDispatcherPriority::High, ref new DispatchedHandler([=]() | |
{ | |
if (state == CallStatus::OUTGOING_RINGING || | |
state == CallStatus::IN_PROGRESS) { | |
try { | |
Configuration::UserPreferences::instance->sendVCard(Utils::toString(callId)); | |
} | |
catch (Exception^ e) { | |
EXC_(e); | |
} | |
} | |
if (state == CallStatus::INCOMING_RINGING) { | |
ringtone_->Start(); | |
} | |
if (state == CallStatus::IN_PROGRESS) { | |
ringtone_->Stop(); | |
if (callToastPopped_) { | |
HideToast(toastCall); | |
callToastPopped_ = false; | |
} | |
} | |
if (state == CallStatus::ENDED || | |
(state == CallStatus::NONE && code == 106)) { | |
if (callToastPopped_) { | |
HideToast(toastCall); | |
callToastPopped_ = false; | |
} | |
DRing::hangUp(Utils::toString(callId)); | |
ringtone_->Stop(); | |
} | |
})); | |
} | |
std::string | |
RingD::getLocalFolder() | |
{ | |
return localFolder_ + Utils::toString("\\"); | |
} | |
void | |
RingD::showLoadingOverlay(String^ text, String^ color) | |
{ | |
Utils::runOnUIThread([=]() { | |
setOverlayStatusText(text, color); | |
mainPage->showLoadingOverlay(true, true); | |
}); | |
} | |
void | |
RingD::hideLoadingOverlay(String^ text, String^ color, int delayInMilliseconds) | |
{ | |
Utils::runOnUIThread([=]() { setOverlayStatusText(text, color); }); | |
Utils::runOnUIThreadDelayed(delayInMilliseconds, [=]() { | |
mainPage->showLoadingOverlay(false, false); | |
}); | |
} | |
std::map<std::string, std::string> | |
RingD::getVolatileAccountDetails(Account^ account) | |
{ | |
return DRing::getVolatileAccountDetails(Utils::toString(account->accountID_)); | |
} | |
std::string | |
RingD::registeredName(Account^ account) | |
{ | |
auto volatileAccountDetails = DRing::getVolatileAccountDetails(Utils::toString(account->accountID_)); | |
return volatileAccountDetails[DRing::Account::VolatileProperties::REGISTERED_NAME]; | |
} | |
CallStatus | |
RingD::translateCallStatus(String^ state) | |
{ | |
if (state == "INCOMING") | |
return CallStatus::INCOMING_RINGING; | |
if (state == "CURRENT") | |
return CallStatus::IN_PROGRESS; | |
if (state == "OVER") | |
return CallStatus::ENDED; | |
if (state == "RINGING") | |
return CallStatus::OUTGOING_RINGING; | |
if (state == "CONNECTING") | |
return CallStatus::SEARCHING; | |
if (state == "HOLD") | |
return CallStatus::PAUSED; | |
if (state == "PEER_PAUSED") | |
return CallStatus::PEER_PAUSED; | |
return CallStatus::NONE; | |
} | |
String^ | |
RingD::getUserName() | |
{ | |
auto users = User::FindAllAsync(); | |
return nullptr; | |
} | |
void | |
RingD::startSmartInfo(int refresh) | |
{ | |
DRing::startSmartInfo(refresh); | |
} | |
void | |
RingD::stopSmartInfo() | |
{ | |
DRing::stopSmartInfo(); | |
} | |
void | |
RingD::setFullScreenMode() | |
{ | |
if (ApplicationView::GetForCurrentView()->TryEnterFullScreenMode()) { | |
MSG_("TryEnterFullScreenMode succeeded"); | |
fullScreenToggled(true); | |
} | |
else { | |
ERR_("TryEnterFullScreenMode failed"); | |
} | |
} | |
void | |
RingD::setWindowedMode() | |
{ | |
ApplicationView::GetForCurrentView()->ExitFullScreenMode(); | |
MSG_("ExitFullScreenMode"); | |
fullScreenToggled(false); | |
} | |
void | |
RingD::toggleFullScreen() | |
{ | |
if (isFullScreen) | |
setWindowedMode(); | |
else | |
setFullScreenMode(); | |
} | |
void | |
RingD::raiseMessageDataLoaded() | |
{ | |
messageDataLoaded(); | |
} | |
void | |
RingD::raiseWindowResized(float width, float height) | |
{ | |
windowResized(width, height); | |
} | |
void | |
RingD::raiseVCardUpdated(Contact^ contact) | |
{ | |
vCardUpdated(contact); | |
} | |
void | |
RingD::raiseShareRequested() | |
{ | |
shareRequested(); | |
} |