diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 35f295eded75049844e5fe43942a8a6a959e5dac..a7b7fa3ca76f6b8ccf8d5205785b047ec055029e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,6 +7,8 @@ list (APPEND Source_Files "${CMAKE_CURRENT_SOURCE_DIR}/account.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/account.h" + "${CMAKE_CURRENT_SOURCE_DIR}/account_config.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/account_config.h" "${CMAKE_CURRENT_SOURCE_DIR}/account_factory.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/account_factory.h" "${CMAKE_CURRENT_SOURCE_DIR}/account_schema.h" diff --git a/src/account.cpp b/src/account.cpp index 21c59ce8aebc6acb19a9b058688e4c3332f029a8..6edebfd6551bbb953f2a1e34fe5d7bdb9cd7df7f 100644 --- a/src/account.cpp +++ b/src/account.cpp @@ -63,73 +63,19 @@ using namespace std::literals; namespace jami { -const char* const Account::ALL_CODECS_KEY = "allCodecs"; -const char* const Account::VIDEO_CODEC_ENABLED = "enabled"; -const char* const Account::VIDEO_CODEC_NAME = "name"; -const char* const Account::VIDEO_CODEC_PARAMETERS = "parameters"; -const char* const Account::VIDEO_CODEC_BITRATE = "bitrate"; -const char* const Account::RINGTONE_PATH_KEY = "ringtonePath"; -const char* const Account::RINGTONE_ENABLED_KEY = "ringtoneEnabled"; -const char* const Account::VIDEO_ENABLED_KEY = "videoEnabled"; -const char* const Account::DISPLAY_NAME_KEY = "displayName"; -const char* const Account::ALIAS_KEY = "alias"; -const char* const Account::TYPE_KEY = "type"; -const char* const Account::ID_KEY = "id"; -const char* const Account::USERNAME_KEY = "username"; -const char* const Account::AUTHENTICATION_USERNAME_KEY = "authenticationUsername"; -const char* const Account::PASSWORD_KEY = "password"; -const char* const Account::HOSTNAME_KEY = "hostname"; -const char* const Account::ACCOUNT_ENABLE_KEY = "enable"; -const char* const Account::ACCOUNT_AUTOANSWER_KEY = "autoAnswer"; -const char* const Account::ACCOUNT_READRECEIPT_KEY = "sendReadReceipt"; -const char* const Account::ACCOUNT_ISRENDEZVOUS_KEY = "rendezVous"; -const char* const Account::ACCOUNT_ACTIVE_CALL_LIMIT_KEY = "activeCallLimit"; -const char* const Account::MAILBOX_KEY = "mailbox"; -const char* const Account::USER_AGENT_KEY = "useragent"; -const char* const Account::HAS_CUSTOM_USER_AGENT_KEY = "hasCustomUserAgent"; -const char* const Account::PRESENCE_MODULE_ENABLED_KEY = "presenceModuleEnabled"; -const char* const Account::UPNP_ENABLED_KEY = "upnpEnabled"; -const char* const Account::ACTIVE_CODEC_KEY = "activeCodecs"; -const std::string Account::DEFAULT_USER_AGENT = Account::setDefaultUserAgent(); -const char* const Account::DEFAULT_MODERATORS_KEY = "defaultModerators"; -const char* const Account::LOCAL_MODERATORS_ENABLED_KEY = "localModeratorsEnabled"; -const char* const Account::ALL_MODERATORS_ENABLED_KEY = "allModeratorsEnabled"; -const char* const Account::PROXY_PUSH_TOKEN_KEY = "proxyPushToken"; -const char* const Account::PROXY_PUSH_IOS_TOPIC_KEY = "proxyPushiOSTopic"; - -// For portability, do not specify the absolute file name of the ring -// tone. Instead, specify its base name to be looked in +// For portability, do not specify the absolute file name of the ringtone. +// Instead, specify its base name to be looked in // JAMI_DATADIR/ringtones/, where JAMI_DATADIR is a preprocessor macro denoting // the data directory prefix that must be set at build time. -constexpr const char* const DEFAULT_RINGTONE_PATH = "default.opus"; +const std::string Account::DEFAULT_USER_AGENT = Account::getDefaultUserAgent(); Account::Account(const std::string& accountID) : rand(dht::crypto::getSeededRandomEngine<std::mt19937_64>()) , accountID_(accountID) - , username_() - , hostname_() - , alias_() - , enabled_(true) - , autoAnswerEnabled_(false) - , sendReadReceipt_(true) - , isRendezVous_(false) , registrationState_(RegistrationState::UNREGISTERED) , systemCodecContainer_(getSystemCodecContainer()) , accountCodecInfoList_() - , ringtonePath_(DEFAULT_RINGTONE_PATH) - , ringtoneEnabled_(true) - , displayName_("") - , customUserAgent_("") - , hasCustomUserAgent_(false) - , mailBox_() - , upnpEnabled_(true) - , localModeratorsEnabled_(true) - , allModeratorsEnabled_(true) -#if defined(__linux__) || defined(WIN32) || defined(__APPLE__) - , multiStreamEnabled_(true) -#else - , multiStreamEnabled_(false) -#endif + , ringtonePath_() { // Initialize the codec order, used when creating a new account loadDefaultCodecs(); @@ -149,7 +95,7 @@ Account::updateUpnpController() { std::lock_guard<std::mutex> lk {upnp_mtx}; - if (not upnpEnabled_ or not isUsable()) { + if (not config().upnpEnabled or not isUsable()) { upnpCtrl_.reset(); return; } @@ -211,156 +157,23 @@ Account::loadDefaultCodecs() } void -Account::serialize(YAML::Emitter& out) const -{ - const auto& activeCodecs = fmt::format("{}", fmt::join(getActiveCodecs(MEDIA_ALL), "/"sv)); - - out << YAML::Key << ID_KEY << YAML::Value << accountID_; - out << YAML::Key << ALIAS_KEY << YAML::Value << alias_; - out << YAML::Key << ACCOUNT_ENABLE_KEY << YAML::Value << enabled_; - out << YAML::Key << TYPE_KEY << YAML::Value << getAccountType(); - out << YAML::Key << ACTIVE_CODEC_KEY << YAML::Value << activeCodecs; - out << YAML::Key << MAILBOX_KEY << YAML::Value << mailBox_; - out << YAML::Key << ACCOUNT_AUTOANSWER_KEY << YAML::Value << autoAnswerEnabled_; - out << YAML::Key << ACCOUNT_READRECEIPT_KEY << YAML::Value << sendReadReceipt_; - out << YAML::Key << ACCOUNT_ISRENDEZVOUS_KEY << YAML::Value << isRendezVous_; - out << YAML::Key << ACCOUNT_ACTIVE_CALL_LIMIT_KEY << YAML::Value << activeCallLimit_; - out << YAML::Key << RINGTONE_ENABLED_KEY << YAML::Value << ringtoneEnabled_; - out << YAML::Key << RINGTONE_PATH_KEY << YAML::Value << ringtonePath_; - out << YAML::Key << HAS_CUSTOM_USER_AGENT_KEY << YAML::Value << hasCustomUserAgent_; - out << YAML::Key << USER_AGENT_KEY << YAML::Value << customUserAgent_; - out << YAML::Key << DISPLAY_NAME_KEY << YAML::Value << displayName_; - out << YAML::Key << HOSTNAME_KEY << YAML::Value << hostname_; - out << YAML::Key << UPNP_ENABLED_KEY << YAML::Value << upnpEnabled_; - out << YAML::Key << DEFAULT_MODERATORS_KEY << YAML::Value << string_join(defaultModerators_); - out << YAML::Key << LOCAL_MODERATORS_ENABLED_KEY << YAML::Value << localModeratorsEnabled_; - out << YAML::Key << ALL_MODERATORS_ENABLED_KEY << YAML::Value << allModeratorsEnabled_; - out << YAML::Key << PROXY_PUSH_TOKEN_KEY << YAML::Value << deviceKey_; - out << YAML::Key << PROXY_PUSH_IOS_TOPIC_KEY << YAML::Value << notificationTopic_; -} - -void -Account::unserialize(const YAML::Node& node) -{ - using yaml_utils::parseValue; - using yaml_utils::parseValueOptional; - - parseValue(node, ALIAS_KEY, alias_); - parseValue(node, ACCOUNT_ENABLE_KEY, enabled_); - parseValue(node, ACCOUNT_AUTOANSWER_KEY, autoAnswerEnabled_); - parseValueOptional(node, ACCOUNT_READRECEIPT_KEY, sendReadReceipt_); - parseValueOptional(node, ACCOUNT_ISRENDEZVOUS_KEY, isRendezVous_); - parseValue(node, ACCOUNT_ACTIVE_CALL_LIMIT_KEY, activeCallLimit_); - // parseValue(node, PASSWORD_KEY, password_); - - parseValue(node, MAILBOX_KEY, mailBox_); - - std::string activeCodecs; - if (parseValueOptional(node, ACTIVE_CODEC_KEY, activeCodecs)) - setActiveCodecs(split_string_to_unsigned(activeCodecs, '/')); - else { - std::string allCodecs; - if (parseValueOptional(node, ALL_CODECS_KEY, allCodecs)) { - JAMI_WARN("Converting deprecated codec list"); - auto list = convertIdToAVId(split_string_to_unsigned(allCodecs, '/')); - auto codec = searchCodecByName("H265", MEDIA_ALL); - // set H265 as first active codec if found - if (codec) - list.emplace(list.begin(), codec->systemCodecInfo.id); - setActiveCodecs(list); - runOnMainThread([id = getAccountID()] { - if (auto sthis = Manager::instance().getAccount(id)) - Manager::instance().saveConfig(sthis); - }); - } +Account::loadConfig() { + setActiveCodecs(config_->activeCodecs); + auto ringtoneDir = fmt::format("{}/{}", JAMI_DATADIR, RINGDIR); + ringtonePath_ = fileutils::getFullPath(ringtoneDir, config_->ringtonePath); + // If the user defined a custom ringtone, the file may not exists + // In this case, fallback on the default ringtone path + if (!fileutils::isFile(ringtonePath_)) { + JAMI_WARNING("Ringtone {} is not a valid file", ringtonePath_); + ringtonePath_ = fileutils::getFullPath(ringtoneDir, DEFAULT_RINGTONE_PATH);; } - - parseValue(node, DISPLAY_NAME_KEY, displayName_); - parseValue(node, HOSTNAME_KEY, hostname_); - - parseValue(node, HAS_CUSTOM_USER_AGENT_KEY, hasCustomUserAgent_); - parseValue(node, USER_AGENT_KEY, customUserAgent_); - parseValue(node, RINGTONE_PATH_KEY, ringtonePath_); - parseValue(node, RINGTONE_ENABLED_KEY, ringtoneEnabled_); - if (ringtonePath_.empty()) { - ringtonePath_ = DEFAULT_RINGTONE_PATH; - } else { - // If the user defined a custom ringtone, the file may not exists - // In this case, fallback on the default ringtone path (this will be set during the next - // setAccountDetails) - auto pathRingtone = fmt::format("{}/{}/{}", JAMI_DATADIR, RINGDIR, ringtonePath_); - if (!fileutils::isFile(ringtonePath_) && !fileutils::isFile(pathRingtone)) { - JAMI_WARN("Ringtone %s is not a valid file", pathRingtone.c_str()); - ringtonePath_ = DEFAULT_RINGTONE_PATH; - } - } - - parseValue(node, UPNP_ENABLED_KEY, upnpEnabled_); updateUpnpController(); - - std::string defMod; - parseValueOptional(node, DEFAULT_MODERATORS_KEY, defMod); - defaultModerators_ = string_split_set(defMod); - parseValueOptional(node, LOCAL_MODERATORS_ENABLED_KEY, localModeratorsEnabled_); - parseValueOptional(node, ALL_MODERATORS_ENABLED_KEY, allModeratorsEnabled_); - parseValueOptional(node, PROXY_PUSH_TOKEN_KEY, deviceKey_); - parseValueOptional(node, PROXY_PUSH_IOS_TOPIC_KEY, notificationTopic_); } void -Account::setAccountDetails(const std::map<std::string, std::string>& details) +Account::saveConfig() const { - // Account setting common to any account type - parseString(details, Conf::CONFIG_ACCOUNT_ALIAS, alias_); - parseString(details, Conf::CONFIG_ACCOUNT_DISPLAYNAME, displayName_); - parseBool(details, Conf::CONFIG_ACCOUNT_ENABLE, enabled_); - parseString(details, Conf::CONFIG_ACCOUNT_HOSTNAME, hostname_); - parseString(details, Conf::CONFIG_ACCOUNT_MAILBOX, mailBox_); - parseBool(details, Conf::CONFIG_ACCOUNT_AUTOANSWER, autoAnswerEnabled_); - parseBool(details, Conf::CONFIG_ACCOUNT_SENDREADRECEIPT, sendReadReceipt_); - parseBool(details, Conf::CONFIG_ACCOUNT_ISRENDEZVOUS, isRendezVous_); - parseInt(details, libjami::Account::ConfProperties::ACTIVE_CALL_LIMIT, activeCallLimit_); - parseBool(details, Conf::CONFIG_RINGTONE_ENABLED, ringtoneEnabled_); - parseString(details, Conf::CONFIG_RINGTONE_PATH, ringtonePath_); - if (ringtonePath_.empty()) { - ringtonePath_ = DEFAULT_RINGTONE_PATH; - } - parseBool(details, Conf::CONFIG_ACCOUNT_HAS_CUSTOM_USERAGENT, hasCustomUserAgent_); - if (hasCustomUserAgent_) - parseString(details, Conf::CONFIG_ACCOUNT_USERAGENT, customUserAgent_); - - parseBool(details, Conf::CONFIG_UPNP_ENABLED, upnpEnabled_); - updateUpnpController(); - - std::string defMod; - parseString(details, Conf::CONFIG_DEFAULT_MODERATORS, defMod); - defaultModerators_ = string_split_set(defMod); - parseBool(details, Conf::CONFIG_LOCAL_MODERATORS_ENABLED, localModeratorsEnabled_); - parseBool(details, Conf::CONFIG_ALL_MODERATORS_ENABLED, allModeratorsEnabled_); -} - -std::map<std::string, std::string> -Account::getAccountDetails() const -{ - return {{Conf::CONFIG_ACCOUNT_ALIAS, alias_}, - {Conf::CONFIG_ACCOUNT_DISPLAYNAME, displayName_}, - {Conf::CONFIG_ACCOUNT_ENABLE, enabled_ ? TRUE_STR : FALSE_STR}, - {Conf::CONFIG_ACCOUNT_TYPE, getAccountType()}, - {Conf::CONFIG_ACCOUNT_HOSTNAME, hostname_}, - {Conf::CONFIG_ACCOUNT_USERNAME, username_}, - {Conf::CONFIG_ACCOUNT_MAILBOX, mailBox_}, - {Conf::CONFIG_ACCOUNT_USERAGENT, customUserAgent_}, - {Conf::CONFIG_ACCOUNT_HAS_CUSTOM_USERAGENT, hasCustomUserAgent_ ? TRUE_STR : FALSE_STR}, - {Conf::CONFIG_ACCOUNT_AUTOANSWER, autoAnswerEnabled_ ? TRUE_STR : FALSE_STR}, - {Conf::CONFIG_ACCOUNT_SENDREADRECEIPT, sendReadReceipt_ ? TRUE_STR : FALSE_STR}, - {Conf::CONFIG_ACCOUNT_ISRENDEZVOUS, isRendezVous_ ? TRUE_STR : FALSE_STR}, - {libjami::Account::ConfProperties::ACTIVE_CALL_LIMIT, std::to_string(activeCallLimit_)}, - {Conf::CONFIG_RINGTONE_ENABLED, ringtoneEnabled_ ? TRUE_STR : FALSE_STR}, - {Conf::CONFIG_RINGTONE_PATH, ringtonePath_}, - {Conf::CONFIG_UPNP_ENABLED, upnpEnabled_ ? TRUE_STR : FALSE_STR}, - {Conf::CONFIG_DEFAULT_MODERATORS, string_join(defaultModerators_)}, - {Conf::CONFIG_LOCAL_MODERATORS_ENABLED, localModeratorsEnabled_ ? TRUE_STR : FALSE_STR}, - {Conf::CONFIG_ALL_MODERATORS_ENABLED, allModeratorsEnabled_ ? TRUE_STR : FALSE_STR}}; + Manager::instance().saveConfig(); } std::map<std::string, std::string> @@ -408,40 +221,6 @@ Account::sortCodec() const std::shared_ptr<AccountCodecInfo>& b) { return a->order < b->order; }); } -std::vector<unsigned> -Account::convertIdToAVId(const std::vector<unsigned>& list) -{ -#if !(defined(TARGET_OS_IOS) && TARGET_OS_IOS) - constexpr size_t CODEC_NUM = 12; -#else - constexpr size_t CODEC_NUM = 10; -#endif - - static constexpr std::array<unsigned, CODEC_NUM> CODEC_ID_MAPPING - = { AV_CODEC_ID_NONE, - AV_CODEC_ID_H264, - AV_CODEC_ID_VP8, -#if !(defined(TARGET_OS_IOS) && TARGET_OS_IOS) - AV_CODEC_ID_MPEG4, - AV_CODEC_ID_H263, -#endif - AV_CODEC_ID_OPUS, - AV_CODEC_ID_ADPCM_G722, - AV_CODEC_ID_SPEEX | 0x20000000, - AV_CODEC_ID_SPEEX | 0x10000000, - AV_CODEC_ID_SPEEX, - AV_CODEC_ID_PCM_ALAW, - AV_CODEC_ID_PCM_MULAW }; - - std::vector<unsigned> av_list; - av_list.reserve(list.size()); - for (auto& item : list) { - if (item > 0 and item < CODEC_ID_MAPPING.size()) - av_list.emplace_back(CODEC_ID_MAPPING[item]); - } - return av_list; -} - std::string Account::mapStateNumberToString(RegistrationState state) { @@ -490,41 +269,6 @@ Account::getDefaultCodecDetails(const unsigned& codecId) return {}; } -#define find_iter() \ - const auto& iter = details.find(key); \ - if (iter == details.end()) { \ - JAMI_ERR("Couldn't find key \"%s\"", key); \ - return; \ - } - -void -Account::parseString(const std::map<std::string, std::string>& details, - const char* key, - std::string& s) -{ - find_iter(); - s = iter->second; -} - -void -Account::parsePath(const std::map<std::string, std::string>& details, - const char* key, - std::string& s, - const std::string& base) -{ - find_iter(); - s = fileutils::getCleanPath(base, iter->second); -} - -void -Account::parseBool(const std::map<std::string, std::string>& details, const char* key, bool& b) -{ - find_iter(); - b = iter->second == TRUE_STR; -} - -#undef find_iter - /** * Get the UPnP IP (external router) address. * If use UPnP is set to false, the address will be empty. @@ -670,36 +414,25 @@ Account::getActiveAccountCodecInfoList(MediaType mediaType) const const std::string& Account::getUserAgentName() { - if (hasCustomUserAgent_ and not customUserAgent_.empty()) - return customUserAgent_; - return DEFAULT_USER_AGENT; + return config_->customUserAgent.empty() ? DEFAULT_USER_AGENT : config_->customUserAgent; } std::string -Account::setDefaultUserAgent() +Account::getDefaultUserAgent() { - // Build the default user-agent string - std::string defaultUA; - defaultUA.append(PACKAGE_NAME); - defaultUA.append(" "); - defaultUA.append(libjami::version()); - defaultUA.append(" ("); - defaultUA.append(libjami::platform()); - defaultUA.append(")"); - - return defaultUA; + return fmt::format("{:s} {:s} ({:s})", PACKAGE_NAME, libjami::version(), libjami::platform()); } void Account::addDefaultModerator(const std::string& uri) { - defaultModerators_.insert(uri); + config_->defaultModerators.insert(uri); } void Account::removeDefaultModerator(const std::string& uri) { - defaultModerators_.erase(uri); + config_->defaultModerators.erase(uri); } bool diff --git a/src/account.h b/src/account.h index 0ce987713e48b17c47e64a30f09bb72e4efb4825..0de5e06c27c118caf189da39569ddb8e43effd80 100644 --- a/src/account.h +++ b/src/account.h @@ -19,9 +19,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ - -#ifndef ACCOUNT_H -#define ACCOUNT_H +#pragma once #ifdef HAVE_CONFIG_H #include "config.h" @@ -39,6 +37,7 @@ #include "logger.h" #include "compiler_intrinsics.h" // include the "UNUSED" macro #include "call_set.h" +#include "account_config.h" #include <functional> #include <string> @@ -52,11 +51,6 @@ #include <mutex> #include <chrono> -namespace YAML { -class Emitter; -class Node; -} // namespace YAML - namespace Json { class Value; } @@ -88,7 +82,7 @@ public: * It contains account, configuration, VoIP Link and Calls (inside the VoIPLink) */ -class Account : public Serializable, public std::enable_shared_from_this<Account> +class Account: public std::enable_shared_from_this<Account> { public: Account(const std::string& accountID); @@ -104,21 +98,49 @@ public: */ void hangupCalls(); - virtual void setAccountDetails(const std::map<std::string, std::string>& details); - - virtual std::map<std::string, std::string> getAccountDetails() const; + virtual std::unique_ptr<AccountConfig> buildConfig() const = 0; - virtual std::map<std::string, std::string> getVolatileAccountDetails() const; - - virtual std::string getFromUri() const = 0; + void setConfig(std::unique_ptr<AccountConfig>&& config) { + std::lock_guard<std::recursive_mutex> lock(configurationMutex_); + config_ = std::move(config); + loadConfig(); + } /** - * Load the settings for this account. + * Load the settings in this account. */ - virtual void loadConfig() = 0; + virtual void loadConfig(); + + const AccountConfig& config() const { + if (config_) return *config_; + else throw std::runtime_error("Account doesn't have a configuration"); + } + + inline void editConfig(std::function<void(AccountConfig& config)>&& edit) { + std::lock_guard<std::recursive_mutex> lock(configurationMutex_); + edit(*config_); + saveConfig(); + } - virtual void serialize(YAML::Emitter& out) const; - virtual void unserialize(const YAML::Node& node); + virtual void saveConfig() const; + + void setAccountDetails(const std::map<std::string, std::string>& details) { + std::lock_guard<std::recursive_mutex> lock(configurationMutex_); + if (not config_) + config_ = buildConfig(); + config_->fromMap(details); + loadConfig(); + saveConfig(); + } + + std::map<std::string, std::string> getAccountDetails() const { + std::lock_guard<std::recursive_mutex> lock(configurationMutex_); + return config().toMap(); + } + + virtual std::map<std::string, std::string> getVolatileAccountDetails() const; + + virtual std::string getFromUri() const = 0; /** * Get the account ID @@ -197,21 +219,25 @@ public: virtual void setPushNotificationToken(const std::string& pushDeviceToken = "") { - deviceKey_ = pushDeviceToken; + editConfig([&](AccountConfig& config){ + config.deviceKey = pushDeviceToken; + }); } virtual void setPushNotificationTopic(const std::string& topic = "") { - notificationTopic_ = topic; + editConfig([&](AccountConfig& config){ + config.notificationTopic = topic; + }); } /** * Tell if the account is enable or not. * @return true if enabled, false otherwise */ - bool isEnabled() const noexcept { return enabled_; } + bool isEnabled() const { return config().enabled; } - void setEnabled(bool enable) noexcept { enabled_ = enable; } + void setEnabled(bool enable) { config_->enabled = enable; } /** * Tell if the account is activated @@ -221,10 +247,14 @@ public: void setActive(bool active) noexcept { active_ = active; } - bool isUsable() const noexcept { return enabled_ and active_; } + bool isUsable() const { return config().enabled and active_; } - void enableVideo(bool enable) { videoEnabled_ = enable; } - bool isVideoEnabled() const noexcept { return videoEnabled_; } + void enableVideo(bool enable) { + editConfig([&](AccountConfig& config){ + config.videoEnabled = enable; + }); + } + bool isVideoEnabled() const { return config().videoEnabled; } /** * Set the registration state of the specified link @@ -234,15 +264,9 @@ public: unsigned detail_code = 0, const std::string& detail_str = {}); - /* They should be treated like macro definitions by the C++ compiler */ - const std::string& getUsername() const { return username_; } - - const std::string& getHostname() const { return hostname_; } - void setHostname(const std::string& hostname) { hostname_ = hostname; } - - const std::string& getAlias() const { return alias_; } - - void setAlias(const std::string& alias) { alias_ = alias; } + const std::string& getUsername() const { return config().username; } + const std::string& getHostname() const { return config().hostname; } + const std::string& getAlias() const { return config().alias; } static std::vector<unsigned> getDefaultCodecsId(); static std::map<std::string, std::string> getDefaultCodecDetails(const unsigned& codecId); @@ -264,29 +288,13 @@ public: std::shared_ptr<AccountCodecInfo> searchCodecByPayload(unsigned payload, MediaType mediaType); std::string getRingtonePath() const { return ringtonePath_; } + bool getRingtoneEnabled() const { return config().ringtoneEnabled; } + std::string getDisplayName() const { return config().displayName; } + std::string getMailBox() const { return config().mailbox; } - void setRingtonePath(const std::string& path) { ringtonePath_ = path; } - - bool getRingtoneEnabled() const { return ringtoneEnabled_; } - void setRingtoneEnabled(bool enable) { ringtoneEnabled_ = enable; } - - std::string getDisplayName() const { return displayName_; } - void setDisplayName(const std::string& name) { displayName_ = name; } - - std::string getMailBox() const { return mailBox_; } - - void setMailBox(const std::string& mb) { mailBox_ = mb; } - - bool isRendezVous() const { return isRendezVous_; } - - bool isAutoAnswerEnabled() const { return autoAnswerEnabled_; } - - bool isReadReceiptEnabled() const { return sendReadReceipt_; } - - static const char* const VIDEO_CODEC_ENABLED; - static const char* const VIDEO_CODEC_NAME; - static const char* const VIDEO_CODEC_PARAMETERS; - static const char* const VIDEO_CODEC_BITRATE; + bool isRendezVous() const { return config().isRendezVous; } + bool isAutoAnswerEnabled() const { return config().autoAnswerEnabled; } + bool isReadReceiptEnabled() const { return config().sendReadReceipt; } /** * returns whether or not UPnP is enabled and active @@ -326,33 +334,25 @@ public: void setCodecInactive(unsigned codecId); - std::vector<unsigned> convertIdToAVId(const std::vector<unsigned>& list); - /** * Get the user-agent */ const std::string& getUserAgentName(); - std::set<std::string> getDefaultModerators() const { return defaultModerators_; } + std::set<std::string> getDefaultModerators() const { return config().defaultModerators; } void addDefaultModerator(const std::string& peerURI); void removeDefaultModerator(const std::string& peerURI); - bool isLocalModeratorsEnabled() const { return localModeratorsEnabled_; } - void enableLocalModerators(bool isModEnabled) { localModeratorsEnabled_ = isModEnabled; } - bool isAllModerators() const { return allModeratorsEnabled_; } - void setAllModerators(bool isAllModeratorEnabled) - { - allModeratorsEnabled_ = isAllModeratorEnabled; - } + bool isLocalModeratorsEnabled() const { return config().localModeratorsEnabled; } + bool isAllModerators() const { return config().allModeratorsEnabled; } // Enable/disable ICE for media bool isIceForMediaEnabled() const { return iceForMediaEnabled_; } void enableIceForMedia(bool enable) { iceForMediaEnabled_ = enable; } // Enable/disable generation of empty offers - bool isEmptyOffersEnabled() const { return emptyOffersEnabled_; } - void enableEmptyOffers(bool enable) { emptyOffersEnabled_ = enable; } + bool isEmptyOffersEnabled() const { return false; } // Check if a Daemon version (typically peer's version) satisfies the // minimum required version. This check is typically used to disable a @@ -416,64 +416,10 @@ private: protected: void updateUpnpController(); - static void parseString(const std::map<std::string, std::string>& details, - const char* key, - std::string& s); - static void parseBool(const std::map<std::string, std::string>& details, - const char* key, - bool& b); - static void parsePath(const std::map<std::string, std::string>& details, - const char* key, - std::string& s, - const std::string& base); - - template<class T> - static inline void parseInt(const std::map<std::string, std::string>& details, - const char* key, - T& i) - { - const auto& iter = details.find(key); - if (iter == details.end()) { - JAMI_ERR("Couldn't find key \"%s\"", key); - return; - } - i = atoi(iter->second.c_str()); - } + std::unique_ptr<AccountConfig> config_ {}; friend class ConfigurationTest; - // General configuration keys for accounts - static const char* const ALL_CODECS_KEY; - static const char* const RINGTONE_PATH_KEY; - static const char* const RINGTONE_ENABLED_KEY; - static const char* const VIDEO_ENABLED_KEY; - static const char* const DISPLAY_NAME_KEY; - static const char* const ALIAS_KEY; - static const char* const TYPE_KEY; - static const char* const ID_KEY; - static const char* const USERNAME_KEY; - static const char* const AUTHENTICATION_USERNAME_KEY; - static const char* const PASSWORD_KEY; - static const char* const HOSTNAME_KEY; - static const char* const ACCOUNT_ENABLE_KEY; - static const char* const ACCOUNT_AUTOANSWER_KEY; - static const char* const ACCOUNT_READRECEIPT_KEY; - static const char* const ACCOUNT_ISRENDEZVOUS_KEY; - static const char* const ACCOUNT_ACTIVE_CALL_LIMIT_KEY; - static const char* const MAILBOX_KEY; - static const char* const USER_AGENT_KEY; - static const char* const HAS_CUSTOM_USER_AGENT_KEY; - static const char* const PRESENCE_MODULE_ENABLED_KEY; - static const char* const UPNP_ENABLED_KEY; - static const char* const PROXY_ENABLED_KEY; - static const char* const PROXY_SERVER_KEY; - static const char* const PROXY_PUSH_TOKEN_KEY; - static const char* const PROXY_PUSH_IOS_TOPIC_KEY; - static const char* const ACTIVE_CODEC_KEY; - static const char* const DEFAULT_MODERATORS_KEY; - static const char* const LOCAL_MODERATORS_ENABLED_KEY; - static const char* const ALL_MODERATORS_ENABLED_KEY; - static const std::string DEFAULT_USER_AGENT; static std::string mapStateNumberToString(RegistrationState state); @@ -481,7 +427,7 @@ protected: /** * Build the user-agent string */ - static std::string setDefaultUserAgent(); + static std::string getDefaultUserAgent(); /** * Account ID are assign in constructor and shall not changed @@ -490,28 +436,6 @@ protected: mutable std::recursive_mutex configurationMutex_ {}; - /** - * Account login information: username - */ - std::string username_; - - /** - * Account login information: hostname - */ - std::string hostname_; - - /** - * Account login information: Alias - */ - std::string alias_; - - /** - * Tells if the account is enabled. - * This implies the link will be initialized on startup. - * Modified by the configuration (key: ENABLED) - */ - bool enabled_; - /** * Tells if the account is active now. * This allows doRegister to be called. @@ -519,24 +443,6 @@ protected: */ bool active_ {true}; - /* If true, automatically answer calls to this account */ - bool autoAnswerEnabled_; - - // If true, send Displayed status (and emit to the client) - bool sendReadReceipt_; - - /* If true mix calls into a conference */ - bool isRendezVous_; - - /** - * The number of concurrent calls for the account - * -1: Unlimited - * 0: Do not disturb - * 1: Single call - * +: Multi line - */ - int activeCallLimit_ {-1}; - /* * The general, protocol neutral registration * state of the account @@ -557,66 +463,15 @@ protected: */ std::string ringtonePath_; - /** - * Allows user to temporarily disable video calling - */ - - bool videoEnabled_ = true; - - /** - * Play ringtone when receiving a call - */ - bool ringtoneEnabled_; - - /** - * Display name when calling - */ - std::string displayName_; - - /** - * User-agent used for registration - */ - std::string customUserAgent_; - - // true if user has overridden default - bool hasCustomUserAgent_; - - /** - * Account mail box - */ - std::string mailBox_; - /** * UPnP IGD controller and the mutex to access it */ - bool upnpEnabled_; mutable std::mutex upnp_mtx {}; std::shared_ptr<jami::upnp::Controller> upnpCtrl_; - std::set<std::string> defaultModerators_ {}; - bool localModeratorsEnabled_; - bool allModeratorsEnabled_; - - bool multiStreamEnabled_ {false}; bool iceForMediaEnabled_ {true}; bool iceCompIdRfc5245Compliant_ {false}; - /** - * Allow generating empty offers. Mainly to validate proper - * handling of incoming empty offers. - */ - bool emptyOffersEnabled_ {false}; - - /** - * Device push notification token. - */ - std::string deviceKey_ {}; - - /** - * Push notification topic. - */ - std::string notificationTopic_ {}; - /** * private account codec searching functions */ @@ -636,4 +491,3 @@ operator<<(std::ostream& os, const Account& acc) } // namespace jami -#endif diff --git a/src/account_config.cpp b/src/account_config.cpp index 5fee1015d4665f7024311e8aef3d7f32ec2d730a..ef0144bb80ceab0f93f7dcc5eed9fbb24d81ee33 100644 --- a/src/account_config.cpp +++ b/src/account_config.cpp @@ -17,7 +17,7 @@ #include "account_config.h" #include "account_const.h" #include "account_schema.h" -#include "yamlparser.h" +#include "config/yamlparser.h" #include "string_utils.h" #include "fileutils.h" @@ -171,7 +171,7 @@ parsePath(const std::map<std::string, std::string>& details, { auto it = details.find(key); if (it != details.end()) - s = fileutils::getCleanPath(base, it->second); + s = fileutils::getFullPath(base, it->second); } } diff --git a/src/account_factory.cpp b/src/account_factory.cpp index 8093512ce27e787fcce0e32c3cd02a9c17d29ae8..2a298cb24ec9a9662f0566e7af6a09c70f55b391 100644 --- a/src/account_factory.cpp +++ b/src/account_factory.cpp @@ -35,14 +35,12 @@ const char* const AccountFactory::DEFAULT_ACCOUNT_TYPE = SIPAccount::ACCOUNT_TYP AccountFactory::AccountFactory() { - auto sipfunc = [](const std::string& id) { + generators_.emplace(SIPAccount::ACCOUNT_TYPE, [](const std::string& id) { return std::make_shared<SIPAccount>(id, true); - }; - generators_.emplace(SIPAccount::ACCOUNT_TYPE, sipfunc); - auto dhtfunc = [](const std::string& id) { - return std::make_shared<JamiAccount>(id, false); - }; - generators_.emplace(JamiAccount::ACCOUNT_TYPE, dhtfunc); + }); + generators_.emplace(JamiAccount::ACCOUNT_TYPE, [](const std::string& id) { + return std::make_shared<JamiAccount>(id); + }); } std::shared_ptr<Account> @@ -66,7 +64,7 @@ AccountFactory::createAccount(const char* const accountType, const std::string& } bool -AccountFactory::isSupportedType(const char* const name) const +AccountFactory::isSupportedType(std::string_view name) const { return generators_.find(name) != generators_.cend(); } @@ -74,14 +72,15 @@ AccountFactory::isSupportedType(const char* const name) const void AccountFactory::removeAccount(Account& account) { - const auto* account_type = account.getAccountType(); - + std::string_view account_type = account.getAccountType(); std::lock_guard<std::recursive_mutex> lock(mutex_); const auto& id = account.getAccountID(); JAMI_DEBUG("Removing account {:s}", id); - auto& map = accountMaps_.at(account_type); - map.erase(id); - JAMI_DEBUG("Remaining {:d} {:s} account(s)", map.size(), account_type); + auto m = accountMaps_.find(account_type); + if (m != accountMaps_.end()) { + m->second.erase(id); + JAMI_DEBUG("Remaining {:d} {:s} account(s)", m->second.size(), account_type); + } } void diff --git a/src/account_factory.h b/src/account_factory.h index 540a7614f96c63b1735f73ff5ac1192001b13628..ebd5a1b5dfd177a1a414ba5044af169aef49bc77 100644 --- a/src/account_factory.h +++ b/src/account_factory.h @@ -44,7 +44,7 @@ public: AccountFactory(); - bool isSupportedType(const char* const accountType) const; + bool isSupportedType(std::string_view accountType) const; std::shared_ptr<Account> createAccount(const char* const accountType, const std::string& id); diff --git a/src/client/configurationmanager.cpp b/src/client/configurationmanager.cpp index 18aa5202f251a9d76b505fc5700a34e16420348d..d7bfaf16f89ce9c8ddab73262cecc43421ba7aee 100644 --- a/src/client/configurationmanager.cpp +++ b/src/client/configurationmanager.cpp @@ -37,6 +37,8 @@ #include "connectivity/ip_utils.h" #include "sip/sipaccount.h" #include "jamidht/jamiaccount.h" +#include "sip/sipaccount_config.h" +#include "jamidht/jamiaccount_config.h" #include "audio/audiolayer.h" #include "system_codec_container.h" #include "account_const.h" @@ -347,10 +349,7 @@ changeAccountPassword(const std::string& accountID, const std::string& password_new) { if (auto acc = jami::Manager::instance().getAccount<jami::JamiAccount>(accountID)) - if (acc->changeArchivePassword(password_old, password_new)) { - jami::Manager::instance().saveConfig(acc); - return true; - } + return acc->changeArchivePassword(password_old, password_new); return false; } @@ -425,9 +424,9 @@ std::map<std::string, std::string> getAccountTemplate(const std::string& accountType) { if (accountType == Account::ProtocolNames::RING) - return jami::JamiAccount("dummy", false).getAccountDetails(); + return jami::JamiAccountConfig().toMap(); else if (accountType == Account::ProtocolNames::SIP) - return jami::SIPAccount("dummy", false).getAccountDetails(); + return jami::SipAccountConfig().toMap(); return {}; } @@ -923,10 +922,14 @@ setCredentials(const std::string& accountID, { if (auto sipaccount = jami::Manager::instance().getAccount<SIPAccount>(accountID)) { sipaccount->doUnregister([&](bool /* transport_free */) { - sipaccount->setCredentials(details); + sipaccount->editConfig([&](jami::SipAccountConfig& config){ + config.setCredentials(details); + }); + sipaccount->loadConfig(); if (sipaccount->isEnabled()) sipaccount->doRegister(); }); + jami::Manager::instance().saveConfig(sipaccount); } } diff --git a/src/config/yamlparser.cpp b/src/config/yamlparser.cpp index 8b8a398e7547c769cc587395cf9832469b4a6c60..94baf956808a327a2ad2aa9825225b0036601f50 100644 --- a/src/config/yamlparser.cpp +++ b/src/config/yamlparser.cpp @@ -30,21 +30,28 @@ parsePath(const YAML::Node& node, const char* key, std::string& path, const std: { std::string val; parseValue(node, key, val); - path = fileutils::getCleanPath(base, val); + path = fileutils::getFullPath(base, val); +} + +void +parsePathOptional(const YAML::Node& node, const char* key, std::string& path, const std::string& base) +{ + std::string val; + if (parseValueOptional(node, key, val)) + path = fileutils::getFullPath(base, val); } -// FIXME: Maybe think of something more clever, this is due to yaml-cpp's poor -// handling of empty values for nested collections. std::vector<std::map<std::string, std::string>> parseVectorMap(const YAML::Node& node, const std::initializer_list<std::string>& keys) { std::vector<std::map<std::string, std::string>> result; + result.reserve(node.size()); for (const auto& n : node) { std::map<std::string, std::string> t; for (const auto& k : keys) { t[k] = n[k].as<std::string>(""); } - result.push_back(t); + result.emplace_back(std::move(t)); } return result; } @@ -54,9 +61,7 @@ parseVector(const YAML::Node& node) { std::set<std::string> result; for (const auto& n : node) { - std::string t; - t = n.as<std::string>(""); - result.emplace(t); + result.emplace(n.as<std::string>("")); } return result; } diff --git a/src/config/yamlparser.h b/src/config/yamlparser.h index e63a7e6b02852eb28758e9d1dbc1f50b3e2af88a..adc53020908a90d4e9997623e42c6a823510073a 100644 --- a/src/config/yamlparser.h +++ b/src/config/yamlparser.h @@ -50,6 +50,7 @@ parseValueOptional(const YAML::Node& node, const char* key, T& value) } void parsePath(const YAML::Node& node, const char* key, std::string& path, const std::string& base); +void parsePathOptional(const YAML::Node& node, const char* key, std::string& path, const std::string& base); std::vector<std::map<std::string, std::string>> parseVectorMap( const YAML::Node& node, const std::initializer_list<std::string>& keys); diff --git a/src/connectivity/sip_utils.h b/src/connectivity/sip_utils.h index 17891ba51e06903c3594326c49018ddd040c1bd6..b633c5332c63a25d47a10ed76aa9870920b763c1 100644 --- a/src/connectivity/sip_utils.h +++ b/src/connectivity/sip_utils.h @@ -39,16 +39,18 @@ namespace jami { namespace sip_utils { +using namespace std::literals; + // SIP methods. Only list methods that need to be explicitly // handled namespace SIP_METHODS { -constexpr std::string_view MESSAGE = "MESSAGE"; -constexpr std::string_view INFO = "INFO"; -constexpr std::string_view OPTIONS = "OPTIONS"; -constexpr std::string_view PUBLISH = "PUBLISH"; -constexpr std::string_view REFER = "REFER"; -constexpr std::string_view NOTIFY = "NOTIFY"; +constexpr std::string_view MESSAGE = "MESSAGE"sv; +constexpr std::string_view INFO = "INFO"sv; +constexpr std::string_view OPTIONS = "OPTIONS"sv; +constexpr std::string_view PUBLISH = "PUBLISH"sv; +constexpr std::string_view REFER = "REFER"sv; +constexpr std::string_view NOTIFY = "NOTIFY"sv; } // namespace SIP_METHODS static constexpr int DEFAULT_SIP_PORT {5060}; @@ -87,9 +89,9 @@ getKeyExchangeName(KeyExchangeProtocol kx) } static inline KeyExchangeProtocol -getKeyExchangeProtocol(const char* name) +getKeyExchangeProtocol(std::string_view name) { - return !std::strcmp("sdes", name) ? KeyExchangeProtocol::SDES : KeyExchangeProtocol::NONE; + return name == "sdes"sv ? KeyExchangeProtocol::SDES : KeyExchangeProtocol::NONE; } /** diff --git a/src/jamidht/CMakeLists.txt b/src/jamidht/CMakeLists.txt index 1c08d51cf3efdeb04b5097380946d5d609b1b601..a929bb867ed8700315afd290b2a709d2602cfcbe 100644 --- a/src/jamidht/CMakeLists.txt +++ b/src/jamidht/CMakeLists.txt @@ -28,6 +28,8 @@ list (APPEND Source_Files__jamidht "${CMAKE_CURRENT_SOURCE_DIR}/jami_contact.h" "${CMAKE_CURRENT_SOURCE_DIR}/jamiaccount.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/jamiaccount.h" + "${CMAKE_CURRENT_SOURCE_DIR}/jamiaccount_config.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/jamiaccount_config.h" "${CMAKE_CURRENT_SOURCE_DIR}/channel_handler.h" "${CMAKE_CURRENT_SOURCE_DIR}/conversation_channel_handler.h" "${CMAKE_CURRENT_SOURCE_DIR}/conversation_channel_handler.cpp" diff --git a/src/jamidht/Makefile.am b/src/jamidht/Makefile.am index bd64a47812f6960c4a801800a335f2d1cacefb96..3a626939cdc7ae8204b3a8b6db05f3397a326424 100644 --- a/src/jamidht/Makefile.am +++ b/src/jamidht/Makefile.am @@ -8,6 +8,8 @@ libjamiacc_la_SOURCES = \ ./jamidht/abstract_sip_transport.h \ ./jamidht/jamiaccount.cpp \ ./jamidht/jamiaccount.h \ + ./jamidht/jamiaccount_config.cpp \ + ./jamidht/jamiaccount_config.h \ ./jamidht/channeled_transport.h \ ./jamidht/channeled_transport.cpp \ ./jamidht/channeled_transfers.h \ diff --git a/src/jamidht/jamiaccount.cpp b/src/jamidht/jamiaccount.cpp index d7da1f3325620f747d177ec8e7625b6106854382..f617bd91266cf7e1fd8252d4ac62fefc4b3505e4 100644 --- a/src/jamidht/jamiaccount.cpp +++ b/src/jamidht/jamiaccount.cpp @@ -1,16 +1,10 @@ /* * Copyright (C) 2004-2022 Savoir-faire Linux Inc. * - * Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com> - * Author: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com> - * Author: Simon Désaulniers <simon.desaulniers@savoirfairelinux.com> - * Author: Nicolas Jäger <nicolas.jager@savoirfairelinux.com> - * Author: Mingrui Zhang <mingrui.zhang@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. + * the Free Software Foundation; either version 3, 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 @@ -18,8 +12,7 @@ * 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, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * along with this program; if not, see <https://www.gnu.org/licenses/>. */ #ifdef HAVE_CONFIG_H @@ -76,7 +69,6 @@ #include "data_transfer.h" #include "conversation.h" -#include "config/yamlparser.h" #include "connectivity/security/certstore.h" #include "libdevcrypto/Common.h" #include "base64.h" @@ -226,18 +218,11 @@ struct JamiAccount::DiscoveredPeer static constexpr const char* const RING_URI_PREFIX = "ring:"; static constexpr const char* const JAMI_URI_PREFIX = "jami:"; -static constexpr const char* DEFAULT_TURN_SERVER = "turn.jami.net"; -static constexpr const char* DEFAULT_TURN_USERNAME = "ring"; -static constexpr const char* DEFAULT_TURN_PWD = "ring"; -static constexpr const char* DEFAULT_TURN_REALM = "ring"; static const auto PROXY_REGEX = std::regex( "(https?://)?([\\w\\.\\-_\\~]+)(:(\\d+)|:\\[(.+)-(.+)\\])?"); static const std::string PEER_DISCOVERY_JAMI_SERVICE = "jami"; const constexpr auto PEER_DISCOVERY_EXPIRATION = std::chrono::minutes(1); -constexpr const char* const JamiAccount::ACCOUNT_TYPE; -constexpr const std::pair<uint16_t, uint16_t> JamiAccount::DHT_PORT_RANGE {4000, 8888}; - using ValueIdDist = std::uniform_int_distribution<dht::Value::Id>; static std::string_view @@ -297,26 +282,16 @@ JamiAccount::createIceTransport(const Args&... args) return ice; } -JamiAccount::JamiAccount(const std::string& accountID, bool /* presenceEnabled */) - : SIPAccountBase(accountID) +JamiAccount::JamiAccount(const std::string& accountId) + : SIPAccountBase(accountId) , dht_(new dht::DhtRunner) - , idPath_(fileutils::get_data_dir() + DIR_SEPARATOR_STR + getAccountID()) - , cachePath_(fileutils::get_cache_dir() + DIR_SEPARATOR_STR + getAccountID()) + , idPath_(fileutils::get_data_dir() + DIR_SEPARATOR_STR + accountId) + , cachePath_(fileutils::get_cache_dir() + DIR_SEPARATOR_STR + accountId) , dataPath_(cachePath_ + DIR_SEPARATOR_STR "values") , dhtPeerConnector_ {} , connectionManager_ {} + , nonSwarmTransferManager_(std::make_shared<TransferManager>(accountId, "")) { - // Force the SFL turn server if none provided yet - turnServer_ = DEFAULT_TURN_SERVER; - turnServerUserName_ = DEFAULT_TURN_USERNAME; - turnServerPwd_ = DEFAULT_TURN_PWD; - turnServerRealm_ = DEFAULT_TURN_REALM; - turnEnabled_ = true; - - proxyListUrl_ = DHT_DEFAULT_PROXY_LIST_URL; - proxyServer_ = DHT_DEFAULT_PROXY; - nonSwarmTransferManager_ = std::make_shared<TransferManager>(getAccountID(), ""); - try { std::istringstream is(fileutils::loadCacheTextFile(cachePath_ + DIR_SEPARATOR_STR "dhtproxy", std::chrono::hours(24 * 7))); @@ -326,16 +301,10 @@ JamiAccount::JamiAccount(const std::string& accountID, bool /* presenceEnabled * getAccountID().c_str(), e.what()); } - - setActiveCodecs({}); } JamiAccount::~JamiAccount() noexcept { - if (peerDiscovery_) { - peerDiscovery_->stopPublish(PEER_DISCOVERY_JAMI_SERVICE); - peerDiscovery_->stopDiscovery(PEER_DISCOVERY_JAMI_SERVICE); - } if (auto dht = dht_) dht->join(); } @@ -462,7 +431,7 @@ JamiAccount::newOutgoingCallHelper(const std::shared_ptr<SIPCall>& call, std::st } catch (...) { #if HAVE_RINGNS NameDirectory::lookupUri(suffix, - nameServer_, + config().nameServer, [wthis_ = weak(), call](const std::string& result, NameDirectory::Response response) { // we may run inside an unknown thread, but following code must @@ -813,149 +782,23 @@ JamiAccount::saveConfig() const { try { YAML::Emitter accountOut; - serialize(accountOut); - auto accountConfig = getPath() + DIR_SEPARATOR_STR + "config.yml"; - + config().serialize(accountOut); + auto accountConfig = config().path + DIR_SEPARATOR_STR + "config.yml"; std::lock_guard<std::mutex> lock(fileutils::getFileLock(accountConfig)); std::ofstream fout = fileutils::ofstream(accountConfig); - fout << accountOut.c_str(); - JAMI_DBG("Exported account to %s", accountConfig.c_str()); + fout.write(accountOut.c_str(), accountOut.size()); + JAMI_DBG("Saved account config to %s", accountConfig.c_str()); } catch (const std::exception& e) { - JAMI_ERR("Error exporting account: %s", e.what()); + JAMI_ERR("Error saving account config: %s", e.what()); } } void -JamiAccount::serialize(YAML::Emitter& out) const +JamiAccount::loadConfig() { - std::lock_guard<std::recursive_mutex> lock(configurationMutex_); - - if (registrationState_ == RegistrationState::INITIALIZING) - return; - - out << YAML::BeginMap; - SIPAccountBase::serialize(out); - out << YAML::Key << Conf::DHT_PORT_KEY << YAML::Value << dhtDefaultPort_; - out << YAML::Key << Conf::DHT_PUBLIC_IN_CALLS << YAML::Value << dhtPublicInCalls_; - out << YAML::Key << Conf::DHT_ALLOW_PEERS_FROM_HISTORY << YAML::Value << allowPeersFromHistory_; - out << YAML::Key << Conf::DHT_ALLOW_PEERS_FROM_CONTACT << YAML::Value << allowPeersFromContact_; - out << YAML::Key << Conf::DHT_ALLOW_PEERS_FROM_TRUSTED << YAML::Value << allowPeersFromTrusted_; - out << YAML::Key << libjami::Account::ConfProperties::DHT_PEER_DISCOVERY << YAML::Value - << dhtPeerDiscovery_; - out << YAML::Key << libjami::Account::ConfProperties::ACCOUNT_PEER_DISCOVERY << YAML::Value - << accountPeerDiscovery_; - out << YAML::Key << libjami::Account::ConfProperties::ACCOUNT_PUBLISH << YAML::Value - << accountPublish_; - - out << YAML::Key << Conf::PROXY_ENABLED_KEY << YAML::Value << proxyEnabled_; - out << YAML::Key << Conf::PROXY_SERVER_KEY << YAML::Value << proxyServer_; - out << YAML::Key << libjami::Account::ConfProperties::DHT_PROXY_LIST_URL << YAML::Value - << proxyListUrl_; - -#if HAVE_RINGNS - out << YAML::Key << libjami::Account::ConfProperties::RingNS::URI << YAML::Value << nameServer_; - if (not registeredName_.empty()) - out << YAML::Key << libjami::Account::VolatileProperties::REGISTERED_NAME << YAML::Value - << registeredName_; -#endif - - out << YAML::Key << libjami::Account::ConfProperties::ARCHIVE_PATH << YAML::Value << archivePath_; - out << YAML::Key << libjami::Account::ConfProperties::ARCHIVE_HAS_PASSWORD << YAML::Value - << archiveHasPassword_; - out << YAML::Key << Conf::RING_ACCOUNT_RECEIPT << YAML::Value << receipt_; - if (receiptSignature_.size() > 0) - out << YAML::Key << Conf::RING_ACCOUNT_RECEIPT_SIG << YAML::Value - << YAML::Binary(receiptSignature_.data(), receiptSignature_.size()); - out << YAML::Key << libjami::Account::ConfProperties::DEVICE_NAME << YAML::Value << deviceName_; - out << YAML::Key << libjami::Account::ConfProperties::MANAGER_URI << YAML::Value << managerUri_; - out << YAML::Key << libjami::Account::ConfProperties::MANAGER_USERNAME << YAML::Value - << managerUsername_; - - // tls submap - out << YAML::Key << Conf::TLS_KEY << YAML::Value << YAML::BeginMap; - SIPAccountBase::serializeTls(out); - out << YAML::EndMap; - - out << YAML::EndMap; -} - -void -JamiAccount::unserialize(const YAML::Node& node) -{ - std::lock_guard<std::recursive_mutex> lock(configurationMutex_); - - using yaml_utils::parseValue; - using yaml_utils::parseValueOptional; - using yaml_utils::parsePath; - - SIPAccountBase::unserialize(node); - - // get tls submap - const auto& tlsMap = node[Conf::TLS_KEY]; - parsePath(tlsMap, Conf::CERTIFICATE_KEY, tlsCertificateFile_, idPath_); - parsePath(tlsMap, Conf::CALIST_KEY, tlsCaListFile_, idPath_); - parseValue(tlsMap, Conf::TLS_PASSWORD_KEY, tlsPassword_); - parsePath(tlsMap, Conf::PRIVATE_KEY_KEY, tlsPrivateKeyFile_, idPath_); - - parseValue(node, Conf::DHT_ALLOW_PEERS_FROM_HISTORY, allowPeersFromHistory_); - parseValue(node, Conf::DHT_ALLOW_PEERS_FROM_CONTACT, allowPeersFromContact_); - parseValue(node, Conf::DHT_ALLOW_PEERS_FROM_TRUSTED, allowPeersFromTrusted_); - - parseValue(node, Conf::PROXY_ENABLED_KEY, proxyEnabled_); - parseValue(node, Conf::PROXY_SERVER_KEY, proxyServer_); - try { - parseValue(node, libjami::Account::ConfProperties::DHT_PROXY_LIST_URL, proxyListUrl_); - } catch (const std::exception& e) { - proxyListUrl_ = DHT_DEFAULT_PROXY_LIST_URL; - } - - parseValueOptional(node, libjami::Account::ConfProperties::DEVICE_NAME, deviceName_); - parseValueOptional(node, libjami::Account::ConfProperties::MANAGER_URI, managerUri_); - parseValueOptional(node, libjami::Account::ConfProperties::MANAGER_USERNAME, managerUsername_); - - try { - parsePath(node, libjami::Account::ConfProperties::ARCHIVE_PATH, archivePath_, idPath_); - parseValue(node, libjami::Account::ConfProperties::ARCHIVE_HAS_PASSWORD, archiveHasPassword_); - } catch (const std::exception& e) { - JAMI_WARN("can't read archive path: %s", e.what()); - archiveHasPassword_ = true; - } - - try { - parseValue(node, Conf::RING_ACCOUNT_RECEIPT, receipt_); - auto receipt_sig = node[Conf::RING_ACCOUNT_RECEIPT_SIG].as<YAML::Binary>(); - receiptSignature_ = {receipt_sig.data(), receipt_sig.data() + receipt_sig.size()}; - } catch (const std::exception& e) { - JAMI_WARN("can't read receipt: %s", e.what()); - } - - // HACK - // MacOS doesn't seems to close the DHT port sometimes, so re-using the DHT port seems - // to make the DHT unusable (Address already in use, and SO_REUSEADDR & SO_REUSEPORT - // doesn't seems to work). For now, use a random port - // See https://git.jami.net/savoirfairelinux/ring-client-macosx/issues/221 - // TODO: parseValueOptional(node, Conf::DHT_PORT_KEY, dhtDefaultPort_); - if (not dhtDefaultPort_) - dhtDefaultPort_ = getRandomEvenPort(DHT_PORT_RANGE); - - parseValueOptional(node, libjami::Account::ConfProperties::DHT_PEER_DISCOVERY, dhtPeerDiscovery_); - parseValueOptional(node, - libjami::Account::ConfProperties::ACCOUNT_PEER_DISCOVERY, - accountPeerDiscovery_); - parseValueOptional(node, libjami::Account::ConfProperties::ACCOUNT_PUBLISH, accountPublish_); - -#if HAVE_RINGNS - parseValueOptional(node, libjami::Account::ConfProperties::RingNS::URI, nameServer_); - if (registeredName_.empty()) { - parseValueOptional(node, - libjami::Account::VolatileProperties::REGISTERED_NAME, - registeredName_); - } -#endif - - parseValue(node, Conf::DHT_PUBLIC_IN_CALLS, dhtPublicInCalls_); - - loadAccount(); + SIPAccountBase::loadConfig(); + registeredName_ = config().registeredName; + loadAccount(config().archive_password, config().archive_pin, config().archive_path); } bool @@ -966,13 +809,17 @@ JamiAccount::changeArchivePassword(const std::string& password_old, const std::s JAMI_ERR("[Account %s] Can't change archive password", getAccountID().c_str()); return false; } - archiveHasPassword_ = not password_new.empty(); + editConfig([&](JamiAccountConfig& config){ + config.archiveHasPassword = not password_new.empty(); + }); } catch (const std::exception& ex) { JAMI_ERR("[Account %s] Can't change archive password: %s", getAccountID().c_str(), ex.what()); if (password_old.empty()) { - archiveHasPassword_ = true; + editConfig([&](JamiAccountConfig& config){ + config.archiveHasPassword = true; + }); emitSignal<libjami::ConfigurationSignal::AccountDetailsChanged>(getAccountID(), getAccountDetails()); } @@ -1037,8 +884,10 @@ JamiAccount::setValidity(const std::string& pwd, const dht::InfoHash& id, int64_ void JamiAccount::forceReloadAccount() { - receipt_.clear(); - receiptSignature_.clear(); + editConfig([&](JamiAccountConfig& conf){ + conf.receipt.clear(); + conf.receiptSignature.clear(); + }); loadAccount(); } @@ -1200,42 +1049,44 @@ JamiAccount::loadAccount(const std::string& archive_password, } }}; + const auto& conf = config(); try { auto onAsync = [w = weak()](AccountManager::AsyncUser&& cb) { if (auto this_ = w.lock()) cb(*this_->accountManager_); }; - if (managerUri_.empty()) { + if (conf.managerUri.empty()) { accountManager_.reset(new ArchiveAccountManager( getPath(), onAsync, [this]() { return getAccountDetails(); }, - archivePath_.empty() ? "archive.gz" : archivePath_, - nameServer_)); + conf.archivePath.empty() ? "archive.gz" : conf.archivePath, + conf.nameServer)); } else { accountManager_.reset( - new ServerAccountManager(getPath(), onAsync, managerUri_, nameServer_)); + new ServerAccountManager(getPath(), onAsync, conf.managerUri, conf.nameServer)); } - auto id = accountManager_->loadIdentity(tlsCertificateFile_, - tlsPrivateKeyFile_, - tlsPassword_); + auto id = accountManager_->loadIdentity(conf.tlsCertificateFile, + conf.tlsPrivateKeyFile, + conf.tlsPassword); if (auto info = accountManager_->useIdentity(id, - receipt_, - receiptSignature_, - managerUsername_, + conf.receipt, + conf.receiptSignature, + conf.managerUsername, callbacks)) { // normal loading path id_ = std::move(id); - username_ = info->accountId; + config_->username = info->accountId; JAMI_WARN("[Account %s] loaded account identity", getAccountID().c_str()); if (not isEnabled()) { setRegistrationState(RegistrationState::UNREGISTERED); } convModule()->loadConversations(); } else if (isEnabled()) { - if (not managerUri_.empty() and archive_password.empty()) { + JAMI_WARNING("[Account {}] useIdentity failed!", getAccountID()); + if (not conf.managerUri.empty() and archive_password.empty()) { Migration::setState(accountID_, Migration::State::INVALID); setRegistrationState(RegistrationState::ERROR_NEED_MIGRATION); return; @@ -1250,12 +1101,9 @@ JamiAccount::loadAccount(const std::string& archive_password, }); std::unique_ptr<AccountManager::AccountCredentials> creds; - if (managerUri_.empty()) { + if (conf.managerUri.empty()) { auto acreds = std::make_unique<ArchiveAccountManager::ArchiveAccountCredentials>(); - if (archivePath_.empty()) { - archivePath_ = "archive.gz"; - } - auto archivePath = fileutils::getFullPath(idPath_, archivePath_); + auto archivePath = fileutils::getFullPath(idPath_, conf.archivePath); bool hasArchive = fileutils::isFile(archivePath); if (not archive_path.empty()) { @@ -1278,17 +1126,17 @@ JamiAccount::loadAccount(const std::string& archive_password, creds = std::move(acreds); } else { auto screds = std::make_unique<ServerAccountManager::ServerAccountCredentials>(); - screds->username = managerUsername_; + screds->username = conf.managerUsername; creds = std::move(screds); } creds->password = archive_password; - archiveHasPassword_ = !archive_password.empty(); + bool hasPassword = !archive_password.empty(); accountManager_->initAuthentication( fDeviceKey, ip_utils::getDeviceName(), std::move(creds), - [this, migrating](const AccountInfo& info, + [this, migrating, hasPassword](const AccountInfo& info, const std::map<std::string, std::string>& config, std::string&& receipt, std::vector<uint8_t>&& receipt_signature) { @@ -1296,45 +1144,41 @@ JamiAccount::loadAccount(const std::string& archive_password, fileutils::check_dir(idPath_.c_str(), 0700); - // save the chain including CA auto id = info.identity; - std::tie(tlsPrivateKeyFile_, tlsCertificateFile_) = saveIdentity(id, - idPath_, - DEVICE_ID_PATH); - id_ = std::move(id); - tlsPassword_ = {}; - - username_ = info.accountId; - registeredName_ = managerUsername_; - deviceName_ = accountManager_->getAccountDeviceName(); - - auto nameServerIt = config.find(libjami::Account::ConfProperties::RingNS::URI); - if (nameServerIt != config.end() && !nameServerIt->second.empty()) { - nameServer_ = nameServerIt->second; - } - auto displayNameIt = config.find(libjami::Account::ConfProperties::DISPLAYNAME); - if (displayNameIt != config.end() && !displayNameIt->second.empty()) { - displayName_ = displayNameIt->second; - } + editConfig([&](JamiAccountConfig& conf){ + std::tie(conf.tlsPrivateKeyFile, conf.tlsCertificateFile) = saveIdentity(id, + idPath_, + DEVICE_ID_PATH); + conf.tlsPassword = {}; + conf.archiveHasPassword = hasPassword; + if (not conf.managerUri.empty()) { + conf.registeredName = conf.managerUsername; + registeredName_ = conf.managerUsername; + } + conf.username = info.accountId; + conf.deviceName = accountManager_->getAccountDeviceName(); - receipt_ = std::move(receipt); - receiptSignature_ = std::move(receipt_signature); + auto nameServerIt = config.find(libjami::Account::ConfProperties::RingNS::URI); + if (nameServerIt != config.end() && !nameServerIt->second.empty()) { + conf.nameServer = nameServerIt->second; + } + auto displayNameIt = config.find(libjami::Account::ConfProperties::DISPLAYNAME); + if (displayNameIt != config.end() && !displayNameIt->second.empty()) { + conf.displayName = displayNameIt->second; + } + conf.receipt = std::move(receipt); + conf.receiptSignature = std::move(receipt_signature); + conf.fromMap(config); + }); + id_ = std::move(id); if (migrating) { Migration::setState(getAccountID(), Migration::State::SUCCESS); } - - // Use the provided config by JAMS instead of default one - auto details = getAccountDetails(); - for (const auto& [key, value] : config) - details[key] = value; - setAccountDetails(details); - - if (not info.photo.empty() or not displayName_.empty()) + if (not info.photo.empty() or not config_->displayName.empty()) emitSignal<libjami::ConfigurationSignal::AccountProfileReceived>(getAccountID(), - displayName_, + config_->displayName, info.photo); setRegistrationState(RegistrationState::UNREGISTERED); - saveConfig(); convModule()->loadConversations(); doRegister(); }, @@ -1371,149 +1215,6 @@ JamiAccount::loadAccount(const std::string& archive_password, } } -void -JamiAccount::setAccountDetails(const std::map<std::string, std::string>& details) -{ - std::lock_guard<std::recursive_mutex> lock(configurationMutex_); - SIPAccountBase::setAccountDetails(details); - - // TLS - parsePath(details, Conf::CONFIG_TLS_CA_LIST_FILE, tlsCaListFile_, idPath_); - parsePath(details, Conf::CONFIG_TLS_CERTIFICATE_FILE, tlsCertificateFile_, idPath_); - parsePath(details, Conf::CONFIG_TLS_PRIVATE_KEY_FILE, tlsPrivateKeyFile_, idPath_); - parseString(details, Conf::CONFIG_TLS_PASSWORD, tlsPassword_); - - if (hostname_.empty()) - hostname_ = DHT_DEFAULT_BOOTSTRAP; - parseString(details, libjami::Account::ConfProperties::BOOTSTRAP_LIST_URL, bootstrapListUrl_); - parseInt(details, Conf::CONFIG_DHT_PORT, dhtDefaultPort_); - parseBool(details, Conf::CONFIG_DHT_PUBLIC_IN_CALLS, dhtPublicInCalls_); - parseBool(details, libjami::Account::ConfProperties::DHT_PEER_DISCOVERY, dhtPeerDiscovery_); - parseBool(details, - libjami::Account::ConfProperties::ACCOUNT_PEER_DISCOVERY, - accountPeerDiscovery_); - parseBool(details, libjami::Account::ConfProperties::ACCOUNT_PUBLISH, accountPublish_); - parseBool(details, - libjami::Account::ConfProperties::ALLOW_CERT_FROM_HISTORY, - allowPeersFromHistory_); - parseBool(details, - libjami::Account::ConfProperties::ALLOW_CERT_FROM_CONTACT, - allowPeersFromContact_); - parseBool(details, - libjami::Account::ConfProperties::ALLOW_CERT_FROM_TRUSTED, - allowPeersFromTrusted_); - if (not dhtDefaultPort_) - dhtDefaultPort_ = getRandomEvenPort(DHT_PORT_RANGE); - - parseString(details, libjami::Account::ConfProperties::MANAGER_URI, managerUri_); - parseString(details, libjami::Account::ConfProperties::MANAGER_USERNAME, managerUsername_); - parseString(details, libjami::Account::ConfProperties::USERNAME, username_); - - std::string archive_password; - std::string archive_pin; - std::string archive_path; - parseString(details, libjami::Account::ConfProperties::ARCHIVE_PASSWORD, archive_password); - parseString(details, libjami::Account::ConfProperties::ARCHIVE_PIN, archive_pin); - std::transform(archive_pin.begin(), archive_pin.end(), archive_pin.begin(), ::toupper); - parsePath(details, libjami::Account::ConfProperties::ARCHIVE_PATH, archive_path, idPath_); - parseString(details, libjami::Account::ConfProperties::DEVICE_NAME, deviceName_); - - auto oldProxyServer = proxyServer_, oldProxyServerList = proxyListUrl_; - parseString(details, libjami::Account::ConfProperties::DHT_PROXY_LIST_URL, proxyListUrl_); - parseBool(details, libjami::Account::ConfProperties::PROXY_ENABLED, proxyEnabled_); - parseString(details, libjami::Account::ConfProperties::PROXY_SERVER, proxyServer_); - // Migrate from old versions - if (proxyServer_.empty() - || ((proxyServer_ == "dhtproxy.jami.net" || proxyServer_ == "dhtproxy.ring.cx") - && proxyServerCached_.empty())) - proxyServer_ = DHT_DEFAULT_PROXY; - if (proxyServer_ != oldProxyServer || oldProxyServerList != proxyListUrl_) { - JAMI_DBG("DHT Proxy configuration changed, resetting cache"); - proxyServerCached_ = {}; - auto proxyCachePath = cachePath_ + DIR_SEPARATOR_STR "dhtproxy"; - auto proxyListCachePath = cachePath_ + DIR_SEPARATOR_STR "dhtproxylist"; - std::remove(proxyCachePath.c_str()); - std::remove(proxyListCachePath.c_str()); - } - if (not managerUri_.empty() and managerUri_.rfind("http", 0) != 0) { - managerUri_ = "https://" + managerUri_; - } - -#if HAVE_RINGNS - parseString(details, libjami::Account::ConfProperties::RingNS::URI, nameServer_); -#endif - - // update device name if necessary - if (accountManager_) - accountManager_->setAccountDeviceName(deviceName_); - - loadAccount(archive_password, archive_pin, archive_path); -} - -std::map<std::string, std::string> -JamiAccount::getAccountDetails() const -{ - std::lock_guard<std::recursive_mutex> lock(configurationMutex_); - std::map<std::string, std::string> a = SIPAccountBase::getAccountDetails(); - a.emplace(Conf::CONFIG_DHT_PORT, std::to_string(dhtDefaultPort_)); - a.emplace(Conf::CONFIG_DHT_PUBLIC_IN_CALLS, dhtPublicInCalls_ ? TRUE_STR : FALSE_STR); - a.emplace(libjami::Account::ConfProperties::DHT_PEER_DISCOVERY, - dhtPeerDiscovery_ ? TRUE_STR : FALSE_STR); - a.emplace(libjami::Account::ConfProperties::ACCOUNT_PEER_DISCOVERY, - accountPeerDiscovery_ ? TRUE_STR : FALSE_STR); - a.emplace(libjami::Account::ConfProperties::ACCOUNT_PUBLISH, - accountPublish_ ? TRUE_STR : FALSE_STR); - if (accountManager_) { - if (auto info = accountManager_->getInfo()) { - a.emplace(libjami::Account::ConfProperties::DEVICE_ID, info->deviceId); - a.emplace(libjami::Account::ConfProperties::RingNS::ACCOUNT, info->ethAccount); - } - } - a.emplace(libjami::Account::ConfProperties::DEVICE_NAME, deviceName_); - a.emplace(libjami::Account::ConfProperties::Presence::SUPPORT_SUBSCRIBE, TRUE_STR); - if (not archivePath_.empty() or not managerUri_.empty()) - a.emplace(libjami::Account::ConfProperties::ARCHIVE_HAS_PASSWORD, - archiveHasPassword_ ? TRUE_STR : FALSE_STR); - - /* these settings cannot be changed (read only), but clients should still be - * able to read what they are */ - a.emplace(Conf::CONFIG_SRTP_KEY_EXCHANGE, sip_utils::getKeyExchangeName(getSrtpKeyExchange())); - a.emplace(Conf::CONFIG_SRTP_ENABLE, isSrtpEnabled() ? TRUE_STR : FALSE_STR); - a.emplace(Conf::CONFIG_SRTP_RTP_FALLBACK, getSrtpFallback() ? TRUE_STR : FALSE_STR); - - a.emplace(Conf::CONFIG_TLS_CA_LIST_FILE, fileutils::getFullPath(idPath_, tlsCaListFile_)); - a.emplace(Conf::CONFIG_TLS_CERTIFICATE_FILE, - fileutils::getFullPath(idPath_, tlsCertificateFile_)); - a.emplace(Conf::CONFIG_TLS_PRIVATE_KEY_FILE, - fileutils::getFullPath(idPath_, tlsPrivateKeyFile_)); - a.emplace(Conf::CONFIG_TLS_PASSWORD, tlsPassword_); - a.emplace(Conf::CONFIG_TLS_METHOD, "Automatic"); - a.emplace(Conf::CONFIG_TLS_CIPHERS, ""); - a.emplace(Conf::CONFIG_TLS_SERVER_NAME, ""); - a.emplace(Conf::CONFIG_TLS_VERIFY_SERVER, TRUE_STR); - a.emplace(Conf::CONFIG_TLS_VERIFY_CLIENT, TRUE_STR); - a.emplace(Conf::CONFIG_TLS_REQUIRE_CLIENT_CERTIFICATE, TRUE_STR); - a.emplace(Conf::CONFIG_TLS_DISABLE_SECURE_DLG_CHECK, TRUE_STR); - a.emplace(libjami::Account::ConfProperties::ALLOW_CERT_FROM_HISTORY, - allowPeersFromHistory_ ? TRUE_STR : FALSE_STR); - a.emplace(libjami::Account::ConfProperties::ALLOW_CERT_FROM_CONTACT, - allowPeersFromContact_ ? TRUE_STR : FALSE_STR); - a.emplace(libjami::Account::ConfProperties::ALLOW_CERT_FROM_TRUSTED, - allowPeersFromTrusted_ ? TRUE_STR : FALSE_STR); - /* GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT is defined as -1 */ - a.emplace(Conf::CONFIG_TLS_NEGOTIATION_TIMEOUT_SEC, "-1"); - a.emplace(libjami::Account::ConfProperties::PROXY_ENABLED, proxyEnabled_ ? TRUE_STR : FALSE_STR); - a.emplace(libjami::Account::ConfProperties::PROXY_SERVER, proxyServer_); - a.emplace(libjami::Account::ConfProperties::DHT_PROXY_LIST_URL, proxyListUrl_); - a.emplace(libjami::Account::ConfProperties::MANAGER_URI, managerUri_); - a.emplace(libjami::Account::ConfProperties::MANAGER_USERNAME, managerUsername_); -#if HAVE_RINGNS - a.emplace(libjami::Account::ConfProperties::RingNS::URI, nameServer_); -#endif - - return a; -} - std::map<std::string, std::string> JamiAccount::getVolatileAccountDetails() const { @@ -1537,7 +1238,7 @@ JamiAccount::lookupName(const std::string& name) std::lock_guard<std::recursive_mutex> lock(configurationMutex_); if (accountManager_) accountManager_->lookupUri(name, - nameServer_, + config().nameServer, [acc = getAccountID(), name](const std::string& result, NameDirectory::Response response) { emitSignal<libjami::ConfigurationSignal::RegisteredNameFound>( @@ -1583,7 +1284,9 @@ JamiAccount::registerName(const std::string& password, const std::string& name) if (response == NameDirectory::RegistrationResponse::success) { if (auto this_ = w.lock()) { this_->registeredName_ = name; - this_->saveConfig(); + this_->editConfig([&](JamiAccountConfig& config){ + config.registeredName = name; + }); emitSignal<libjami::ConfigurationSignal::VolatileDetailsChanged>( this_->accountID_, this_->getVolatileAccountDetails()); } @@ -1758,13 +1461,11 @@ std::vector<std::string> JamiAccount::loadBootstrap() const { std::vector<std::string> bootstrap; - if (!hostname_.empty()) { - std::string_view stream(hostname_), node_addr; - while (jami::getline(stream, node_addr, ';')) - bootstrap.emplace_back(node_addr); - for (const auto& b : bootstrap) - JAMI_DBG("[Account %s] Bootstrap node: %s", getAccountID().c_str(), b.c_str()); - } + std::string_view stream(config().hostname), node_addr; + while (jami::getline(stream, node_addr, ';')) + bootstrap.emplace_back(node_addr); + for (const auto& b : bootstrap) + JAMI_DBG("[Account %s] Bootstrap node: %s", getAccountID().c_str(), b.c_str()); return bootstrap; } @@ -1923,6 +1624,7 @@ JamiAccount::doRegister_() } JAMI_DBG("[Account %s] Starting account...", getAccountID().c_str()); + const auto& conf = config(); try { if (not accountManager_ or not accountManager_->getInfo()) @@ -1938,7 +1640,7 @@ JamiAccount::doRegister_() convModule()->clearPendingFetch(); #if HAVE_RINGNS - // Look for registered name on the blockchain + // Look for registered name accountManager_->lookupAddress( accountManager_->getInfo()->accountId, [w = weak()](const std::string& result, const NameDirectory::Response& response) { @@ -1946,12 +1648,18 @@ JamiAccount::doRegister_() if (response == NameDirectory::Response::found) { if (this_->registeredName_ != result) { this_->registeredName_ = result; + this_->editConfig([&](JamiAccountConfig& config){ + config.registeredName = result; + }); emitSignal<libjami::ConfigurationSignal::VolatileDetailsChanged>( this_->accountID_, this_->getVolatileAccountDetails()); } } else if (response == NameDirectory::Response::notFound) { if (not this_->registeredName_.empty()) { this_->registeredName_.clear(); + this_->editConfig([&](JamiAccountConfig& config){ + config.registeredName.clear(); + }); emitSignal<libjami::ConfigurationSignal::VolatileDetailsChanged>( this_->accountID_, this_->getVolatileAccountDetails()); } @@ -1967,12 +1675,12 @@ JamiAccount::doRegister_() config.dht_config.id = id_; config.dht_config.cert_cache_all = true; config.push_node_id = getAccountID(); - config.push_token = deviceKey_; - config.push_topic = notificationTopic_; + config.push_token = conf.deviceKey; + config.push_topic = conf.notificationTopic; config.threaded = true; - config.peer_discovery = dhtPeerDiscovery_; - config.peer_publish = dhtPeerDiscovery_; - if (proxyEnabled_) + config.peer_discovery = conf.dhtPeerDiscovery; + config.peer_publish = conf.dhtPeerDiscovery; + if (conf.proxyEnabled) config.proxy_server = proxyServerCached_; if (not config.proxy_server.empty()) { @@ -1985,13 +1693,13 @@ JamiAccount::doRegister_() } // check if dht peer service is enabled - if (accountPeerDiscovery_ or accountPublish_) { + if (conf.accountPeerDiscovery or conf.accountPublish) { peerDiscovery_ = std::make_shared<dht::PeerDiscovery>(); - if (accountPeerDiscovery_) { + if (conf.accountPeerDiscovery) { JAMI_INFO("[Account %s] starting Jami account discovery...", getAccountID().c_str()); startAccountDiscovery(); } - if (accountPublish_) + if (conf.accountPublish) startAccountPublish(); } dht::DhtRunner::Context context {}; @@ -2142,7 +1850,7 @@ JamiAccount::doRegister_() getAccountID().c_str(), name.c_str()); - if (turnEnabled_ && !cacheTurnV4_) { + if (this->config().turnEnabled && !cacheTurnV4_) { // If TURN is enabled, but no TURN cached, there can be a temporary resolution // error to solve. Sometimes, a connectivity change is not enough, so even if // this case is really rare, it should be easy to avoid. @@ -2442,6 +2150,11 @@ JamiAccount::doUnregister(std::function<void(bool)> released_cb) std::condition_variable cv; bool shutdown_complete {false}; + if (peerDiscovery_) { + peerDiscovery_->stopPublish(PEER_DISCOVERY_JAMI_SERVICE); + peerDiscovery_->stopDiscovery(PEER_DISCOVERY_JAMI_SERVICE); + } + JAMI_WARN("[Account %s] unregistering account %p", getAccountID().c_str(), this); dht_->shutdown( [&] { @@ -2741,14 +2454,15 @@ JamiAccount::loadCachedUrl(const std::string& url, void JamiAccount::loadCachedProxyServer(std::function<void(const std::string& proxy)> cb) { - if (proxyEnabled_ and proxyServerCached_.empty()) { - JAMI_DBG("[Account %s] loading DHT proxy URL: %s", - getAccountID().c_str(), - proxyListUrl_.c_str()); - if (proxyListUrl_.empty()) { - cb(getDhtProxyServer(proxyServer_)); + const auto& conf = config(); + if (conf.proxyEnabled and proxyServerCached_.empty()) { + JAMI_DEBUG("[Account {:s}] loading DHT proxy URL: {:s}", + getAccountID(), + conf.proxyListUrl); + if (conf.proxyListUrl.empty()) { + cb(getDhtProxyServer(conf.proxyServer)); } else { - loadCachedUrl(proxyListUrl_, + loadCachedUrl(conf.proxyListUrl, cachePath_ + DIR_SEPARATOR_STR "dhtproxylist", std::chrono::hours(24 * 3), [w = weak(), cb = std::move(cb)](const dht::http::Response& response) { @@ -2756,7 +2470,7 @@ JamiAccount::loadCachedProxyServer(std::function<void(const std::string& proxy)> if (response.status_code == 200) { cb(sthis->getDhtProxyServer(response.body)); } else { - cb(sthis->getDhtProxyServer(sthis->proxyServer_)); + cb(sthis->getDhtProxyServer(sthis->config().proxyServer)); } } }); @@ -2840,8 +2554,8 @@ std::string JamiAccount::getFromUri() const { const std::string uri = "<sip:" + accountManager_->getInfo()->accountId + "@ring.dht>"; - if (not displayName_.empty()) - return "\"" + displayName_ + "\" " + uri; + if (not config().displayName.empty()) + return "\"" + config().displayName + "\" " + uri; return uri; } @@ -2953,7 +2667,7 @@ JamiAccount::getContactHeader(const std::shared_ptr<SipTransport>& sipTransport) auto address = td->self->getLocalAddress().toString(true); bool reliable = transport->flag & PJSIP_TRANSPORT_RELIABLE; return fmt::format("\"{}\" <sips:{}{}{};transport={}>", - displayName_, + config().displayName, id_.second->getId().toString(), address.empty() ? "" : "@", address, @@ -2961,7 +2675,7 @@ JamiAccount::getContactHeader(const std::shared_ptr<SipTransport>& sipTransport) } else { JAMI_ERR("getContactHeader: no SIP transport provided"); return fmt::format("\"{}\" <sips:{}@ring.dht>", - displayName_, + config().displayName, id_.second->getId().toString()); } } @@ -3354,8 +3068,7 @@ JamiAccount::sendMessage(const std::string& to, std::unique_lock<std::mutex> l(confirm->lock); if (not confirm->replied) { if (auto this_ = w.lock()) { - JAMI_DBG() << "[Account " << this_->getAccountID() << "] [message " << token - << "] Timeout"; + JAMI_DBG() << "[Account " << this_->getAccountID() << "] [message " << token << "] Timeout"; for (auto& t : confirm->listenTokens) this_->dht_->cancelListen(t.first, std::move(t.second)); confirm->listenTokens.clear(); @@ -3499,16 +3212,18 @@ JamiAccount::closePeerConnection(const libjami::DataTransferId& tid) void JamiAccount::setPushNotificationToken(const std::string& token) { - JAMI_WARN("[Account %s] setPushNotificationToken: %s", getAccountID().c_str(), token.c_str()); - deviceKey_ = token; - dht_->setPushNotificationToken(deviceKey_); + JAMI_WARNING("[Account {:s}] setPushNotificationToken: {:s}", getAccountID(), token); + SIPAccountBase::setPushNotificationToken(token); + if (dht_) + dht_->setPushNotificationToken(token); } void JamiAccount::setPushNotificationTopic(const std::string& topic) { - notificationTopic_ = topic; - dht_->setPushNotificationTopic(notificationTopic_); + SIPAccountBase::setPushNotificationTopic(topic); + if (dht_) + dht_->setPushNotificationTopic(topic); } /** @@ -3529,7 +3244,7 @@ JamiAccount::getUserUri() const if (not registeredName_.empty()) return JAMI_URI_PREFIX + registeredName_; #endif - return JAMI_URI_PREFIX + username_; + return JAMI_URI_PREFIX + config().username; } std::vector<libjami::Message> @@ -3543,7 +3258,7 @@ JamiAccount::startAccountPublish() { AccountPeerInfo info_pub; info_pub.accountId = dht::InfoHash(accountManager_->getInfo()->accountId); - info_pub.displayName = displayName_; + info_pub.displayName = config().displayName; peerDiscovery_->startPublish<AccountPeerInfo>(PEER_DISCOVERY_JAMI_SERVICE, info_pub); } @@ -3609,6 +3324,7 @@ JamiAccount::setActiveCodecs(const std::vector<unsigned>& list) setCodecActive(AV_CODEC_ID_H264); setCodecActive(AV_CODEC_ID_VP8); } + config_->activeCodecs = getActiveCodecs(MEDIA_ALL); } // Member management @@ -3758,7 +3474,8 @@ JamiAccount::cacheTurnServers() // Avoid multiple refresh if (this_->isRefreshing_.exchange(true)) return; - if (!this_->turnEnabled_) { + const auto& conf = this_->config(); + if (!conf.turnEnabled) { // In this case, we do not use any TURN server std::lock_guard<std::mutex> lk(this_->cachedTurnMutex_); this_->cacheTurnV4_.reset(); @@ -3771,7 +3488,7 @@ JamiAccount::cacheTurnServers() // Retrieve old cached value if available. // This means that we directly get the correct value when launching the application on the // same network - std::string server = this_->turnServer_.empty() ? DEFAULT_TURN_SERVER : this_->turnServer_; + std::string server = conf.turnServer.empty() ? DEFAULT_TURN_SERVER : conf.turnServer; // No need to resolve, it's already a valid address if (IpAddr::isValid(server, AF_INET)) { this_->cacheTurnV4_ = std::make_unique<IpAddr>(server, AF_INET); @@ -3813,7 +3530,7 @@ JamiAccount::cacheTurnServers() // Update TURN this_->cacheTurnV4_ = std::make_unique<IpAddr>(std::move(turnV4)); } - auto turnV6 = IpAddr {server.empty() ? DEFAULT_TURN_SERVER : server, AF_INET6}; + auto turnV6 = IpAddr {server, AF_INET6}; { if (turnV6) { // Cache value to avoid a delay when starting up Jami @@ -3913,7 +3630,7 @@ JamiAccount::requestSIPConnection(const std::string& peerId, connectionManager_->connectDevice( deviceId, "sip", - [w = weak(), id, pc = std::move(pc)](std::shared_ptr<ChannelSocket> socket, + [w = weak(), id = std::move(id), pc = std::move(pc)](std::shared_ptr<ChannelSocket> socket, const DeviceId&) { if (socket) return; diff --git a/src/jamidht/jamiaccount.h b/src/jamidht/jamiaccount.h index 5469355a0e0648d370672d9fdeed4aae24f8cf73..b6be21108592faf3a24d3db2b07406753aa9bb0a 100644 --- a/src/jamidht/jamiaccount.h +++ b/src/jamidht/jamiaccount.h @@ -35,6 +35,7 @@ #include "connectivity/multiplexed_socket.h" #include "data_transfer.h" #include "uri.h" +#include "jamiaccount_config.h" #include "noncopyable.h" #include "connectivity/ip_utils.h" @@ -67,11 +68,6 @@ #include "namedirectory.h" #endif -namespace YAML { -class Node; -class Emitter; -} // namespace YAML - namespace dev { template<unsigned N> class FixedHash; @@ -104,16 +100,8 @@ using GitSocketList = std::map<DeviceId, /* device class JamiAccount : public SIPAccountBase { public: - constexpr static const char* const ACCOUNT_TYPE = "RING"; - constexpr static const in_port_t DHT_DEFAULT_PORT = 4222; - constexpr static const char* const DHT_DEFAULT_BOOTSTRAP = "bootstrap.jami.net"; - constexpr static const char* const DHT_DEFAULT_PROXY = "dhtproxy.jami.net:[80-95]"; - constexpr static const char* const DHT_DEFAULT_BOOTSTRAP_LIST_URL - = "https://config.jami.net/boostrapList"; - constexpr static const char* const DHT_DEFAULT_PROXY_LIST_URL - = "https://config.jami.net/proxyList"; - - /* constexpr */ static const std::pair<uint16_t, uint16_t> DHT_PORT_RANGE; + constexpr static auto ACCOUNT_TYPE = ACCOUNT_TYPE_JAMI; + constexpr static const std::pair<uint16_t, uint16_t> DHT_PORT_RANGE {4000, 8888}; constexpr static int ICE_STREAMS_COUNT {1}; constexpr static int ICE_COMP_COUNT_PER_STREAM {1}; @@ -138,43 +126,29 @@ public: const std::string& getPath() const { return idPath_; } + const JamiAccountConfig& config() const { + return *static_cast<const JamiAccountConfig*>(&Account::config()); + } + + void loadConfig() override; + /** * Constructor * @param accountID The account identifier */ - JamiAccount(const std::string& accountID, bool presenceEnabled); + JamiAccount(const std::string& accountId); ~JamiAccount() noexcept; - /** - * Serialize internal state of this account for configuration - * @param YamlEmitter the configuration engine which generate the configuration file - */ - virtual void serialize(YAML::Emitter& out) const override; - - /** - * Populate the internal state for this account based on info stored in the configuration file - * @param The configuration node for this account - */ - virtual void unserialize(const YAML::Node& node) override; - - /** - * Return an map containing the internal state of this account. Client application can use this - * method to manage account info. - * @return A map containing the account information. - */ - virtual std::map<std::string, std::string> getAccountDetails() const override; - /** * Retrieve volatile details such as recent registration errors * @return std::map< std::string, std::string > The account volatile details */ virtual std::map<std::string, std::string> getVolatileAccountDetails() const override; - /** - * Actually useless, since config loading is done in init() - */ - void loadConfig() override {} + std::unique_ptr<AccountConfig> buildConfig() const override { + return std::make_unique<JamiAccountConfig>(getAccountID(), idPath_); + } /** * Adds an account id to the list of accounts to track on the DHT for @@ -285,9 +259,7 @@ public: const std::map<std::string, std::string>& payloads) override; virtual bool isTlsEnabled() const override { return true; } - bool isSrtpEnabled() const override { return true; } - KeyExchangeProtocol getSrtpKeyExchange() const { return KeyExchangeProtocol::SDES; } virtual bool getSrtpFallback() const override { return false; } @@ -446,7 +418,13 @@ public: */ void startAccountDiscovery(); - void saveConfig() const; + void saveConfig() const override; + + inline void editConfig(std::function<void(JamiAccountConfig& conf)>&& edit) { + Account::editConfig([&](AccountConfig& conf) { + edit(*static_cast<JamiAccountConfig*>(&conf)); + }); + } /** * Get current discovered peers account id and display name @@ -660,13 +638,6 @@ private: const std::string& to_id, IpAddr target); - /** - * Set the internal state for this account, mainly used to manage account details from the - * client application. - * @param The map containing the account information. - */ - virtual void setAccountDetails(const std::map<std::string, std::string>& details) override; - /** * Start a SIP Call * @param call The current call @@ -741,7 +712,6 @@ private: void updateContactHeader(); #if HAVE_RINGNS - std::string nameServer_; std::string registeredName_; #endif std::shared_ptr<dht::Logger> logger_; @@ -754,17 +724,10 @@ private: std::map<dht::Value::Id, PendingMessage> sentMessages_; std::set<std::string, std::less<>> treatedMessages_ {}; - std::string deviceName_ {}; std::string idPath_ {}; std::string cachePath_ {}; std::string dataPath_ {}; - std::string archivePath_ {}; - bool archiveHasPassword_ {true}; - - std::string receipt_ {}; - std::vector<uint8_t> receiptSignature_ {}; - /* tracked buddies presence */ mutable std::mutex buddyInfoMtx; std::map<dht::InfoHash, BuddyInfo> trackedBuddies_; @@ -793,27 +756,15 @@ private: /* Current UPNP mapping */ upnp::Mapping dhtUpnpMapping_ {upnp::PortType::UDP}; - bool dhtPeerDiscovery_ {false}; - /** * Proxy */ - std::string proxyListUrl_; - bool proxyEnabled_ {false}; - std::string proxyServer_ {}; std::string proxyServerCached_ {}; std::mutex dhParamsMtx_ {}; std::shared_future<tls::DhParams> dhParams_; std::condition_variable dhParamsCv_; - bool allowPeersFromHistory_ {true}; - bool allowPeersFromContact_ {true}; - bool allowPeersFromTrusted_ {true}; - - std::string managerUri_ {}; - std::string managerUsername_ {}; - /** * Optional: via_addr construct from received parameters */ @@ -830,8 +781,6 @@ private: std::shared_ptr<dht::PeerDiscovery> peerDiscovery_; std::map<dht::InfoHash, DiscoveredPeer> discoveredPeers_; std::map<std::string, std::string> discoveredPeerMap_; - bool accountPeerDiscovery_ {false}; - bool accountPublish_ {false}; /** * Avoid to refresh the cache multiple times @@ -843,7 +792,7 @@ private: */ void cacheTurnServers(); - std::chrono::duration<int> turnRefreshDelay_ {std::chrono::seconds(10)}; + std::chrono::seconds turnRefreshDelay_ {std::chrono::seconds(10)}; std::set<std::shared_ptr<dht::http::Request>> requests_; diff --git a/src/jamidht/jamiaccount_config.cpp b/src/jamidht/jamiaccount_config.cpp new file mode 100644 index 0000000000000000000000000000000000000000..905a2d7842e7096a085c7382f475090d81ddcb2a --- /dev/null +++ b/src/jamidht/jamiaccount_config.cpp @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2004-2022 Savoir-faire Linux Inc. + * + * 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, 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 <https://www.gnu.org/licenses/>. + */ +#include "jamiaccount_config.h" +#include "account_const.h" +#include "account_schema.h" +#include "configkeys.h" +#include "config/yamlparser.h" +#include "fileutils.h" + +namespace jami { + +namespace Conf { +constexpr const char* const TLS_KEY = "tls"; +constexpr const char* CERTIFICATE_KEY = "certificate"; +constexpr const char* CALIST_KEY = "calist"; +const char* const TLS_PASSWORD_KEY = "password"; +const char* const PRIVATE_KEY_KEY = "privateKey"; +} // namespace Conf + +void +JamiAccountConfig::serialize(YAML::Emitter& out) const +{ + out << YAML::BeginMap; + SipAccountBaseConfig::serialize(out); + out << YAML::Key << Conf::DHT_PORT_KEY << YAML::Value << dhtPort; + out << YAML::Key << Conf::DHT_PUBLIC_IN_CALLS << YAML::Value << allowPublicIncoming; + out << YAML::Key << Conf::DHT_ALLOW_PEERS_FROM_HISTORY << YAML::Value << allowPeersFromHistory; + out << YAML::Key << Conf::DHT_ALLOW_PEERS_FROM_CONTACT << YAML::Value << allowPeersFromContact; + out << YAML::Key << Conf::DHT_ALLOW_PEERS_FROM_TRUSTED << YAML::Value << allowPeersFromTrusted; + out << YAML::Key << libjami::Account::ConfProperties::DHT_PEER_DISCOVERY << YAML::Value + << dhtPeerDiscovery; + out << YAML::Key << libjami::Account::ConfProperties::ACCOUNT_PEER_DISCOVERY << YAML::Value + << accountPeerDiscovery; + out << YAML::Key << libjami::Account::ConfProperties::ACCOUNT_PUBLISH << YAML::Value + << accountPublish; + + out << YAML::Key << Conf::PROXY_ENABLED_KEY << YAML::Value << proxyEnabled; + out << YAML::Key << Conf::PROXY_SERVER_KEY << YAML::Value << proxyServer; + out << YAML::Key << libjami::Account::ConfProperties::DHT_PROXY_LIST_URL << YAML::Value + << proxyListUrl; + +#if HAVE_RINGNS + out << YAML::Key << libjami::Account::ConfProperties::RingNS::URI << YAML::Value << nameServer; + if (not registeredName.empty()) + out << YAML::Key << libjami::Account::VolatileProperties::REGISTERED_NAME << YAML::Value + << registeredName; +#endif + + out << YAML::Key << libjami::Account::ConfProperties::ARCHIVE_PATH << YAML::Value << fileutils::getCleanPath(path, archivePath); + out << YAML::Key << libjami::Account::ConfProperties::ARCHIVE_HAS_PASSWORD << YAML::Value + << archiveHasPassword; + out << YAML::Key << Conf::RING_ACCOUNT_RECEIPT << YAML::Value << receipt; + if (receiptSignature.size() > 0) + out << YAML::Key << Conf::RING_ACCOUNT_RECEIPT_SIG << YAML::Value + << YAML::Binary(receiptSignature.data(), receiptSignature.size()); + out << YAML::Key << libjami::Account::ConfProperties::DEVICE_NAME << YAML::Value << deviceName; + out << YAML::Key << libjami::Account::ConfProperties::MANAGER_URI << YAML::Value << managerUri; + out << YAML::Key << libjami::Account::ConfProperties::MANAGER_USERNAME << YAML::Value + << managerUsername; + + // tls submap + out << YAML::Key << Conf::TLS_KEY << YAML::Value << YAML::BeginMap; + out << YAML::Key << Conf::CALIST_KEY << YAML::Value << fileutils::getCleanPath(path, tlsCaListFile); + out << YAML::Key << Conf::CERTIFICATE_KEY << YAML::Value << fileutils::getCleanPath(path, tlsCertificateFile);; + out << YAML::Key << Conf::TLS_PASSWORD_KEY << YAML::Value << tlsPassword; + out << YAML::Key << Conf::PRIVATE_KEY_KEY << YAML::Value << fileutils::getCleanPath(path, tlsPrivateKeyFile);; + out << YAML::EndMap; + + out << YAML::EndMap; +} + +void +JamiAccountConfig::unserialize(const YAML::Node& node) +{ + using yaml_utils::parseValueOptional; + using yaml_utils::parsePath; + using yaml_utils::parsePathOptional; + SipAccountBaseConfig::unserialize(node); + + // get tls submap + try { + const auto& tlsMap = node[Conf::TLS_KEY]; + parsePathOptional(tlsMap, Conf::CERTIFICATE_KEY, tlsCertificateFile, path); + parsePathOptional(tlsMap, Conf::CALIST_KEY, tlsCaListFile, path); + parseValueOptional(tlsMap, Conf::TLS_PASSWORD_KEY, tlsPassword); + parsePathOptional(tlsMap, Conf::PRIVATE_KEY_KEY, tlsPrivateKeyFile, path); + } catch (...) { + } + parseValueOptional(node, Conf::DHT_ALLOW_PEERS_FROM_HISTORY, allowPeersFromHistory); + parseValueOptional(node, Conf::DHT_ALLOW_PEERS_FROM_CONTACT, allowPeersFromContact); + parseValueOptional(node, Conf::DHT_ALLOW_PEERS_FROM_TRUSTED, allowPeersFromTrusted); + + parseValueOptional(node, Conf::PROXY_ENABLED_KEY, proxyEnabled); + parseValueOptional(node, Conf::PROXY_SERVER_KEY, proxyServer); + parseValueOptional(node, libjami::Account::ConfProperties::DHT_PROXY_LIST_URL, proxyListUrl); + + parseValueOptional(node, libjami::Account::ConfProperties::DEVICE_NAME, deviceName); + parseValueOptional(node, libjami::Account::ConfProperties::MANAGER_URI, managerUri); + parseValueOptional(node, libjami::Account::ConfProperties::MANAGER_USERNAME, managerUsername); + + parsePathOptional(node, libjami::Account::ConfProperties::ARCHIVE_PATH, archivePath, path); + parseValueOptional(node, + libjami::Account::ConfProperties::ARCHIVE_HAS_PASSWORD, + archiveHasPassword); + + try { + parseValueOptional(node, Conf::RING_ACCOUNT_RECEIPT, receipt); + auto receipt_sig = node[Conf::RING_ACCOUNT_RECEIPT_SIG].as<YAML::Binary>(); + receiptSignature = {receipt_sig.data(), receipt_sig.data() + receipt_sig.size()}; + } catch (const std::exception& e) { + JAMI_WARN("can't read receipt: %s", e.what()); + } + + parseValueOptional(node, libjami::Account::ConfProperties::DHT_PEER_DISCOVERY, dhtPeerDiscovery); + parseValueOptional(node, + libjami::Account::ConfProperties::ACCOUNT_PEER_DISCOVERY, + accountPeerDiscovery); + parseValueOptional(node, libjami::Account::ConfProperties::ACCOUNT_PUBLISH, accountPublish); + parseValueOptional(node, libjami::Account::ConfProperties::RingNS::URI, nameServer); + parseValueOptional(node, libjami::Account::VolatileProperties::REGISTERED_NAME, registeredName); + parseValueOptional(node, Conf::DHT_PUBLIC_IN_CALLS, allowPublicIncoming); +} + +std::map<std::string, std::string> +JamiAccountConfig::toMap() const +{ + std::map<std::string, std::string> a = SipAccountBaseConfig::toMap(); + a.emplace(Conf::CONFIG_DHT_PORT, std::to_string(dhtPort)); + a.emplace(Conf::CONFIG_DHT_PUBLIC_IN_CALLS, allowPublicIncoming ? TRUE_STR : FALSE_STR); + a.emplace(libjami::Account::ConfProperties::DHT_PEER_DISCOVERY, + dhtPeerDiscovery ? TRUE_STR : FALSE_STR); + a.emplace(libjami::Account::ConfProperties::ACCOUNT_PEER_DISCOVERY, + accountPeerDiscovery ? TRUE_STR : FALSE_STR); + a.emplace(libjami::Account::ConfProperties::ACCOUNT_PUBLISH, + accountPublish ? TRUE_STR : FALSE_STR); + a.emplace(libjami::Account::ConfProperties::DEVICE_NAME, deviceName); + a.emplace(libjami::Account::ConfProperties::Presence::SUPPORT_SUBSCRIBE, TRUE_STR); + if (not archivePath.empty() or not managerUri.empty()) + a.emplace(libjami::Account::ConfProperties::ARCHIVE_HAS_PASSWORD, + archiveHasPassword ? TRUE_STR : FALSE_STR); + + a.emplace(Conf::CONFIG_TLS_CA_LIST_FILE, fileutils::getFullPath(path, tlsCaListFile)); + a.emplace(Conf::CONFIG_TLS_CERTIFICATE_FILE, fileutils::getFullPath(path, tlsCertificateFile)); + a.emplace(Conf::CONFIG_TLS_PRIVATE_KEY_FILE, fileutils::getFullPath(path, tlsPrivateKeyFile)); + a.emplace(Conf::CONFIG_TLS_PASSWORD, tlsPassword); + a.emplace(libjami::Account::ConfProperties::ALLOW_CERT_FROM_HISTORY, + allowPeersFromHistory ? TRUE_STR : FALSE_STR); + a.emplace(libjami::Account::ConfProperties::ALLOW_CERT_FROM_CONTACT, + allowPeersFromContact ? TRUE_STR : FALSE_STR); + a.emplace(libjami::Account::ConfProperties::ALLOW_CERT_FROM_TRUSTED, + allowPeersFromTrusted ? TRUE_STR : FALSE_STR); + a.emplace(libjami::Account::ConfProperties::PROXY_ENABLED, proxyEnabled ? TRUE_STR : FALSE_STR); + a.emplace(libjami::Account::ConfProperties::PROXY_SERVER, proxyServer); + a.emplace(libjami::Account::ConfProperties::DHT_PROXY_LIST_URL, proxyListUrl); + a.emplace(libjami::Account::ConfProperties::MANAGER_URI, managerUri); + a.emplace(libjami::Account::ConfProperties::MANAGER_USERNAME, managerUsername); +#if HAVE_RINGNS + a.emplace(libjami::Account::ConfProperties::RingNS::URI, nameServer); +#endif + return a; +} + +void +JamiAccountConfig::fromMap(const std::map<std::string, std::string>& details) +{ + SipAccountBaseConfig::fromMap(details); + // TLS + parsePath(details, Conf::CONFIG_TLS_CA_LIST_FILE, tlsCaListFile, path); + parsePath(details, Conf::CONFIG_TLS_CERTIFICATE_FILE, tlsCertificateFile, path); + parsePath(details, Conf::CONFIG_TLS_PRIVATE_KEY_FILE, tlsPrivateKeyFile, path); + parseString(details, Conf::CONFIG_TLS_PASSWORD, tlsPassword); + + if (hostname.empty()) + hostname = DHT_DEFAULT_BOOTSTRAP; + parseString(details, libjami::Account::ConfProperties::BOOTSTRAP_LIST_URL, bootstrapListUrl); + parseInt(details, Conf::CONFIG_DHT_PORT, dhtPort); + parseBool(details, Conf::CONFIG_DHT_PUBLIC_IN_CALLS, allowPublicIncoming); + parseBool(details, libjami::Account::ConfProperties::DHT_PEER_DISCOVERY, dhtPeerDiscovery); + parseBool(details, + libjami::Account::ConfProperties::ACCOUNT_PEER_DISCOVERY, + accountPeerDiscovery); + parseBool(details, libjami::Account::ConfProperties::ACCOUNT_PUBLISH, accountPublish); + parseBool(details, + libjami::Account::ConfProperties::ALLOW_CERT_FROM_HISTORY, + allowPeersFromHistory); + parseBool(details, + libjami::Account::ConfProperties::ALLOW_CERT_FROM_CONTACT, + allowPeersFromContact); + parseBool(details, + libjami::Account::ConfProperties::ALLOW_CERT_FROM_TRUSTED, + allowPeersFromTrusted); + + parseString(details, libjami::Account::ConfProperties::MANAGER_URI, managerUri); + parseString(details, libjami::Account::ConfProperties::MANAGER_USERNAME, managerUsername); + // parseString(details, libjami::Account::ConfProperties::USERNAME, username); + + parseString(details, libjami::Account::ConfProperties::ARCHIVE_PASSWORD, archive_password); + parseString(details, libjami::Account::ConfProperties::ARCHIVE_PIN, archive_pin); + std::transform(archive_pin.begin(), archive_pin.end(), archive_pin.begin(), ::toupper); + parseString(details, libjami::Account::ConfProperties::ARCHIVE_PATH, archive_path); + parseString(details, libjami::Account::ConfProperties::DEVICE_NAME, deviceName); + + auto oldProxyServer = proxyServer, oldProxyServerList = proxyListUrl; + parseString(details, libjami::Account::ConfProperties::DHT_PROXY_LIST_URL, proxyListUrl); + parseBool(details, libjami::Account::ConfProperties::PROXY_ENABLED, proxyEnabled); + parseString(details, libjami::Account::ConfProperties::PROXY_SERVER, proxyServer); + if (not managerUri.empty() and managerUri.rfind("http", 0) != 0) { + managerUri = "https://" + managerUri; + } + +#if HAVE_RINGNS + parseString(details, libjami::Account::ConfProperties::RingNS::URI, nameServer); +#endif +} + +} // namespace jami diff --git a/src/jamidht/jamiaccount_config.h b/src/jamidht/jamiaccount_config.h new file mode 100644 index 0000000000000000000000000000000000000000..dd6d3c8d95b259e02c822f9a8d09796c48c5cbad --- /dev/null +++ b/src/jamidht/jamiaccount_config.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2004-2022 Savoir-faire Linux Inc. + * + * 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, 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 <https://www.gnu.org/licenses/>. + */ +#pragma once +#include "sip/sipaccountbase_config.h" + +namespace jami { +constexpr static const char* ACCOUNT_TYPE_JAMI = "RING"; +constexpr static const char* const DHT_DEFAULT_BOOTSTRAP = "bootstrap.jami.net"; +constexpr static const char* DEFAULT_TURN_SERVER = "turn.jami.net"; +constexpr static const char* DEFAULT_TURN_USERNAME = "ring"; +constexpr static const char* DEFAULT_TURN_PWD = "ring"; +constexpr static const char* DEFAULT_TURN_REALM = "ring"; + +struct JamiAccountConfig : public SipAccountBaseConfig { + JamiAccountConfig(const std::string& id = {}, const std::string& path = {}): SipAccountBaseConfig(ACCOUNT_TYPE_JAMI, id, path) { + // Default values specific to Jami accounts + hostname = DHT_DEFAULT_BOOTSTRAP; + turnServer = DEFAULT_TURN_SERVER; + turnServerUserName = DEFAULT_TURN_USERNAME; + turnServerPwd = DEFAULT_TURN_PWD; + turnServerRealm = DEFAULT_TURN_REALM; + turnEnabled = true; + upnpEnabled = true; + } + void serialize(YAML::Emitter& out) const override; + void unserialize(const YAML::Node& node) override; + std::map<std::string, std::string> toMap() const override; + void fromMap(const std::map<std::string, std::string>&) override; + + std::string deviceName {}; + uint16_t dhtPort {0}; + bool dhtPeerDiscovery {false}; + bool accountPeerDiscovery {false}; + bool accountPublish {false}; + std::string bootstrapListUrl {"https://config.jami.net/boostrapList"}; + + bool proxyEnabled {false}; + std::string proxyServer {"dhtproxy.jami.net:[80-95]"}; + std::string proxyListUrl {"https://config.jami.net/proxyList"}; + + std::string nameServer {}; + std::string registeredName {}; + + bool allowPeersFromHistory {true}; + bool allowPeersFromContact {true}; + bool allowPeersFromTrusted {true}; + bool allowPublicIncoming {true}; + + std::string managerUri {}; + std::string managerUsername {}; + + std::string archivePath {"archive.gz"}; + bool archiveHasPassword {true}; + // not saved, only used client->daemon + std::string archive_password; + std::string archive_pin; + std::string archive_path; + + std::string receipt {}; + std::vector<uint8_t> receiptSignature {}; +}; + +} diff --git a/src/manager.cpp b/src/manager.cpp index 1f92858d89fc81ac7b8225041c48db54bd89d8d5..b82e9f51326f620239b46ef6d924fec065fcd75c 100644 --- a/src/manager.cpp +++ b/src/manager.cpp @@ -43,7 +43,6 @@ #include "account.h" #include "string_utils.h" #include "jamidht/jamiaccount.h" -#include "sip/sipvoiplink.h" #include "account.h" #include <opendht/rng.h> using random_device = dht::crypto::random_device; @@ -52,6 +51,7 @@ using random_device = dht::crypto::random_device; #include "connectivity/sip_utils.h" #include "sip/sipvoiplink.h" +#include "sip/sipaccount_config.h" #include "im/instant_messaging.h" @@ -628,23 +628,26 @@ void Manager::ManagerPimpl::loadAccount(const YAML::Node& node, int& errorCount) { using yaml_utils::parseValue; - - std::string accountType; - parseValue(node, "type", accountType); + using yaml_utils::parseValueOptional; std::string accountid; parseValue(node, "id", accountid); + std::string accountType = ACCOUNT_TYPE_SIP; + parseValueOptional(node, "type", accountType); + if (!accountid.empty()) { - if (base_.accountFactory.isSupportedType(accountType.c_str())) { + if (base_.accountFactory.isSupportedType(accountType)) { if (auto a = base_.accountFactory.createAccount(accountType.c_str(), accountid)) { - a->unserialize(node); + auto config = a->buildConfig(); + config->unserialize(node); + a->setConfig(std::move(config)); } else { - JAMI_ERR("Failed to create account type \"%s\"", accountType.c_str()); + JAMI_ERROR("Failed to create account of type \"{:s}\"", accountType); ++errorCount; } } else { - JAMI_WARN("Ignoring unknown account type \"%s\"", accountType.c_str()); + JAMI_WARNING("Ignoring unknown account type \"{:s}\"", accountType); } } } @@ -1655,15 +1658,6 @@ Manager::ioContext() const return pimpl_->ioContext_; } -void -Manager::addTask(std::function<bool()>&& task, const char* filename, uint32_t linum) -{ - pimpl_->scheduler_.scheduleAtFixedRate(std::move(task), - std::chrono::milliseconds(30), - filename, - linum); -} - std::shared_ptr<Task> Manager::scheduleTask(std::function<void()>&& task, std::chrono::steady_clock::time_point when, @@ -1682,13 +1676,6 @@ Manager::scheduleTaskIn(std::function<void()>&& task, return pimpl_->scheduler_.scheduleIn(std::move(task), timeout, filename, linum); } -// Must be invoked periodically by a timer from the main event loop -void -Manager::pollEvents() -{} - -// THREAD=Main - void Manager::saveConfig(const std::shared_ptr<Account>& acc) { @@ -1724,7 +1711,7 @@ Manager::saveConfig() saveConfig(ringAccount); } } else { - account->serialize(out); + account->config().serialize(out); } } out << YAML::EndSeq; @@ -1743,7 +1730,7 @@ Manager::saveConfig() std::lock_guard<std::mutex> lock(fileutils::getFileLock(pimpl_->path_)); std::ofstream fout = fileutils::ofstream(pimpl_->path_); - fout << out.c_str(); + fout.write(out.c_str(), out.size()); } catch (const YAML::Exception& e) { JAMI_ERR("%s", e.what()); } catch (const std::runtime_error& e) { @@ -2098,14 +2085,6 @@ Manager::playRingtone(const std::string& accountID) return; } - std::string ringtone = account->getRingtonePath(); - if (ringtone.find(DIR_SEPARATOR_CH) == std::string::npos) { - // A base file name was provided (such as the default); try to - // resolve it from Jami's data installation prefix. - ringtone = std::string(JAMI_DATADIR) + DIR_SEPARATOR_STR + RINGDIR + DIR_SEPARATOR_STR - + ringtone; - } - { std::lock_guard<std::mutex> lock(pimpl_->audioLayerMutex_); @@ -2119,7 +2098,7 @@ Manager::playRingtone(const std::string& accountID) pimpl_->toneCtrl_.setSampleRate(pimpl_->audiodriver_->getSampleRate()); } - if (not pimpl_->toneCtrl_.setAudioFile(ringtone)) + if (not pimpl_->toneCtrl_.setAudioFile(account->getRingtonePath())) ringback(); } @@ -2245,7 +2224,7 @@ AudioDeviceGuard::AudioDeviceGuard(Manager& manager, AudioDeviceType type) auto streamId = (unsigned) type; if (streamId >= manager_.pimpl_->audioStreamUsers_.size()) throw std::invalid_argument("Invalid audio device type"); - if (manager_.pimpl_->audioStreamUsers_[(unsigned) type]++ == 0) { + if (manager_.pimpl_->audioStreamUsers_[streamId]++ == 0) { if (auto layer = manager_.getAudioDriver()) layer->startStream(type); } @@ -2658,18 +2637,14 @@ Manager::getVolatileAccountDetails(const std::string& accountID) const } } -// method to reduce the if/else mess. -// Even better, switch to XML ! - void Manager::setAccountDetails(const std::string& accountID, const std::map<std::string, std::string>& details) { JAMI_DBG("Set account details for %s", accountID.c_str()); - const auto account = getAccount(accountID); - - if (account == nullptr) { + auto account = getAccount(accountID); + if (not account) { JAMI_ERR("Could not find account %s", accountID.c_str()); return; } @@ -2679,16 +2654,8 @@ Manager::setAccountDetails(const std::string& accountID, return; // Unregister before modifying any account information - // FIXME: inefficient api, don't pass details (not as ref nor copy) - // let client requiests them we needed. account->doUnregister([&](bool /* transport_free */) { account->setAccountDetails(details); - // Serialize configuration to disk once it is done - if (auto ringAccount = std::dynamic_pointer_cast<JamiAccount>(account)) { - saveConfig(ringAccount); - } else { - saveConfig(); - } if (account->isUsable()) account->doRegister(); @@ -2839,10 +2806,9 @@ Manager::loadAccountMap(const YAML::Node& node) if (fileutils::isFile(configFile)) { try { if (auto a = accountFactory.createAccount(JamiAccount::ACCOUNT_TYPE, dir)) { - std::ifstream file = fileutils::ifstream(configFile); - YAML::Node parsedConfig = YAML::Load(file); - file.close(); - a->unserialize(parsedConfig); + auto config = a->buildConfig(); + config->unserialize(YAML::LoadFile(configFile)); + a->setConfig(std::move(config)); } } catch (const std::exception& e) { JAMI_ERR("Can't import account %s: %s", dir.c_str(), e.what()); @@ -2904,8 +2870,6 @@ Manager::sendRegister(const std::string& accountID, bool enable) return; acc->setEnabled(enable); - acc->loadConfig(); - saveConfig(acc); if (acc->isEnabled()) { @@ -3208,13 +3172,10 @@ Manager::getDefaultModerators(const std::string& accountID) void Manager::enableLocalModerators(const std::string& accountID, bool isModEnabled) { - auto acc = getAccount(accountID); - if (!acc) { - JAMI_ERR("Fail to set local moderators, account %s not found", accountID.c_str()); - return; - } - acc->enableLocalModerators(isModEnabled); - saveConfig(acc); + if (auto acc = getAccount(accountID)) + acc->editConfig([&](AccountConfig& config){ + config.localModeratorsEnabled = isModEnabled; + }); } bool @@ -3231,13 +3192,10 @@ Manager::isLocalModeratorsEnabled(const std::string& accountID) void Manager::setAllModerators(const std::string& accountID, bool allModerators) { - auto acc = getAccount(accountID); - if (!acc) { - JAMI_ERR("Fail to set all moderators, account %s not found", accountID.c_str()); - return; - } - acc->setAllModerators(allModerators); - saveConfig(acc); + if (auto acc = getAccount(accountID)) + acc->editConfig([&](AccountConfig& config){ + config.allModeratorsEnabled = allModerators; + }); } bool diff --git a/src/manager.h b/src/manager.h index 124ea80a9957aeb331f70b6d5b69accd54993f60..7d251c0e57921bdfb8361e7651ce63db402bf99c 100644 --- a/src/manager.h +++ b/src/manager.h @@ -765,10 +765,6 @@ public: */ void unregisterAccounts(); - /** - * Call periodically to poll for VoIP events */ - void pollEvents(); - /** * Create a new outgoing call * @param toUrl Destination address @@ -789,10 +785,6 @@ public: std::shared_ptr<asio::io_context> ioContext() const; - void addTask(std::function<bool()>&& task, - const char* filename = CURRENT_FILENAME(), - uint32_t linum = CURRENT_LINE()); - std::shared_ptr<Task> scheduleTask(std::function<void()>&& task, std::chrono::steady_clock::time_point when, const char* filename = CURRENT_FILENAME(), diff --git a/src/meson.build b/src/meson.build index 86452aea04ecfeb381032daca8ec6c8d952e2d12..fd0fe308f80da5ba9a674972dd6e9bc94ce350c6 100644 --- a/src/meson.build +++ b/src/meson.build @@ -49,6 +49,7 @@ libjami_sources = files( 'jamidht/conversationrepository.cpp', 'jamidht/gitserver.cpp', 'jamidht/jamiaccount.cpp', + 'jamidht/jamiaccount_config.cpp', 'jamidht/namedirectory.cpp', 'jamidht/p2p.cpp', 'jamidht/server_account_manager.cpp', @@ -93,6 +94,7 @@ libjami_sources = files( 'media/srtp.c', 'media/system_codec_container.cpp', 'account.cpp', + 'account_config.cpp', 'account_factory.cpp', 'archiver.cpp', 'base64.cpp', @@ -115,6 +117,8 @@ libjami_sources = files( 'sip/sdp.cpp', 'sip/sipaccountbase.cpp', 'sip/sipaccount.cpp', + 'sip/sipaccountbase_config.cpp', + 'sip/sipaccount_config.cpp', 'sip/sipcall.cpp', 'sip/sippresence.cpp', 'sip/siptransport.cpp', diff --git a/src/sip/CMakeLists.txt b/src/sip/CMakeLists.txt index 329b760d5d409fbc3054ec5ca5469d2d366f1c95..9da37a07a65d58fcf9666bfb33028e1471e8f655 100644 --- a/src/sip/CMakeLists.txt +++ b/src/sip/CMakeLists.txt @@ -14,6 +14,10 @@ list (APPEND Source_Files__sip "${CMAKE_CURRENT_SOURCE_DIR}/sipaccount.h" "${CMAKE_CURRENT_SOURCE_DIR}/sipaccountbase.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/sipaccountbase.h" + "${CMAKE_CURRENT_SOURCE_DIR}/sipaccount_config.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/sipaccount_config.h" + "${CMAKE_CURRENT_SOURCE_DIR}/sipaccountbase_config.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/sipaccountbase_config.h" "${CMAKE_CURRENT_SOURCE_DIR}/sipcall.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/sipcall.h" "${CMAKE_CURRENT_SOURCE_DIR}/sippresence.cpp" diff --git a/src/sip/Makefile.am b/src/sip/Makefile.am index 9193ef9ddd8e4ef17d77964c809aebaf2b35af9f..51a03797fe5092c4916856a91b770f3011779e59 100644 --- a/src/sip/Makefile.am +++ b/src/sip/Makefile.am @@ -10,6 +10,10 @@ libsiplink_la_SOURCES = \ ./sip/sipvoiplink.cpp \ ./sip/siptransport.cpp \ ./sip/sipaccountbase.h \ + ./sip/sipaccountbase_config.h \ + ./sip/sipaccountbase_config.cpp \ + ./sip/sipaccount_config.h \ + ./sip/sipaccount_config.cpp \ ./sip/sdp.h \ ./sip/sipaccount.h \ ./sip/sipcall.h \ diff --git a/src/sip/sipaccount.cpp b/src/sip/sipaccount.cpp index 071511ceb90a313411dbf7d3e67f4fb01e2ddf18..695b71839e13684b3dd4f8aa4b7e9d177441a4b1 100644 --- a/src/sip/sipaccount.cpp +++ b/src/sip/sipaccount.cpp @@ -85,11 +85,8 @@ namespace jami { using yaml_utils::parseValue; using yaml_utils::parseValueOptional; -using yaml_utils::parseVectorMap; using sip_utils::CONST_PJ_STR; -static constexpr int MIN_REGISTRATION_TIME = 60; // seconds -static constexpr unsigned DEFAULT_REGISTRATION_EXPIRE = 3600; // seconds static constexpr unsigned REGISTRATION_FIRST_RETRY_INTERVAL = 60; // seconds static constexpr unsigned REGISTRATION_RETRY_INTERVAL = 300; // seconds static const char* const VALID_TLS_PROTOS[] = {"Default", "TLSv1.2", "TLSv1.1", "TLSv1"}; @@ -125,36 +122,12 @@ registration_cb(pjsip_regc_cbparam* param) SIPAccount::SIPAccount(const std::string& accountID, bool presenceEnabled) : SIPAccountBase(accountID) - , auto_rereg_() - , credentials_() - , regc_(nullptr) - , bRegister_(false) - , registrationExpire_(DEFAULT_REGISTRATION_EXPIRE) - , serviceRoute_() - , cred_() - , tlsSetting_() , ciphers_(100) - , tlsMethod_("TLSv1") - , tlsCiphers_() - , tlsServerName_("") - , tlsVerifyServer_(false) - , tlsVerifyClient_(true) - , tlsRequireClientCertificate_(true) - , tlsDisableSecureDlgCheck_(true) - , tlsNegotiationTimeoutSec_("2") - , registrationStateDetailed_() - , registrationRefreshEnabled_(true) - , receivedParameter_("") - , rPort_(-1) - , via_addr_() - , allowIPAutoRewrite_(true) - , via_tp_(nullptr) , presence_(presenceEnabled ? new SIPPresence(this) : nullptr) { via_addr_.host.ptr = 0; via_addr_.host.slen = 0; via_addr_.port = 0; - setActiveCodecs({}); } SIPAccount::~SIPAccount() noexcept @@ -314,7 +287,7 @@ SIPAccount::onTransportStateChanged(pjsip_transport_state state, // Notify the client of the new transport state if (currentStatus != transportStatus_) emitSignal<libjami::ConfigurationSignal::VolatileDetailsChanged>(accountID_, - getVolatileAccountDetails()); + getVolatileAccountDetails()); } void @@ -428,68 +401,13 @@ SIPAccount::SIPStartCall(std::shared_ptr<SIPCall>& call) return true; } -void -SIPAccount::serialize(YAML::Emitter& out) const -{ - std::lock_guard<std::recursive_mutex> lock(configurationMutex_); - - out << YAML::BeginMap; - SIPAccountBase::serialize(out); - - out << YAML::Key << Conf::BIND_ADDRESS_KEY << YAML::Value << bindAddress_; - out << YAML::Key << Conf::PORT_KEY << YAML::Value << localPort_; - - out << YAML::Key << USERNAME_KEY << YAML::Value << username_; - - // each credential is a map, and we can have multiple credentials - out << YAML::Key << Conf::CRED_KEY << YAML::Value << getCredentials(); - - out << YAML::Key << Conf::KEEP_ALIVE_ENABLED << YAML::Value << registrationRefreshEnabled_; - - out << YAML::Key << PRESENCE_MODULE_ENABLED_KEY << YAML::Value - << (presence_ and presence_->isEnabled()); - out << YAML::Key << Conf::PRESENCE_PUBLISH_SUPPORTED_KEY << YAML::Value - << (presence_ and presence_->isSupported(PRESENCE_FUNCTION_PUBLISH)); - out << YAML::Key << Conf::PRESENCE_SUBSCRIBE_SUPPORTED_KEY << YAML::Value - << (presence_ and presence_->isSupported(PRESENCE_FUNCTION_SUBSCRIBE)); - - out << YAML::Key << Conf::CONFIG_ACCOUNT_REGISTRATION_EXPIRE << YAML::Value - << registrationExpire_; - out << YAML::Key << Conf::SERVICE_ROUTE_KEY << YAML::Value << serviceRoute_; - out << YAML::Key << Conf::ALLOW_IP_AUTO_REWRITE << YAML::Value << allowIPAutoRewrite_; - - // tls submap - out << YAML::Key << Conf::TLS_KEY << YAML::Value << YAML::BeginMap; - SIPAccountBase::serializeTls(out); - out << YAML::Key << Conf::TLS_ENABLE_KEY << YAML::Value << tlsEnable_; - out << YAML::Key << Conf::TLS_PORT_KEY << YAML::Value << tlsListenerPort_; - out << YAML::Key << Conf::VERIFY_CLIENT_KEY << YAML::Value << tlsVerifyClient_; - out << YAML::Key << Conf::VERIFY_SERVER_KEY << YAML::Value << tlsVerifyServer_; - out << YAML::Key << Conf::REQUIRE_CERTIF_KEY << YAML::Value << tlsRequireClientCertificate_; - out << YAML::Key << Conf::DISABLE_SECURE_DLG_CHECK << YAML::Value << tlsDisableSecureDlgCheck_; - out << YAML::Key << Conf::TIMEOUT_KEY << YAML::Value << tlsNegotiationTimeoutSec_; - out << YAML::Key << Conf::CIPHERS_KEY << YAML::Value << tlsCiphers_; - out << YAML::Key << Conf::METHOD_KEY << YAML::Value << tlsMethod_; - out << YAML::Key << Conf::SERVER_KEY << YAML::Value << tlsServerName_; - out << YAML::EndMap; - - // srtp submap - out << YAML::Key << Conf::SRTP_KEY << YAML::Value << YAML::BeginMap; - out << YAML::Key << Conf::KEY_EXCHANGE_KEY << YAML::Value - << sip_utils::getKeyExchangeName(srtpKeyExchange_); - out << YAML::Key << Conf::RTP_FALLBACK_KEY << YAML::Value << srtpFallback_; - out << YAML::EndMap; - - out << YAML::EndMap; -} - void SIPAccount::usePublishedAddressPortInVIA() { publishedIpStr_ = getPublishedIpAddress().toString(); via_addr_.host.ptr = (char*) publishedIpStr_.c_str(); via_addr_.host.slen = publishedIpStr_.size(); - via_addr_.port = publishedPort_; + via_addr_.port = publishedPortUsed_; } void @@ -513,207 +431,6 @@ validate(std::string& member, const std::string& param, const T& valid) JAMI_ERR("Invalid parameter \"%s\"", param.c_str()); } -void -SIPAccount::unserialize(const YAML::Node& node) -{ - std::lock_guard<std::recursive_mutex> lock(configurationMutex_); - - SIPAccountBase::unserialize(node); - parseValue(node, USERNAME_KEY, username_); - - parseValue(node, Conf::BIND_ADDRESS_KEY, bindAddress_); - - int port = sip_utils::DEFAULT_SIP_PORT; - parseValue(node, Conf::PORT_KEY, port); - localPort_ = port; - - if (not isIP2IP()) { - unsigned expire = 0; - if (not parseValueOptional(node, Conf::CONFIG_ACCOUNT_REGISTRATION_EXPIRE, expire)) { - // Proably using an older config file. - parseValueOptional(node, Preferences::REGISTRATION_EXPIRE_KEY, expire); - } - setRegistrationExpire(expire); - - parseValue(node, Conf::KEEP_ALIVE_ENABLED, registrationRefreshEnabled_); - parseValue(node, Conf::SERVICE_ROUTE_KEY, serviceRoute_); - parseValueOptional(node, Conf::ALLOW_IP_AUTO_REWRITE, allowIPAutoRewrite_); - - const auto& credsNode = node[Conf::CRED_KEY]; - setCredentials(parseVectorMap(credsNode, - {Conf::CONFIG_ACCOUNT_REALM, - Conf::CONFIG_ACCOUNT_USERNAME, - Conf::CONFIG_ACCOUNT_PASSWORD})); - } - - bool presEnabled = false; - parseValue(node, PRESENCE_MODULE_ENABLED_KEY, presEnabled); - enablePresence(presEnabled); - bool publishSupported = false; - parseValue(node, Conf::PRESENCE_PUBLISH_SUPPORTED_KEY, publishSupported); - bool subscribeSupported = false; - parseValue(node, Conf::PRESENCE_SUBSCRIBE_SUPPORTED_KEY, subscribeSupported); - if (presence_) { - presence_->support(PRESENCE_FUNCTION_PUBLISH, publishSupported); - presence_->support(PRESENCE_FUNCTION_SUBSCRIBE, subscribeSupported); - } - - // Init stun server name with default server name - stunServerName_ = pj_str((char*) stunServer_.data()); - - const auto& credsNode = node[Conf::CRED_KEY]; - setCredentials(parseVectorMap(credsNode, - {Conf::CONFIG_ACCOUNT_REALM, - Conf::CONFIG_ACCOUNT_USERNAME, - Conf::CONFIG_ACCOUNT_PASSWORD})); - - // get tls submap - const auto& tlsMap = node[Conf::TLS_KEY]; - parseValue(tlsMap, Conf::CERTIFICATE_KEY, tlsCertificateFile_); - parseValue(tlsMap, Conf::CALIST_KEY, tlsCaListFile_); - parseValue(tlsMap, Conf::TLS_PASSWORD_KEY, tlsPassword_); - parseValue(tlsMap, Conf::PRIVATE_KEY_KEY, tlsPrivateKeyFile_); - parseValue(tlsMap, Conf::TLS_ENABLE_KEY, tlsEnable_); - parseValue(tlsMap, Conf::TLS_PORT_KEY, tlsListenerPort_); - parseValue(tlsMap, Conf::CIPHERS_KEY, tlsCiphers_); - - std::string tmpMethod(tlsMethod_); - parseValue(tlsMap, Conf::METHOD_KEY, tmpMethod); - validate(tlsMethod_, tmpMethod, VALID_TLS_PROTOS); - - parseValue(tlsMap, Conf::SERVER_KEY, tlsServerName_); - parseValue(tlsMap, Conf::REQUIRE_CERTIF_KEY, tlsRequireClientCertificate_); - parseValueOptional(tlsMap, Conf::DISABLE_SECURE_DLG_CHECK, tlsDisableSecureDlgCheck_); - parseValue(tlsMap, Conf::VERIFY_CLIENT_KEY, tlsVerifyClient_); - parseValue(tlsMap, Conf::VERIFY_SERVER_KEY, tlsVerifyServer_); - // FIXME - parseValue(tlsMap, Conf::TIMEOUT_KEY, tlsNegotiationTimeoutSec_); - - // get srtp submap - const auto& srtpMap = node[Conf::SRTP_KEY]; - std::string tmpKey; - parseValue(srtpMap, Conf::KEY_EXCHANGE_KEY, tmpKey); - srtpKeyExchange_ = sip_utils::getKeyExchangeProtocol(tmpKey.c_str()); - parseValue(srtpMap, Conf::RTP_FALLBACK_KEY, srtpFallback_); -} - -void -SIPAccount::setAccountDetails(const std::map<std::string, std::string>& details) -{ - std::lock_guard<std::recursive_mutex> lock(configurationMutex_); - - SIPAccountBase::setAccountDetails(details); - parseString(details, Conf::CONFIG_ACCOUNT_USERNAME, username_); - - parseInt(details, Conf::CONFIG_LOCAL_PORT, localPort_); - - // TLS - parseString(details, Conf::CONFIG_TLS_CA_LIST_FILE, tlsCaListFile_); - parseString(details, Conf::CONFIG_TLS_CERTIFICATE_FILE, tlsCertificateFile_); - parseString(details, Conf::CONFIG_TLS_PRIVATE_KEY_FILE, tlsPrivateKeyFile_); - parseString(details, Conf::CONFIG_TLS_PASSWORD, tlsPassword_); - - // SIP specific account settings - parseString(details, Conf::CONFIG_BIND_ADDRESS, bindAddress_); - parseString(details, Conf::CONFIG_ACCOUNT_ROUTESET, serviceRoute_); - parseBool(details, Conf::CONFIG_ACCOUNT_IP_AUTO_REWRITE, allowIPAutoRewrite_); - - unsigned expire = 0; - parseInt(details, Conf::CONFIG_ACCOUNT_REGISTRATION_EXPIRE, expire); - setRegistrationExpire(expire); - - parseBool(details, Conf::CONFIG_KEEP_ALIVE_ENABLED, registrationRefreshEnabled_); - bool presenceEnabled = false; - parseBool(details, Conf::CONFIG_PRESENCE_ENABLED, presenceEnabled); - enablePresence(presenceEnabled); - - // TLS settings - parseBool(details, Conf::CONFIG_TLS_ENABLE, tlsEnable_); - parseInt(details, Conf::CONFIG_TLS_LISTENER_PORT, tlsListenerPort_); - auto iter = details.find(Conf::CONFIG_TLS_METHOD); - if (iter != details.end()) - validate(tlsMethod_, iter->second, VALID_TLS_PROTOS); - parseString(details, Conf::CONFIG_TLS_CIPHERS, tlsCiphers_); - parseString(details, Conf::CONFIG_TLS_SERVER_NAME, tlsServerName_); - parseBool(details, Conf::CONFIG_TLS_VERIFY_SERVER, tlsVerifyServer_); - parseBool(details, Conf::CONFIG_TLS_VERIFY_CLIENT, tlsVerifyClient_); - parseBool(details, Conf::CONFIG_TLS_REQUIRE_CLIENT_CERTIFICATE, tlsRequireClientCertificate_); - parseBool(details, Conf::CONFIG_TLS_DISABLE_SECURE_DLG_CHECK, tlsDisableSecureDlgCheck_); - parseString(details, Conf::CONFIG_TLS_NEGOTIATION_TIMEOUT_SEC, tlsNegotiationTimeoutSec_); - parseBool(details, Conf::CONFIG_TLS_VERIFY_SERVER, tlsVerifyServer_); - parseBool(details, Conf::CONFIG_TLS_VERIFY_CLIENT, tlsVerifyClient_); - parseBool(details, Conf::CONFIG_TLS_REQUIRE_CLIENT_CERTIFICATE, tlsRequireClientCertificate_); - parseString(details, Conf::CONFIG_TLS_NEGOTIATION_TIMEOUT_SEC, tlsNegotiationTimeoutSec_); - - // srtp settings - parseBool(details, Conf::CONFIG_SRTP_RTP_FALLBACK, srtpFallback_); - iter = details.find(Conf::CONFIG_SRTP_KEY_EXCHANGE); - if (iter != details.end()) - srtpKeyExchange_ = sip_utils::getKeyExchangeProtocol(iter->second.c_str()); - - if (credentials_.empty()) { // credentials not set, construct 1 entry - JAMI_WARN("No credentials set, inferring them..."); - std::vector<std::map<std::string, std::string>> v; - std::map<std::string, std::string> map; - map[Conf::CONFIG_ACCOUNT_USERNAME] = username_; - parseString(details, Conf::CONFIG_ACCOUNT_PASSWORD, map[Conf::CONFIG_ACCOUNT_PASSWORD]); - map[Conf::CONFIG_ACCOUNT_REALM] = "*"; - v.push_back(map); - setCredentials(v); - } - if (isIP2IP()) { - localPort_ = (localPort_ == sip_utils::DEFAULT_AUTO_SELECT_PORT) - ? sip_utils::DEFAULT_SIP_PORT - : localPort_; - tlsListenerPort_ = (tlsListenerPort_ == sip_utils::DEFAULT_AUTO_SELECT_PORT) - ? sip_utils::DEFAULT_SIP_TLS_PORT - : tlsListenerPort_; - } -} - -std::map<std::string, std::string> -SIPAccount::getAccountDetails() const -{ - std::lock_guard<std::recursive_mutex> lock(configurationMutex_); - - auto a = SIPAccountBase::getAccountDetails(); - - std::string password {}; - if (hasCredentials()) { - for (const auto& cred : credentials_) - if (cred.username == username_) { - password = cred.password; - break; - } - } - a.emplace(Conf::CONFIG_ACCOUNT_PASSWORD, std::move(password)); - - a.emplace(Conf::CONFIG_BIND_ADDRESS, bindAddress_); - a.emplace(Conf::CONFIG_LOCAL_PORT, std::to_string(localPort_)); - a.emplace(Conf::CONFIG_ACCOUNT_ROUTESET, serviceRoute_); - a.emplace(Conf::CONFIG_ACCOUNT_IP_AUTO_REWRITE, allowIPAutoRewrite_ ? TRUE_STR : FALSE_STR); - a.emplace(Conf::CONFIG_ACCOUNT_REGISTRATION_EXPIRE, std::to_string(registrationExpire_)); - a.emplace(Conf::CONFIG_KEEP_ALIVE_ENABLED, registrationRefreshEnabled_ ? TRUE_STR : FALSE_STR); - - a.emplace(Conf::CONFIG_PRESENCE_ENABLED, - presence_ and presence_->isEnabled() ? TRUE_STR : FALSE_STR); - a.emplace(Conf::CONFIG_PRESENCE_PUBLISH_SUPPORTED, - presence_ and presence_->isSupported(PRESENCE_FUNCTION_PUBLISH) ? TRUE_STR - : FALSE_STR); - a.emplace(Conf::CONFIG_PRESENCE_SUBSCRIBE_SUPPORTED, - presence_ and presence_->isSupported(PRESENCE_FUNCTION_SUBSCRIBE) ? TRUE_STR - : FALSE_STR); - - auto tlsSettings(getTlsSettings()); - a.insert(tlsSettings.begin(), tlsSettings.end()); - - a.emplace(Conf::CONFIG_SRTP_KEY_EXCHANGE, sip_utils::getKeyExchangeName(srtpKeyExchange_)); - a.emplace(Conf::CONFIG_SRTP_ENABLE, isSrtpEnabled() ? TRUE_STR : FALSE_STR); - a.emplace(Conf::CONFIG_SRTP_RTP_FALLBACK, srtpFallback_ ? TRUE_STR : FALSE_STR); - - return a; -} - std::map<std::string, std::string> SIPAccount::getVolatileAccountDetails() const { @@ -752,13 +469,13 @@ SIPAccount::getVolatileAccountDetails() const bool SIPAccount::mapPortUPnP() { - upnp::Mapping map(upnp::PortType::UDP, publishedPort_, localPort_); + upnp::Mapping map(upnp::PortType::UDP, config().publishedPort, config().localPort); map.setNotifyCallback([w = weak()](upnp::Mapping::sharedPtr_t mapRes) { if (auto accPtr = w.lock()) { auto oldPort = static_cast<in_port_t>(accPtr->publishedPortUsed_); bool success = mapRes->getState() == upnp::MappingState::OPEN or mapRes->getState() == upnp::MappingState::IN_PROGRESS; - auto newPort = success ? mapRes->getExternalPort() : accPtr->publishedPort_; + auto newPort = success ? mapRes->getExternalPort() : accPtr->config().publishedPort; if (not success and not accPtr->isRegistered()) { JAMI_WARN("[Account %s] Failed to open port %u: registering SIP account anyway", accPtr->getAccountID().c_str(), @@ -800,12 +517,11 @@ SIPAccount::setPushNotificationToken(const std::string& pushDeviceToken) getAccountID().c_str(), pushDeviceToken.c_str()); - if (deviceKey_ == pushDeviceToken) + if (config().deviceKey == pushDeviceToken) return; + SIPAccountBase::setPushNotificationToken(pushDeviceToken); - deviceKey_ = pushDeviceToken; - - if (enabled_) + if (config().enabled) doUnregister([&](bool /* transport_free */) { doRegister(); }); } @@ -815,7 +531,7 @@ SIPAccount::pushNotificationReceived(const std::string& from, { JAMI_WARN("[SIP Account %s] pushNotificationReceived: %s", getAccountID().c_str(), from.c_str()); - if (enabled_) + if (config().enabled) doUnregister([&](bool /* transport_free */) { doRegister(); }); } @@ -827,7 +543,7 @@ SIPAccount::doRegister() return; } - JAMI_DBG("doRegister %s", hostname_.c_str()); + JAMI_DEBUG("doRegister {:s}", config_->hostname); /* if UPnP is enabled, then wait for IGD to complete registration */ if (upnpCtrl_) { @@ -853,8 +569,8 @@ SIPAccount::doRegister1_() } } - link_.resolveSrvName(hasServiceRoute() ? getServiceRoute() : hostname_, - tlsEnable_ ? PJSIP_TRANSPORT_TLS : PJSIP_TRANSPORT_UDP, + link_.resolveSrvName(hasServiceRoute() ? getServiceRoute() : config().hostname, + config().tlsEnable ? PJSIP_TRANSPORT_TLS : PJSIP_TRANSPORT_UDP, [w = weak()](std::vector<IpAddr> host_ips) { if (auto acc = w.lock()) { std::lock_guard<std::recursive_mutex> lock( @@ -888,11 +604,11 @@ SIPAccount::doRegister2_() } bool ipv6 = bindAddress.isIpv6(); - transportType_ = tlsEnable_ ? (ipv6 ? PJSIP_TRANSPORT_TLS6 : PJSIP_TRANSPORT_TLS) - : (ipv6 ? PJSIP_TRANSPORT_UDP6 : PJSIP_TRANSPORT_UDP); + transportType_ = config().tlsEnable ? (ipv6 ? PJSIP_TRANSPORT_TLS6 : PJSIP_TRANSPORT_TLS) + : (ipv6 ? PJSIP_TRANSPORT_UDP6 : PJSIP_TRANSPORT_UDP); // Init TLS settings if the user wants to use TLS - if (tlsEnable_) { + if (config().tlsEnable) { JAMI_DBG("TLS is enabled for account %s", accountID_.c_str()); // Dropping current calls already using the transport is currently required @@ -912,14 +628,11 @@ SIPAccount::doRegister2_() tlsListener_.reset(); } - // Init STUN settings for this account if the user selected it - initStunConfiguration(); - // In our definition of the ip2ip profile (aka Direct IP Calls), // no registration should be performed if (isIP2IP()) { // If we use Tls for IP2IP, transports will be created on connection. - if (!tlsEnable_) { + if (!config().tlsEnable) { setTransport(link_.sipTransportBroker->getUdpTransport(bindAddress)); } setRegistrationState(RegistrationState::REGISTERED); @@ -932,9 +645,9 @@ SIPAccount::doRegister2_() if (isTlsEnabled()) { setTransport(link_.sipTransportBroker->getTlsTransport(tlsListener_, hostIp_, - tlsServerName_.empty() - ? hostname_ - : tlsServerName_)); + config().tlsServerName.empty() + ? config().hostname + : config().tlsServerName)); } else { setTransport(link_.sipTransportBroker->getUdpTransport(bindAddress)); } @@ -1000,7 +713,7 @@ SIPAccount::sendRegister() return; } - setRegister(true); + bRegister_ = true; setRegistrationState(RegistrationState::TRYING); pjsip_regc* regc = nullptr; @@ -1094,7 +807,7 @@ SIPAccount::setUpTransmissionData(pjsip_tx_data* tdata, long transportKeyType) { if (hostIp_) { auto ai = &tdata->dest_info; - ai->name = pj_strdup3(tdata->pool, hostname_.c_str()); + ai->name = pj_strdup3(tdata->pool, config().hostname.c_str()); ai->addr.count = 1; ai->addr.entry[0].type = (pjsip_transport_type_e) transportKeyType; pj_memcpy(&ai->addr.entry[0].addr, hostIp_.pjPtr(), sizeof(pj_sockaddr)); @@ -1149,7 +862,7 @@ SIPAccount::onRegister(pjsip_regc_cbparam* param) */ // update_rfc5626_status(acc, param->rdata); - if (allowIPAutoRewrite_ and checkNATAddress(param, link_.getPool())) + if (config().allowIPAutoRewrite and checkNATAddress(param, link_.getPool())) JAMI_WARN("New contact: %s", getContactHeader().c_str()); /* TODO Check and update Service-Route header */ @@ -1181,10 +894,10 @@ SIPAccount::onRegister(pjsip_regc_cbparam* param) scheduleReregistration(); } - if (param->expiration != registrationExpire_) { + if (param->expiration != config().registrationExpire) { JAMI_DBG("Registrar returned EXPIRE value [%u s] different from the requested [%u s]", param->expiration, - registrationExpire_); + config().registrationExpire); // NOTE: We don't alter the EXPIRE set by the user even if the registrar // returned a different value. PJSIP lib will set the proper timer for // the refresh, if the auto-regisration is enabled. @@ -1200,8 +913,7 @@ SIPAccount::sendUnregister() return; } - setRegister(false); - + bRegister_ = false; pjsip_regc* regc = getRegistrationInfo(); if (!regc) throw VoipLinkException("Registration structure is NULL"); @@ -1262,7 +974,8 @@ void SIPAccount::initTlsConfiguration() { pjsip_tls_setting_default(&tlsSetting_); - tlsSetting_.proto = tlsProtocolFromString(tlsMethod_); + const auto& conf = config(); + tlsSetting_.proto = tlsProtocolFromString(conf.tlsMethod); // Determine the cipher list supported on this machine CipherArray avail_ciphers(256); @@ -1272,7 +985,7 @@ SIPAccount::initTlsConfiguration() avail_ciphers.resize(cipherNum); ciphers_.clear(); - std::string_view stream(tlsCiphers_), item; + std::string_view stream(conf.tlsCiphers), item; while (jami::getline(stream, item, ' ')) { std::string cipher(item); auto item_cid = pj_ssl_cipher_id(cipher.c_str()); @@ -1295,14 +1008,10 @@ SIPAccount::initTlsConfiguration() trimCiphers(); - pj_strset(&tlsSetting_.ca_list_file, (char*) tlsCaListFile_.c_str(), tlsCaListFile_.size()); - pj_strset(&tlsSetting_.cert_file, - (char*) tlsCertificateFile_.c_str(), - tlsCertificateFile_.size()); - pj_strset(&tlsSetting_.privkey_file, - (char*) tlsPrivateKeyFile_.c_str(), - tlsPrivateKeyFile_.size()); - pj_strset(&tlsSetting_.password, (char*) tlsPassword_.c_str(), tlsPassword_.size()); + tlsSetting_.ca_list_file = CONST_PJ_STR(conf.tlsCaListFile); + tlsSetting_.cert_file = CONST_PJ_STR(conf.tlsCaListFile); + tlsSetting_.privkey_file = CONST_PJ_STR(conf.tlsPrivateKeyFile); + tlsSetting_.password = CONST_PJ_STR(conf.tlsPassword); JAMI_DBG("Using %zu ciphers", ciphers_.size()); tlsSetting_.ciphers_num = ciphers_.size(); @@ -1310,14 +1019,11 @@ SIPAccount::initTlsConfiguration() tlsSetting_.ciphers = &ciphers_.front(); } - tlsSetting_.verify_server = tlsVerifyServer_; - tlsSetting_.verify_client = tlsVerifyClient_; - tlsSetting_.require_client_cert = tlsRequireClientCertificate_; - - pjsip_cfg()->endpt.disable_secure_dlg_check = tlsDisableSecureDlgCheck_; - JAMI_DEBUG("Secure check dialog disabled ? {}", tlsDisableSecureDlgCheck_); - - tlsSetting_.timeout.sec = atol(tlsNegotiationTimeoutSec_.c_str()); + tlsSetting_.verify_server = conf.tlsVerifyServer; + tlsSetting_.verify_client = conf.tlsVerifyClient; + tlsSetting_.require_client_cert = conf.tlsRequireClientCertificate; + pjsip_cfg()->endpt.disable_secure_dlg_check = conf.tlsDisableSecureDlgCheck; + tlsSetting_.timeout.sec = conf.tlsNegotiationTimeout; tlsSetting_.qos_type = PJ_QOS_TYPE_BEST_EFFORT; tlsSetting_.qos_ignore_error = PJ_TRUE; @@ -1326,7 +1032,7 @@ SIPAccount::initTlsConfiguration() void SIPAccount::initStunConfiguration() { - std::string_view stunServer(stunServer_); + std::string_view stunServer(config().stunServer); auto pos = stunServer.find(':'); if (pos == std::string_view::npos) { stunServerName_ = sip_utils::CONST_PJ_STR(stunServer); @@ -1341,11 +1047,11 @@ SIPAccount::initStunConfiguration() void SIPAccount::loadConfig() { - if (registrationExpire_ == 0) - registrationExpire_ - = DEFAULT_REGISTRATION_EXPIRE; /** Default expire value for registration */ - - if (tlsEnable_) { + SIPAccountBase::loadConfig(); + setCredentials(config().credentials); + enablePresence(config().presenceEnabled); + initStunConfiguration(); + if (config().tlsEnable) { initTlsConfiguration(); transportType_ = PJSIP_TRANSPORT_TLS; } else @@ -1361,26 +1067,26 @@ SIPAccount::fullMatch(std::string_view username, std::string_view hostname) cons bool SIPAccount::userMatch(std::string_view username) const { - return !username.empty() and username == username_; + return !username.empty() and username == config().username; } bool SIPAccount::hostnameMatch(std::string_view hostname) const { - if (hostname == hostname_) + if (hostname == config().hostname) return true; const auto a = ip_utils::getAddrList(hostname); - const auto b = ip_utils::getAddrList(hostname_); + const auto b = ip_utils::getAddrList(config().hostname); return ip_utils::haveCommonAddr(a, b); } bool SIPAccount::proxyMatch(std::string_view hostname) const { - if (hostname == serviceRoute_) + if (hostname == config().serviceRoute) return true; const auto a = ip_utils::getAddrList(hostname); - const auto b = ip_utils::getAddrList(hostname_); + const auto b = ip_utils::getAddrList(config().hostname); return ip_utils::haveCommonAddr(a, b); } @@ -1410,8 +1116,9 @@ SIPAccount::getFromUri() const std::string transport; // Get login name if username is not specified - std::string username(username_.empty() ? getLoginName() : username_); - std::string hostname(hostname_); + const auto& conf = config(); + std::string username(conf.username.empty() ? getLoginName() : conf.username); + std::string hostname(conf.hostname); // UDP does not require the transport specification if (transportType_ == PJSIP_TRANSPORT_TLS || transportType_ == PJSIP_TRANSPORT_TLS6) { @@ -1421,7 +1128,7 @@ SIPAccount::getFromUri() const scheme = "sip:"; // Get machine hostname if not provided - if (hostname_.empty()) { + if (hostname.empty()) { hostname = sip_utils::as_view(*pj_gethostname()); } @@ -1429,8 +1136,8 @@ SIPAccount::getFromUri() const hostname = IpAddr(hostname).toString(false, true); std::string uri = "<" + scheme + username + "@" + hostname + transport + ">"; - if (not displayName_.empty()) - return "\"" + displayName_ + "\" " + uri; + if (not conf.displayName.empty()) + return "\"" + conf.displayName + "\" " + uri; return uri; } @@ -1454,7 +1161,7 @@ SIPAccount::getToUri(const std::string& username) const // Check if hostname is already specified if (username.find('@') == std::string::npos) - hostname = hostname_; + hostname = config().hostname; if (not hostname.empty() and IpAddr::isIpv6(hostname)) hostname = IpAddr(hostname).toString(false, true); @@ -1481,10 +1188,10 @@ SIPAccount::getServerUri() const } std::string host; - if (IpAddr::isIpv6(hostname_)) - host = IpAddr(hostname_).toString(false, true); + if (IpAddr::isIpv6(config().hostname)) + host = IpAddr(config().hostname).toString(false, true); else - host = hostname_; + host = config().hostname; return "<" + scheme + host + transport + ">"; } @@ -1518,12 +1225,12 @@ SIPAccount::updateContactHeader() return; } - auto contactHdr = printContactHeader(username_, - displayName_, + auto contactHdr = printContactHeader(config().username, + config().displayName, contactAddress_.toString(false, true), contactAddress_.getPort(), PJSIP_TRANSPORT_IS_SECURE(transport_->get()), - deviceKey_); + config().deviceKey); contactHeader_ = std::move(contactHdr); } @@ -1552,18 +1259,22 @@ SIPAccount::initContactAddress() pj_uint16_t port; // Init the address to the local address. - link_.findLocalAddressFromTransport(transport_->get(), transportType, hostname_, address, port); + link_.findLocalAddressFromTransport(transport_->get(), + transportType, + config().hostname, + address, + port); if (getUPnPActive() and getUPnPIpAddress()) { address = getUPnPIpAddress().toString(); port = publishedPortUsed_; useUPnPAddressPortInVIA(); JAMI_DBG("Using UPnP address %s and port %d", address.c_str(), port); - } else if (not publishedSameasLocal_) { + } else if (not config().publishedSameasLocal) { address = getPublishedIpAddress().toString(); - port = publishedPort_; + port = config().publishedPort; JAMI_DBG("Using published address %s and port %d", address.c_str(), port); - } else if (stunEnabled_) { + } else if (config().stunEnabled) { auto success = link_.findLocalAddressFromSTUN(transport_->get(), &stunServerName_, stunPort_, @@ -1572,7 +1283,7 @@ SIPAccount::initContactAddress() if (not success) emitSignal<libjami::ConfigurationSignal::StunStatusFailed>(getAccountID()); setPublishedAddress({address}); - publishedPort_ = port; + publishedPortUsed_ = port; usePublishedAddressPortInVIA(); } else { if (!receivedParameter_.empty()) { @@ -1683,82 +1394,26 @@ SIPAccount::getSupportedTlsProtocols() } void -SIPAccount::Credentials::computePasswordHash() -{ - pj_md5_context pms; - - /* Compute md5 hash = MD5(username ":" realm ":" password) */ - pj_md5_init(&pms); - pj_md5_update(&pms, (const uint8_t*) username.data(), username.length()); - pj_md5_update(&pms, (const uint8_t*) ":", 1); - pj_md5_update(&pms, (const uint8_t*) realm.data(), realm.length()); - pj_md5_update(&pms, (const uint8_t*) ":", 1); - pj_md5_update(&pms, (const uint8_t*) password.data(), password.length()); - - unsigned char digest[16]; - pj_md5_final(&pms, digest); - - char hash[32]; - - for (int i = 0; i < 16; ++i) - pj_val_to_hex_digit(digest[i], &hash[2 * i]); - - password_h = {hash, 32}; -} - -void -SIPAccount::setCredentials(const std::vector<std::map<std::string, std::string>>& creds) +SIPAccount::setCredentials(const std::vector<SipAccountConfig::Credentials>& creds) { - if (creds.empty()) { - JAMI_ERR("Cannot authenticate with empty credentials list"); - return; - } - credentials_.clear(); cred_.clear(); - - bool md5HashingEnabled = Manager::instance().preferences.getMd5Hash(); - - credentials_.reserve(creds.size()); cred_.reserve(creds.size()); - for (const auto& cred : creds) { - auto realm = cred.find(Conf::CONFIG_ACCOUNT_REALM); - auto user = cred.find(Conf::CONFIG_ACCOUNT_USERNAME); - auto passw = cred.find(Conf::CONFIG_ACCOUNT_PASSWORD); - credentials_.emplace_back(realm != cred.end() ? realm->second : "", - user != cred.end() ? user->second : "", - passw != cred.end() ? passw->second : ""); - auto& c = credentials_.back(); - if (md5HashingEnabled) - c.computePasswordHash(); + bool md5HashingEnabled = Manager::instance().preferences.getMd5Hash(); + for (auto& c : creds) { cred_.emplace_back( - pjsip_cred_info {/*.realm = */ pj_str((char*) c.realm.c_str()), - /*.scheme = */ pj_str((char*) "digest"), - /*.username = */ pj_str((char*) c.username.c_str()), + pjsip_cred_info {/*.realm = */ CONST_PJ_STR(c.realm), + /*.scheme = */ CONST_PJ_STR("digest"), + /*.username = */ CONST_PJ_STR(c.username), /*.data_type = */ - (c.password_h.empty() ? PJSIP_CRED_DATA_PLAIN_PASSWD - : PJSIP_CRED_DATA_DIGEST), + (md5HashingEnabled ? PJSIP_CRED_DATA_DIGEST + : PJSIP_CRED_DATA_PLAIN_PASSWD), /*.data = */ - pj_str((char*) (c.password_h.empty() ? c.password.c_str() - : c.password_h.c_str())), + CONST_PJ_STR(md5HashingEnabled ? c.password_h : c.password), /*.ext = */ {}}); } } -std::vector<std::map<std::string, std::string>> -SIPAccount::getCredentials() const -{ - std::vector<std::map<std::string, std::string>> ret; - ret.reserve(credentials_.size()); - for (const auto& c : credentials_) { - ret.emplace_back( - std::map<std::string, std::string> {{Conf::CONFIG_ACCOUNT_REALM, c.realm}, - {Conf::CONFIG_ACCOUNT_USERNAME, c.username}, - {Conf::CONFIG_ACCOUNT_PASSWORD, c.password}}); - } - return ret; -} - void SIPAccount::setRegistrationState(RegistrationState state, unsigned details_code, @@ -1767,50 +1422,15 @@ SIPAccount::setRegistrationState(RegistrationState state, std::string details_str; const pj_str_t* description = pjsip_get_status_text(details_code); if (description) - details_str = {description->ptr, (size_t) description->slen}; + details_str = sip_utils::as_view(*description); setRegistrationStateDetailed({details_code, details_str}); SIPAccountBase::setRegistrationState(state, details_code, details_str); } -void -SIPAccount::setRegistrationExpire(unsigned expire) -{ - if (expire >= MIN_REGISTRATION_TIME) { - JAMI_DBG("Set SIP registration EXPIRE to %u - current %u", expire, registrationExpire_); - registrationExpire_ = expire; - } else { - JAMI_WARN("SIP registration EXPIRE %u is lower than min value %u", - expire, - MIN_REGISTRATION_TIME); - registrationExpire_ = MIN_REGISTRATION_TIME; - } -} - -std::map<std::string, std::string> -SIPAccount::getTlsSettings() const -{ - return {{Conf::CONFIG_TLS_ENABLE, tlsEnable_ ? TRUE_STR : FALSE_STR}, - {Conf::CONFIG_TLS_LISTENER_PORT, std::to_string(tlsListenerPort_)}, - {Conf::CONFIG_TLS_CA_LIST_FILE, tlsCaListFile_}, - {Conf::CONFIG_TLS_CERTIFICATE_FILE, tlsCertificateFile_}, - {Conf::CONFIG_TLS_PRIVATE_KEY_FILE, tlsPrivateKeyFile_}, - {Conf::CONFIG_TLS_PASSWORD, tlsPassword_}, - {Conf::CONFIG_TLS_METHOD, tlsMethod_}, - {Conf::CONFIG_TLS_CIPHERS, tlsCiphers_}, - {Conf::CONFIG_TLS_SERVER_NAME, tlsServerName_}, - {Conf::CONFIG_TLS_VERIFY_SERVER, tlsVerifyServer_ ? TRUE_STR : FALSE_STR}, - {Conf::CONFIG_TLS_VERIFY_CLIENT, tlsVerifyClient_ ? TRUE_STR : FALSE_STR}, - {Conf::CONFIG_TLS_REQUIRE_CLIENT_CERTIFICATE, - tlsRequireClientCertificate_ ? TRUE_STR : FALSE_STR}, - {Conf::CONFIG_TLS_DISABLE_SECURE_DLG_CHECK, - tlsDisableSecureDlgCheck_ ? TRUE_STR : FALSE_STR}, - {Conf::CONFIG_TLS_NEGOTIATION_TIMEOUT_SEC, tlsNegotiationTimeoutSec_}}; -} - bool SIPAccount::isIP2IP() const { - return hostname_.empty(); + return config().hostname.empty(); } SIPPresence* @@ -1957,8 +1577,6 @@ SIPAccount::checkNATAddress(pjsip_regc_cbparam* param, pj_pool_t* pool) } // Set published Ip address - publishedSameasLocal_ = false; - publishedIpAddress_ = via_addrstr; setPublishedAddress(IpAddr(via_addrstr)); /* Compare received and rport with the URI in our registration */ @@ -2033,12 +1651,12 @@ SIPAccount::checkNATAddress(pjsip_regc_cbparam* param, pj_pool_t* pool) * Build new Contact header */ { - auto tempContact = printContactHeader(username_, - displayName_, + auto tempContact = printContactHeader(config().username, + config().displayName, via_addrstr, rport, PJSIP_TRANSPORT_IS_SECURE(tp), - deviceKey_); + config().deviceKey); if (tempContact.empty()) { JAMI_ERR("Invalid contact header"); @@ -2134,7 +1752,7 @@ SIPAccount::scheduleReregistration() void SIPAccount::updateDialogViaSentBy(pjsip_dialog* dlg) { - if (allowIPAutoRewrite_ && via_addr_.host.slen > 0) + if (config().allowIPAutoRewrite && via_addr_.host.slen > 0) pjsip_dlg_set_via_sent_by(dlg, &via_addr_, via_tp_); } @@ -2324,15 +1942,16 @@ IpAddr SIPAccount::createBindingAddress() { auto family = hostIp_ ? hostIp_.getFamily() : PJ_AF_INET; + const auto& conf = config(); - IpAddr ret = bindAddress_.empty() - ? (interface_ == ip_utils::DEFAULT_INTERFACE + IpAddr ret = conf.bindAddress.empty() + ? (conf.interface == ip_utils::DEFAULT_INTERFACE ? ip_utils::getAnyHostAddr(family) : ip_utils::getInterfaceAddr(getLocalInterface(), family)) - : IpAddr(bindAddress_, family); + : IpAddr(conf.bindAddress, family); if (ret.getPort() == 0) { - ret.setPort(tlsEnable_ ? getTlsListenerPort() : getLocalPort()); + ret.setPort(conf.tlsEnable ? conf.tlsListenerPort : conf.localPort); } return ret; @@ -2350,6 +1969,7 @@ SIPAccount::setActiveCodecs(const std::vector<unsigned>& list) JAMI_WARN("All video codecs disabled, enabling all"); setAllCodecsActive(MEDIA_VIDEO, true); } + config_->activeCodecs = getActiveCodecs(MEDIA_ALL); } } // namespace jami diff --git a/src/sip/sipaccount.h b/src/sip/sipaccount.h index 6b4ddf04b4c901daa22442239cdb1f40791a53f1..4c3adb950cda84760ca9d3da1e6230382b9ebf66 100644 --- a/src/sip/sipaccount.h +++ b/src/sip/sipaccount.h @@ -31,6 +31,7 @@ #include "sip/siptransport.h" #include "noncopyable.h" #include "ring_types.h" // enable_if_base_of +#include "sipaccount_config.h" #include <pjsip/sip_transport_tls.h> #include <pjsip/sip_types.h> @@ -39,23 +40,8 @@ #include <vector> #include <map> -namespace YAML { -class Node; -class Emitter; -} // namespace YAML - namespace jami { -namespace Conf { -const char* const KEEP_ALIVE_ENABLED = "keepAlive"; - -// TODO: write an object to store credential which implement serializable -const char* const SRTP_KEY = "srtp"; -const char* const SRTP_ENABLE_KEY = "enable"; -const char* const KEY_EXCHANGE_KEY = "keyExchange"; -const char* const RTP_FALLBACK_KEY = "rtpFallback"; -} // namespace Conf - typedef std::vector<pj_ssl_cipher> CipherArray; class SIPPresence; @@ -68,7 +54,7 @@ class SIPCall; class SIPAccount : public SIPAccountBase { public: - constexpr static const char* const ACCOUNT_TYPE = "SIP"; + constexpr static auto ACCOUNT_TYPE = ACCOUNT_TYPE_SIP; constexpr static const char* const PN_FCM = "fcm"; constexpr static const char* const PN_APNS = "apns"; @@ -97,6 +83,19 @@ public: ~SIPAccount() noexcept; + const SipAccountConfig& config() const { + return *static_cast<const SipAccountConfig*>(&Account::config()); + } + + std::unique_ptr<AccountConfig> buildConfig() const override { + return std::make_unique<SipAccountConfig>(getAccountID()); + } + inline void editConfig(std::function<void(SipAccountConfig& conf)>&& edit) { + Account::editConfig([&](AccountConfig& conf) { + edit(*static_cast<SipAccountConfig*>(&conf)); + }); + } + const char* getAccountType() const override { return ACCOUNT_TYPE; } pjsip_host_port getHostPortFromSTUN(pj_pool_t* pool); @@ -123,25 +122,6 @@ public: */ bool isIP2IP() const override; - /** - * Serialize internal state of this account for configuration - * @param out Emitter to which state will be saved - */ - virtual void serialize(YAML::Emitter& out) const override; - - /** - * Populate the internal state for this account based on info stored in the configuration file - * @param The configuration node for this account - */ - virtual void unserialize(const YAML::Node& node) override; - - /** - * Return an map containing the internal state of this account. Client application can use this - * method to manage account info. - * @return A map containing the account information. - */ - virtual std::map<std::string, std::string> getAccountDetails() const override; - /** * Retrieve volatile details such as recent registration errors * @return std::map< std::string, std::string > The account volatile details @@ -188,13 +168,13 @@ public: * @param none * @return int The number of credentials set for this account. */ - unsigned getCredentialCount() const { return credentials_.size(); } + unsigned getCredentialCount() const { return config().credentials.size(); } - bool hasCredentials() const { return not credentials_.empty(); } + bool hasCredentials() const { return not config().credentials.empty(); } - void setCredentials(const std::vector<std::map<std::string, std::string>>& details); - - std::vector<std::map<std::string, std::string>> getCredentials() const; + std::vector<std::map<std::string, std::string>> getCredentials() const { + return config().getCredentials(); + } virtual void setRegistrationState(RegistrationState state, unsigned code = 0, @@ -209,28 +189,15 @@ public: */ unsigned getRegistrationExpire() const { - if (registrationExpire_ == 0) - return PJSIP_REGC_EXPIRATION_NOT_SPECIFIED; - - return registrationExpire_; + unsigned re = config().registrationExpire; + return re ? re : PJSIP_REGC_EXPIRATION_NOT_SPECIFIED; } - /** - * Set the expiration for this account as found in - * the "Expire" sip header or the CONTACT's "expire" param. - */ - void setRegistrationExpire(unsigned expire); - /** * Registration flag */ bool isRegistered() const { return bRegister_; } - /** - * Set registration flag - */ - void setRegister(bool result) { bRegister_ = result; } - /** * Get the registration structure that is used * for PJSIP in the registration process. @@ -259,27 +226,13 @@ public: * actually using. * @return pj_uint16 The port used for that account */ - pj_uint16_t getLocalPort() const { return localPort_; } - - /** - * Set the new port on which this account is running over. - * @pram port The port used by this account. - */ - void setLocalPort(pj_uint16_t port) { localPort_ = port; } + uint16_t getLocalPort() const { return config().localPort; } - /** - * Get the bind ip address on which the account should use, or is - * actually using. - * Note: if it is NULL, this address should not be used - * @return std::string The bind ip address used for that account - */ - std::string getBindAddress() const { return bindAddress_; } - - /** - * Set the new bind ip address on which this account is bind on. - * @pram address The bind ip address used by this account. - */ - void setBindAddress(const std::string& address) { bindAddress_ = address; } + void setLocalPort(uint16_t port) { + editConfig([&](SipAccountConfig& config){ + config.localPort = port; + }); + } /** * @return pjsip_tls_setting structure, filled from the configuration @@ -292,9 +245,9 @@ public: * Get the local port for TLS listener. * @return pj_uint16 The port used for that account */ - pj_uint16_t getTlsListenerPort() const { return tlsListenerPort_; } + pj_uint16_t getTlsListenerPort() const { return config().tlsListenerPort; } - pj_str_t getStunServerName() const override { return stunServerName_; } + pj_str_t getStunServerName() const { return stunServerName_; } static const std::vector<std::string>& getSupportedTlsCiphers(); static const std::vector<std::string>& getSupportedTlsProtocols(); @@ -310,7 +263,17 @@ public: * @return bool Tells if current transport for that * account is set to OTHER. */ - bool isStunEnabled() const override { return stunEnabled_; } + bool isStunEnabled() const override { return config().stunEnabled; } + + /** + * @return pj_str_t , filled from the configuration + * file, that can be used directly by PJSIP to initialize + * an alternate UDP transport. + */ + std::string getStunServer() const + { + return config().stunServer; + } /** * @return pj_str_t "From" uri based on account information. @@ -354,13 +317,13 @@ public: */ std::string getContactHeader() const; - std::string getServiceRoute() const { return serviceRoute_; } + std::string getServiceRoute() const { return config().serviceRoute; } - bool hasServiceRoute() const { return not serviceRoute_.empty(); } + bool hasServiceRoute() const { return not config().serviceRoute.empty(); } - virtual bool isTlsEnabled() const override { return tlsEnable_; } + virtual bool isTlsEnabled() const override { return config().tlsEnable; } - virtual bool getSrtpFallback() const override { return srtpFallback_; } + virtual bool getSrtpFallback() const override { return config().srtpFallback; } void setReceivedParameter(const std::string& received) { @@ -375,7 +338,7 @@ public: int getRPort() const { if (rPort_ == -1) - return localPort_; + return config().localPort; else return rPort_; } @@ -386,7 +349,7 @@ public: via_addr_.port = rPort; } - bool isRegistrationRefreshEnabled() const { return registrationRefreshEnabled_; } + bool isRegistrationRefreshEnabled() const { return config().registrationRefreshEnabled; } void setTransport(const std::shared_ptr<SipTransport>& = nullptr); @@ -459,7 +422,7 @@ public: IpAddr createBindingAddress(); void setActiveCodecs(const std::vector<unsigned>& list) override; - bool isSrtpEnabled() const override { return srtpKeyExchange_ != KeyExchangeProtocol::NONE; } + bool isSrtpEnabled() const override { return config().srtpKeyExchange != KeyExchangeProtocol::NONE; } void setPushNotificationToken(const std::string& pushDeviceToken = "") override; @@ -478,14 +441,9 @@ private: bool initContactAddress(); void updateContactHeader(); - void setUpTransmissionData(pjsip_tx_data* tdata, long transportKeyType); + void setCredentials(const std::vector<SipAccountConfig::Credentials>& creds); - /** - * Set the internal state for this account, mainly used to manage account details from the - * client application. - * @param The map containing the account information. - */ - void setAccountDetails(const std::map<std::string, std::string>& details) override; + void setUpTransmissionData(pjsip_tx_data* tdata, long transportKeyType); NON_COPYABLE(SIPAccount); @@ -514,10 +472,10 @@ private: struct { - pj_bool_t active; /**< Flag of reregister status. */ - pj_timer_entry timer; /**< Timer for reregistration. */ - unsigned attempt_cnt; /**< Attempt counter. */ - } auto_rereg_; /**< Reregister/reconnect data. */ + pj_bool_t active {false}; /**< Flag of reregister status. */ + pj_timer_entry timer {}; /**< Timer for reregistration. */ + unsigned attempt_cnt {0}; /**< Attempt counter. */ + } auto_rereg_ {}; /**< Reregister/reconnect data. */ std::uniform_int_distribution<int> delay10ZeroDist_ {-10000, 10000}; std::uniform_int_distribution<unsigned int> delay10PosDist_ {0, 10000}; @@ -525,24 +483,6 @@ private: void scheduleReregistration(); void autoReregTimerCb(); - /** - * Map of credential for this account - */ - struct Credentials - { - std::string realm {}; - std::string username {}; - std::string password {}; - std::string password_h {}; - Credentials(const std::string& r, const std::string& u, const std::string& p) - : realm(r) - , username(u) - , password(p) - {} - void computePasswordHash(); - }; - std::vector<Credentials> credentials_; - std::shared_ptr<SipTransport> transport_ {}; std::shared_ptr<TlsListener> tlsListener_ {}; @@ -610,23 +550,13 @@ private: /** * The pjsip client registration information */ - pjsip_regc* regc_; + pjsip_regc* regc_ {nullptr}; /** * To check if the account is registered */ bool bRegister_; - /** - * Network settings - */ - unsigned registrationExpire_; - - /** - * Input Outbound Proxy Server Address - */ - std::string serviceRoute_; - /** * Credential information stored for further registration. * Points to credentials_ members. @@ -653,62 +583,17 @@ private: */ pj_uint16_t stunPort_ {PJ_STUN_PORT}; - /** - * Local port to whih this account is bound - */ - pj_uint16_t localPort_ {sip_utils::DEFAULT_AUTO_SELECT_PORT}; - - /** - * Potential ip addresss on which this account is bound - */ - std::string bindAddress_; - - /** - * The TLS listener port - */ - pj_uint16_t tlsListenerPort_ {sip_utils::DEFAULT_AUTO_SELECT_PORT}; - /** * Send Request Callback */ static void onComplete(void* token, pjsip_event* event); - bool tlsEnable_ {false}; - std::string tlsMethod_; - std::string tlsCiphers_; - std::string tlsServerName_; - bool tlsVerifyServer_; - bool tlsVerifyClient_; - bool tlsRequireClientCertificate_; - bool tlsDisableSecureDlgCheck_; - std::string tlsNegotiationTimeoutSec_; - - /** - * Specifies the type of key exchange used for SRTP, if any. - * This only determine if the media channel is secured. - */ - KeyExchangeProtocol srtpKeyExchange_ {KeyExchangeProtocol::NONE}; - - /** - * Determine if the softphone should fallback on non secured media channel if SRTP negotiation - * fails. Make sure other SIP endpoints share the same behavior since it could result in - * encrypted data to be played through the audio device. - */ - bool srtpFallback_ {}; - /** * Details about the registration state. * This is a protocol Code:Description pair. */ std::pair<int, std::string> registrationStateDetailed_; - /** - * Enable/disable automatic refresh of the registration. - * If enabled, a new registration request is sent shortly before - * the current registration expires. - */ - bool registrationRefreshEnabled_; - /** * Optional: "received" parameter from VIA header */ @@ -717,7 +602,7 @@ private: /** * Optional: "rport" parameter from VIA header */ - int rPort_; + int rPort_ {-1}; /** * Optional: via_addr construct from received parameters @@ -738,10 +623,7 @@ private: std::string contactHeader_; // Contact address (the address part of a SIP URI) IpAddr contactAddress_ {}; - // If true, the contact addreass and header will be rewritten - // using the information received from the registrar. - bool allowIPAutoRewrite_; - pjsip_transport* via_tp_; + pjsip_transport* via_tp_ {nullptr}; /** * Presence data structure diff --git a/src/sip/sipaccount_config.cpp b/src/sip/sipaccount_config.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ebf03f7459ff58b11a1f4d0454c99ece104b4783 --- /dev/null +++ b/src/sip/sipaccount_config.cpp @@ -0,0 +1,361 @@ +/* + * Copyright (C) 2004-2022 Savoir-faire Linux Inc. + * + * 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, 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 <https://www.gnu.org/licenses/>. + */ +#include "sipaccount_config.h" +#include "account_const.h" +#include "account_schema.h" +#include "config/yamlparser.h" + +extern "C" { +#include <pjlib-util/md5.h> +} + +namespace jami { + +namespace Conf { +constexpr const char* ID_KEY = "id"; +constexpr const char* USERNAME_KEY = "username"; +constexpr const char* BIND_ADDRESS_KEY = "bindAddress"; +constexpr const char* INTERFACE_KEY = "interface"; +constexpr const char* PORT_KEY = "port"; +constexpr const char* PUBLISH_ADDR_KEY = "publishAddr"; +constexpr const char* PUBLISH_PORT_KEY = "publishPort"; +constexpr const char* SAME_AS_LOCAL_KEY = "sameasLocal"; +constexpr const char* DTMF_TYPE_KEY = "dtmfType"; +constexpr const char* SERVICE_ROUTE_KEY = "serviceRoute"; +constexpr const char* ALLOW_IP_AUTO_REWRITE = "allowIPAutoRewrite"; +constexpr const char* PRESENCE_ENABLED_KEY = "presenceEnabled"; +constexpr const char* PRESENCE_PUBLISH_SUPPORTED_KEY = "presencePublishSupported"; +constexpr const char* PRESENCE_SUBSCRIBE_SUPPORTED_KEY = "presenceSubscribeSupported"; +constexpr const char* PRESENCE_STATUS_KEY = "presenceStatus"; +constexpr const char* PRESENCE_NOTE_KEY = "presenceNote"; +constexpr const char* PRESENCE_MODULE_ENABLED_KEY = "presenceModuleEnabled"; +constexpr const char* KEEP_ALIVE_ENABLED = "keepAlive"; + +constexpr const char* const TLS_KEY = "tls"; +constexpr const char* CERTIFICATE_KEY = "certificate"; +constexpr const char* CALIST_KEY = "calist"; +constexpr const char* TLS_PORT_KEY = "tlsPort"; +constexpr const char* CIPHERS_KEY = "ciphers"; +constexpr const char* TLS_ENABLE_KEY = "enable"; +constexpr const char* METHOD_KEY = "method"; +constexpr const char* TIMEOUT_KEY = "timeout"; +constexpr const char* TLS_PASSWORD_KEY = "password"; +constexpr const char* PRIVATE_KEY_KEY = "privateKey"; +constexpr const char* REQUIRE_CERTIF_KEY = "requireCertif"; +constexpr const char* SERVER_KEY = "server"; +constexpr const char* VERIFY_CLIENT_KEY = "verifyClient"; +constexpr const char* VERIFY_SERVER_KEY = "verifyServer"; +constexpr const char* DISABLE_SECURE_DLG_CHECK = "keepAlive"; + +constexpr const char* STUN_ENABLED_KEY = "stunEnabled"; +constexpr const char* STUN_SERVER_KEY = "stunServer"; +constexpr const char* CRED_KEY = "credential"; +constexpr const char* SRTP_KEY = "srtp"; +constexpr const char* SRTP_ENABLE_KEY = "enable"; +constexpr const char* KEY_EXCHANGE_KEY = "keyExchange"; +constexpr const char* RTP_FALLBACK_KEY = "rtpFallback"; +} // namespace Conf + +static constexpr unsigned MIN_REGISTRATION_TIME = 60; // seconds + +using yaml_utils::parseValueOptional; +using yaml_utils::parseVectorMap; + +static void +addRangeToDetails(std::map<std::string, std::string>& a, + const char* minKey, + const char* maxKey, + const std::pair<uint16_t, uint16_t>& range) +{ + a.emplace(minKey, std::to_string(range.first)); + a.emplace(maxKey, std::to_string(range.second)); +} + +void +SipAccountConfig::serialize(YAML::Emitter& out) const +{ + out << YAML::BeginMap; + out << YAML::Key << Conf::ID_KEY << YAML::Value << id; + SipAccountBaseConfig::serialize(out); + + out << YAML::Key << Conf::BIND_ADDRESS_KEY << YAML::Value << bindAddress; + out << YAML::Key << Conf::PORT_KEY << YAML::Value << localPort; + out << YAML::Key << Conf::PUBLISH_PORT_KEY << YAML::Value << publishedPort; + + out << YAML::Key << Conf::USERNAME_KEY << YAML::Value << username; + + // each credential is a map, and we can have multiple credentials + out << YAML::Key << Conf::CRED_KEY << YAML::Value << getCredentials(); + + out << YAML::Key << Conf::KEEP_ALIVE_ENABLED << YAML::Value << registrationRefreshEnabled; + + //out << YAML::Key << PRESENCE_MODULE_ENABLED_KEY << YAML::Value + // << (presence_ and presence_->isEnabled()); + + out << YAML::Key << Conf::CONFIG_ACCOUNT_REGISTRATION_EXPIRE << YAML::Value + << registrationExpire; + out << YAML::Key << Conf::SERVICE_ROUTE_KEY << YAML::Value << serviceRoute; + out << YAML::Key << Conf::ALLOW_IP_AUTO_REWRITE << YAML::Value << allowIPAutoRewrite; + out << YAML::Key << Conf::STUN_ENABLED_KEY << YAML::Value << stunEnabled; + out << YAML::Key << Conf::STUN_SERVER_KEY << YAML::Value << stunServer; + + // tls submap + out << YAML::Key << Conf::TLS_KEY << YAML::Value << YAML::BeginMap; + out << YAML::Key << Conf::CALIST_KEY << YAML::Value << tlsCaListFile; + out << YAML::Key << Conf::CERTIFICATE_KEY << YAML::Value << tlsCertificateFile; + out << YAML::Key << Conf::TLS_PASSWORD_KEY << YAML::Value << tlsPassword; + out << YAML::Key << Conf::PRIVATE_KEY_KEY << YAML::Value << tlsPrivateKeyFile; + out << YAML::Key << Conf::TLS_ENABLE_KEY << YAML::Value << tlsEnable; + out << YAML::Key << Conf::TLS_PORT_KEY << YAML::Value << tlsListenerPort; + out << YAML::Key << Conf::VERIFY_CLIENT_KEY << YAML::Value << tlsVerifyClient; + out << YAML::Key << Conf::VERIFY_SERVER_KEY << YAML::Value << tlsVerifyServer; + out << YAML::Key << Conf::REQUIRE_CERTIF_KEY << YAML::Value << tlsRequireClientCertificate; + out << YAML::Key << Conf::DISABLE_SECURE_DLG_CHECK << YAML::Value << tlsDisableSecureDlgCheck; + out << YAML::Key << Conf::TIMEOUT_KEY << YAML::Value << tlsNegotiationTimeout; + out << YAML::Key << Conf::CIPHERS_KEY << YAML::Value << tlsCiphers; + out << YAML::Key << Conf::METHOD_KEY << YAML::Value << tlsMethod; + out << YAML::Key << Conf::SERVER_KEY << YAML::Value << tlsServerName; + out << YAML::EndMap; + + // srtp submap + out << YAML::Key << Conf::SRTP_KEY << YAML::Value << YAML::BeginMap; + out << YAML::Key << Conf::KEY_EXCHANGE_KEY << YAML::Value + << sip_utils::getKeyExchangeName(srtpKeyExchange); + out << YAML::Key << Conf::RTP_FALLBACK_KEY << YAML::Value << srtpFallback; + out << YAML::EndMap; + + out << YAML::EndMap; +} + +void +SipAccountConfig::unserialize(const YAML::Node& node) +{ + SipAccountBaseConfig::unserialize(node); + parseValueOptional(node, Conf::USERNAME_KEY, username); + parseValueOptional(node, Conf::BIND_ADDRESS_KEY, bindAddress); + parseValueOptional(node, Conf::PORT_KEY, localPort); + parseValueOptional(node, Conf::PUBLISH_PORT_KEY, publishedPort); + parseValueOptional(node, Conf::CONFIG_ACCOUNT_REGISTRATION_EXPIRE, registrationExpire); + registrationExpire = std::max(MIN_REGISTRATION_TIME, registrationExpire); + parseValueOptional(node, Conf::KEEP_ALIVE_ENABLED, registrationRefreshEnabled); + parseValueOptional(node, Conf::SERVICE_ROUTE_KEY, serviceRoute); + parseValueOptional(node, Conf::ALLOW_IP_AUTO_REWRITE, allowIPAutoRewrite); + + parseValueOptional(node, Conf::PRESENCE_MODULE_ENABLED_KEY, presenceEnabled); + parseValueOptional(node, Conf::PRESENCE_PUBLISH_SUPPORTED_KEY, publishSupported); + parseValueOptional(node, Conf::PRESENCE_SUBSCRIBE_SUPPORTED_KEY, subscribeSupported); + + // ICE - STUN/TURN + parseValueOptional(node, Conf::STUN_ENABLED_KEY, stunEnabled); + parseValueOptional(node, Conf::STUN_SERVER_KEY, stunServer); + + const auto& credsNode = node[Conf::CRED_KEY]; + setCredentials(parseVectorMap(credsNode, + {Conf::CONFIG_ACCOUNT_REALM, + Conf::CONFIG_ACCOUNT_USERNAME, + Conf::CONFIG_ACCOUNT_PASSWORD})); + + // get tls submap + try { + const auto& tlsMap = node[Conf::TLS_KEY]; + parseValueOptional(tlsMap, Conf::CERTIFICATE_KEY, tlsCertificateFile); + parseValueOptional(tlsMap, Conf::CALIST_KEY, tlsCaListFile); + parseValueOptional(tlsMap, Conf::TLS_PASSWORD_KEY, tlsPassword); + parseValueOptional(tlsMap, Conf::PRIVATE_KEY_KEY, tlsPrivateKeyFile); + parseValueOptional(tlsMap, Conf::TLS_ENABLE_KEY, tlsEnable); + parseValueOptional(tlsMap, Conf::TLS_PORT_KEY, tlsListenerPort); + parseValueOptional(tlsMap, Conf::CIPHERS_KEY, tlsCiphers); + parseValueOptional(tlsMap, Conf::METHOD_KEY, tlsMethod); + parseValueOptional(tlsMap, Conf::SERVER_KEY, tlsServerName); + parseValueOptional(tlsMap, Conf::REQUIRE_CERTIF_KEY, tlsRequireClientCertificate); + parseValueOptional(tlsMap, Conf::VERIFY_CLIENT_KEY, tlsVerifyClient); + parseValueOptional(tlsMap, Conf::VERIFY_SERVER_KEY, tlsVerifyServer); + parseValueOptional(tlsMap, Conf::DISABLE_SECURE_DLG_CHECK, tlsDisableSecureDlgCheck); + parseValueOptional(tlsMap, Conf::TIMEOUT_KEY, tlsNegotiationTimeout); + } catch (...) {} + + // get srtp submap + const auto& srtpMap = node[Conf::SRTP_KEY]; + std::string tmpKey; + parseValueOptional(srtpMap, Conf::KEY_EXCHANGE_KEY, tmpKey); + srtpKeyExchange = sip_utils::getKeyExchangeProtocol(tmpKey); + parseValueOptional(srtpMap, Conf::RTP_FALLBACK_KEY, srtpFallback); +} + +std::map<std::string, std::string> +SipAccountConfig::toMap() const +{ + auto a = SipAccountBaseConfig::toMap(); + a.emplace(Conf::CONFIG_ACCOUNT_USERNAME, username); + a.emplace(Conf::CONFIG_LOCAL_PORT, std::to_string(localPort)); + a.emplace(Conf::CONFIG_ACCOUNT_DTMF_TYPE, dtmfType); + a.emplace(Conf::CONFIG_LOCAL_INTERFACE, interface); + a.emplace(Conf::CONFIG_PUBLISHED_PORT, std::to_string(publishedPort)); + a.emplace(Conf::CONFIG_PUBLISHED_SAMEAS_LOCAL, publishedSameasLocal ? TRUE_STR : FALSE_STR); + a.emplace(Conf::CONFIG_PUBLISHED_ADDRESS, publishedIp); + a.emplace(Conf::CONFIG_STUN_ENABLE, stunEnabled ? TRUE_STR : FALSE_STR); + a.emplace(Conf::CONFIG_STUN_SERVER, stunServer); + + std::string password {}; + if (not credentials.empty()) { + for (const auto& cred : credentials) + if (cred.username == username) { + password = cred.password; + break; + } + } + a.emplace(Conf::CONFIG_ACCOUNT_PASSWORD, std::move(password)); + + a.emplace(Conf::CONFIG_TLS_ENABLE, tlsEnable ? TRUE_STR : FALSE_STR); + a.emplace(Conf::CONFIG_TLS_LISTENER_PORT, std::to_string(tlsListenerPort)); + a.emplace(Conf::CONFIG_TLS_CA_LIST_FILE, tlsCaListFile); + a.emplace(Conf::CONFIG_TLS_CERTIFICATE_FILE, tlsCertificateFile); + a.emplace(Conf::CONFIG_TLS_PRIVATE_KEY_FILE, tlsPrivateKeyFile); + a.emplace(Conf::CONFIG_TLS_PASSWORD, tlsPassword); + a.emplace(Conf::CONFIG_TLS_METHOD, tlsMethod); + a.emplace(Conf::CONFIG_TLS_CIPHERS, tlsCiphers); + a.emplace(Conf::CONFIG_TLS_SERVER_NAME, tlsServerName); + a.emplace(Conf::CONFIG_TLS_VERIFY_SERVER, tlsVerifyServer ? TRUE_STR : FALSE_STR); + a.emplace(Conf::CONFIG_TLS_VERIFY_CLIENT, tlsVerifyClient ? TRUE_STR : FALSE_STR); + a.emplace(Conf::CONFIG_TLS_REQUIRE_CLIENT_CERTIFICATE, tlsRequireClientCertificate ? TRUE_STR : FALSE_STR); + a.emplace(Conf::CONFIG_TLS_NEGOTIATION_TIMEOUT_SEC, std::to_string(tlsNegotiationTimeout)); + a.emplace(Conf::CONFIG_TLS_DISABLE_SECURE_DLG_CHECK, tlsDisableSecureDlgCheck ? TRUE_STR : FALSE_STR); + return a; +} + +void +SipAccountConfig::fromMap(const std::map<std::string, std::string>& details) +{ + SipAccountBaseConfig::fromMap(details); + + // general sip settings + parseString(details, Conf::CONFIG_ACCOUNT_USERNAME, username); + parseInt(details, Conf::CONFIG_LOCAL_PORT, localPort); + parseString(details, Conf::CONFIG_BIND_ADDRESS, bindAddress); + parseString(details, Conf::CONFIG_ACCOUNT_ROUTESET, serviceRoute); + parseBool(details, Conf::CONFIG_ACCOUNT_IP_AUTO_REWRITE, allowIPAutoRewrite); + parseString(details, Conf::CONFIG_LOCAL_INTERFACE, interface); + parseBool(details, Conf::CONFIG_PUBLISHED_SAMEAS_LOCAL, publishedSameasLocal); + parseString(details, Conf::CONFIG_PUBLISHED_ADDRESS, publishedIp); + parseInt(details, Conf::CONFIG_PUBLISHED_PORT, publishedPort); + parseBool(details, Conf::CONFIG_PRESENCE_ENABLED, presenceEnabled); + parseString(details, Conf::CONFIG_ACCOUNT_DTMF_TYPE, dtmfType); + + int tmpMin = -1; + parseInt(details, Conf::CONFIG_ACCOUNT_AUDIO_PORT_MIN, tmpMin); + int tmpMax = -1; + parseInt(details, Conf::CONFIG_ACCOUNT_AUDIO_PORT_MAX, tmpMax); + updateRange(tmpMin, tmpMax, audioPortRange); + tmpMin = -1; + parseInt(details, Conf::CONFIG_ACCOUNT_VIDEO_PORT_MIN, tmpMin); + tmpMax = -1; + parseInt(details, Conf::CONFIG_ACCOUNT_VIDEO_PORT_MAX, tmpMax); + updateRange(tmpMin, tmpMax, videoPortRange); + + if (credentials.empty()) { // credentials not set, construct 1 entry + JAMI_WARN("No credentials set, inferring them..."); + std::map<std::string, std::string> map; + map[Conf::CONFIG_ACCOUNT_USERNAME] = username; + parseString(details, Conf::CONFIG_ACCOUNT_PASSWORD, map[Conf::CONFIG_ACCOUNT_PASSWORD]); + map[Conf::CONFIG_ACCOUNT_REALM] = "*"; + setCredentials({map}); + } + + // ICE - STUN + parseBool(details, Conf::CONFIG_STUN_ENABLE, stunEnabled); + parseString(details, Conf::CONFIG_STUN_SERVER, stunServer); + + // TLS + parseBool(details, Conf::CONFIG_TLS_ENABLE, tlsEnable); + parseInt(details, Conf::CONFIG_TLS_LISTENER_PORT, tlsListenerPort); + parsePath(details, Conf::CONFIG_TLS_CA_LIST_FILE, tlsCaListFile, path); + parsePath(details, Conf::CONFIG_TLS_CERTIFICATE_FILE, tlsCertificateFile, path); + parsePath(details, Conf::CONFIG_TLS_PRIVATE_KEY_FILE, tlsPrivateKeyFile, path); + parseString(details, Conf::CONFIG_TLS_PASSWORD, tlsPassword); + parseString(details, Conf::CONFIG_TLS_METHOD, tlsMethod); + parseString(details, Conf::CONFIG_TLS_CIPHERS, tlsCiphers); + parseString(details, Conf::CONFIG_TLS_SERVER_NAME, tlsServerName); + parseBool(details, Conf::CONFIG_TLS_VERIFY_SERVER, tlsVerifyServer); + parseBool(details, Conf::CONFIG_TLS_VERIFY_CLIENT, tlsVerifyClient); + parseBool(details, Conf::CONFIG_TLS_REQUIRE_CLIENT_CERTIFICATE, tlsRequireClientCertificate); + parseBool(details, Conf::CONFIG_TLS_DISABLE_SECURE_DLG_CHECK, tlsDisableSecureDlgCheck); + parseInt(details, Conf::CONFIG_TLS_NEGOTIATION_TIMEOUT_SEC, tlsNegotiationTimeout); +} + +SipAccountConfig::Credentials::Credentials(const std::map<std::string, std::string>& cred) +{ + auto itrealm = cred.find(Conf::CONFIG_ACCOUNT_REALM); + auto user = cred.find(Conf::CONFIG_ACCOUNT_USERNAME); + auto passw = cred.find(Conf::CONFIG_ACCOUNT_PASSWORD); + realm = itrealm != cred.end() ? itrealm->second : ""; + username = user != cred.end() ? user->second : ""; + password = passw != cred.end() ? passw->second : ""; + computePasswordHash(); +} + +std::map<std::string, std::string> +SipAccountConfig::Credentials::toMap() const +{ + return {{Conf::CONFIG_ACCOUNT_REALM, realm}, + {Conf::CONFIG_ACCOUNT_USERNAME, username}, + {Conf::CONFIG_ACCOUNT_PASSWORD, password}}; +} + +void +SipAccountConfig::Credentials::computePasswordHash() { + pj_md5_context pms; + + /* Compute md5 hash = MD5(username ":" realm ":" password) */ + pj_md5_init(&pms); + pj_md5_update(&pms, (const uint8_t*) username.data(), username.length()); + pj_md5_update(&pms, (const uint8_t*) ":", 1); + pj_md5_update(&pms, (const uint8_t*) realm.data(), realm.length()); + pj_md5_update(&pms, (const uint8_t*) ":", 1); + pj_md5_update(&pms, (const uint8_t*) password.data(), password.length()); + + unsigned char digest[16]; + pj_md5_final(&pms, digest); + + char hash[32]; + + for (int i = 0; i < 16; ++i) + pj_val_to_hex_digit(digest[i], &hash[2 * i]); + + password_h = {hash, 32}; +} + +std::vector<std::map<std::string, std::string>> +SipAccountConfig::getCredentials() const +{ + std::vector<std::map<std::string, std::string>> ret; + ret.reserve(credentials.size()); + for (const auto& c : credentials) { + ret.emplace_back(c.toMap()); + } + return ret; +} + +void +SipAccountConfig::setCredentials(const std::vector<std::map<std::string, std::string>>& creds) +{ + credentials.clear(); + credentials.reserve(creds.size()); + for (const auto& cred : creds) + credentials.emplace_back(cred); +} + +} diff --git a/src/sip/sipaccount_config.h b/src/sip/sipaccount_config.h new file mode 100644 index 0000000000000000000000000000000000000000..b708e002cc724b8856ec013edc5089ddbe47eb0c --- /dev/null +++ b/src/sip/sipaccount_config.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2004-2022 Savoir-faire Linux Inc. + * + * 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, 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 <https://www.gnu.org/licenses/>. + */ +#pragma once +#include "sipaccountbase_config.h" + +namespace jami { +constexpr static const char* ACCOUNT_TYPE_SIP = "SIP"; + +struct SipAccountConfig : public SipAccountBaseConfig { + SipAccountConfig(const std::string& id = {}, const std::string& path = {}): SipAccountBaseConfig(ACCOUNT_TYPE_SIP, id, path) {} + void serialize(YAML::Emitter& out) const override; + void unserialize(const YAML::Node& node) override; + std::map<std::string, std::string> toMap() const override; + void fromMap(const std::map<std::string, std::string>&) override; + + /** + * Local port to whih this account is bound + */ + uint16_t localPort {sip_utils::DEFAULT_SIP_PORT}; + + /** + * Potential ip addresss on which this account is bound + */ + std::string bindAddress {}; + + /** + * Published port, used only if defined by the user + */ + uint16_t publishedPort {sip_utils::DEFAULT_SIP_PORT}; + + /** + * interface name on which this account is bound + */ + std::string interface; + + /** + * Determine if STUN public address resolution is required to register this account. In this + * case a STUN server hostname must be specified. + */ + bool stunEnabled {false}; + + /** + * The STUN server hostname (optional), used to provide the public IP address in case the + * softphone stay behind a NAT. + */ + std::string stunServer {}; + + /** + * Network settings + */ + unsigned registrationExpire {3600}; + bool registrationRefreshEnabled {true}; + + // If true, the contact addreass and header will be rewritten + // using the information received from the registrar. + bool allowIPAutoRewrite {true}; + + /** + * Input Outbound Proxy Server Address + */ + std::string serviceRoute; + + /** + * The TLS listener port + */ + uint16_t tlsListenerPort {sip_utils::DEFAULT_SIP_TLS_PORT}; + bool tlsEnable {false}; + std::string tlsMethod; + std::string tlsCiphers; + std::string tlsServerName; + bool tlsVerifyServer {true}; + bool tlsVerifyClient {true}; + bool tlsRequireClientCertificate {true}; + bool tlsDisableSecureDlgCheck {true}; + int tlsNegotiationTimeout {2}; + + /** + * Determine if the softphone should fallback on non secured media channel if SRTP negotiation + * fails. Make sure other SIP endpoints share the same behavior since it could result in + * encrypted data to be played through the audio device. + */ + bool srtpFallback {false}; + /** + * Specifies the type of key exchange used for SRTP, if any. + * This only determine if the media channel is secured. + */ + KeyExchangeProtocol srtpKeyExchange {KeyExchangeProtocol::NONE}; + + bool presenceEnabled {false}; + bool publishSupported {false}; + bool subscribeSupported {false}; + + /** + * Map of credential for this account + */ + struct Credentials + { + std::string realm {}; + std::string username {}; + std::string password {}; + std::string password_h {}; + Credentials(const std::string& r, const std::string& u, const std::string& p) + : realm(r) + , username(u) + , password(p) + {} + Credentials(const std::map<std::string, std::string>& r); + std::map<std::string, std::string> toMap() const; + void computePasswordHash(); + }; + std::vector<Credentials> credentials; + std::vector<std::map<std::string, std::string>> getCredentials() const; + void setCredentials(const std::vector<std::map<std::string, std::string>>& creds); +}; + +} diff --git a/src/sip/sipaccountbase.cpp b/src/sip/sipaccountbase.cpp index 114d3a40b84d159ecf837a25c6dd4a4be7456e30..57e29c959de0f818f573cdf0fa5595636b719c59 100644 --- a/src/sip/sipaccountbase.cpp +++ b/src/sip/sipaccountbase.cpp @@ -128,203 +128,18 @@ SIPAccountBase::flush() { // Class base method Account::flush(); - fileutils::remove(fileutils::get_cache_dir() + DIR_SEPARATOR_STR + getAccountID() + DIR_SEPARATOR_STR "messages"); } -template<typename T> -static void -validate(std::string& member, const std::string& param, const T& valid) -{ - const auto begin = std::begin(valid); - const auto end = std::end(valid); - if (find(begin, end, param) != end) - member = param; - else - JAMI_ERR("Invalid parameter \"%s\"", param.c_str()); -} - -static void -updateRange(uint16_t min, uint16_t max, std::pair<uint16_t, uint16_t>& range) -{ - if (min > 0 and (max > min) and max <= SIPAccountBase::MAX_PORT - 2) { - range.first = min; - range.second = max; - } -} - -static void -unserializeRange(const YAML::Node& node, - const char* minKey, - const char* maxKey, - std::pair<uint16_t, uint16_t>& range) -{ - int tmpMin = 0; - int tmpMax = 0; - yaml_utils::parseValue(node, minKey, tmpMin); - yaml_utils::parseValue(node, maxKey, tmpMax); - updateRange(tmpMin, tmpMax, range); -} - -static void -addRangeToDetails(std::map<std::string, std::string>& a, - const char* minKey, - const char* maxKey, - const std::pair<uint16_t, uint16_t>& range) -{ - a.emplace(minKey, std::to_string(range.first)); - a.emplace(maxKey, std::to_string(range.second)); -} - void -SIPAccountBase::serialize(YAML::Emitter& out) const +SIPAccountBase::loadConfig() { - Account::serialize(out); - - out << YAML::Key << Conf::AUDIO_PORT_MAX_KEY << YAML::Value << audioPortRange_.second; - out << YAML::Key << Conf::AUDIO_PORT_MIN_KEY << YAML::Value << audioPortRange_.first; - out << YAML::Key << Conf::DTMF_TYPE_KEY << YAML::Value << dtmfType_; - out << YAML::Key << Conf::INTERFACE_KEY << YAML::Value << interface_; - out << YAML::Key << Conf::PUBLISH_ADDR_KEY << YAML::Value << publishedIpAddress_; - out << YAML::Key << Conf::PUBLISH_PORT_KEY << YAML::Value << publishedPort_; - out << YAML::Key << Conf::SAME_AS_LOCAL_KEY << YAML::Value << publishedSameasLocal_; - - out << YAML::Key << VIDEO_ENABLED_KEY << YAML::Value << videoEnabled_; - out << YAML::Key << Conf::VIDEO_PORT_MAX_KEY << YAML::Value << videoPortRange_.second; - out << YAML::Key << Conf::VIDEO_PORT_MIN_KEY << YAML::Value << videoPortRange_.first; - - out << YAML::Key << Conf::STUN_ENABLED_KEY << YAML::Value << stunEnabled_; - out << YAML::Key << Conf::STUN_SERVER_KEY << YAML::Value << stunServer_; - out << YAML::Key << Conf::TURN_ENABLED_KEY << YAML::Value << turnEnabled_; - out << YAML::Key << Conf::TURN_SERVER_KEY << YAML::Value << turnServer_; - out << YAML::Key << Conf::TURN_SERVER_UNAME_KEY << YAML::Value << turnServerUserName_; - out << YAML::Key << Conf::TURN_SERVER_PWD_KEY << YAML::Value << turnServerPwd_; - out << YAML::Key << Conf::TURN_SERVER_REALM_KEY << YAML::Value << turnServerRealm_; -} - -void -SIPAccountBase::serializeTls(YAML::Emitter& out) const -{ - out << YAML::Key << Conf::CALIST_KEY << YAML::Value << tlsCaListFile_; - out << YAML::Key << Conf::CERTIFICATE_KEY << YAML::Value << tlsCertificateFile_; - out << YAML::Key << Conf::TLS_PASSWORD_KEY << YAML::Value << tlsPassword_; - out << YAML::Key << Conf::PRIVATE_KEY_KEY << YAML::Value << tlsPrivateKeyFile_; -} - -void -SIPAccountBase::unserialize(const YAML::Node& node) -{ - using yaml_utils::parseValue; - using yaml_utils::parseValueOptional; - using yaml_utils::parseVectorMap; - - Account::unserialize(node); - - parseValue(node, VIDEO_ENABLED_KEY, videoEnabled_); - - parseValue(node, Conf::INTERFACE_KEY, interface_); - parseValue(node, Conf::SAME_AS_LOCAL_KEY, publishedSameasLocal_); - parseValue(node, Conf::PUBLISH_ADDR_KEY, publishedIpAddress_); - IpAddr publishedIp {publishedIpAddress_}; - if (publishedIp and not publishedSameasLocal_) + Account::loadConfig(); + const auto& conf = config(); + IpAddr publishedIp {conf.publishedIp}; + if (not conf.publishedSameasLocal and publishedIp) setPublishedAddress(publishedIp); - - int port = sip_utils::DEFAULT_SIP_PORT; - parseValue(node, Conf::PUBLISH_PORT_KEY, port); - publishedPort_ = port; - - parseValue(node, Conf::DTMF_TYPE_KEY, dtmfType_); - - unserializeRange(node, Conf::AUDIO_PORT_MIN_KEY, Conf::AUDIO_PORT_MAX_KEY, audioPortRange_); - unserializeRange(node, Conf::VIDEO_PORT_MIN_KEY, Conf::VIDEO_PORT_MAX_KEY, videoPortRange_); - - // ICE - STUN/TURN - if (not isIP2IP()) { - parseValue(node, Conf::STUN_ENABLED_KEY, stunEnabled_); - parseValue(node, Conf::STUN_SERVER_KEY, stunServer_); - parseValue(node, Conf::TURN_ENABLED_KEY, turnEnabled_); - parseValue(node, Conf::TURN_SERVER_KEY, turnServer_); - parseValue(node, Conf::TURN_SERVER_UNAME_KEY, turnServerUserName_); - parseValue(node, Conf::TURN_SERVER_PWD_KEY, turnServerPwd_); - parseValue(node, Conf::TURN_SERVER_REALM_KEY, turnServerRealm_); - } -} - -void -SIPAccountBase::setAccountDetails(const std::map<std::string, std::string>& details) -{ - Account::setAccountDetails(details); - - parseBool(details, Conf::CONFIG_VIDEO_ENABLED, videoEnabled_); - - // general sip settings - parseString(details, Conf::CONFIG_LOCAL_INTERFACE, interface_); - parseBool(details, Conf::CONFIG_PUBLISHED_SAMEAS_LOCAL, publishedSameasLocal_); - parseString(details, Conf::CONFIG_PUBLISHED_ADDRESS, publishedIpAddress_); - parseInt(details, Conf::CONFIG_PUBLISHED_PORT, publishedPort_); - IpAddr publishedIp {publishedIpAddress_}; - if (publishedIp and not publishedSameasLocal_) - setPublishedAddress(publishedIp); - - parseString(details, Conf::CONFIG_ACCOUNT_DTMF_TYPE, dtmfType_); - - int tmpMin = -1; - parseInt(details, Conf::CONFIG_ACCOUNT_AUDIO_PORT_MIN, tmpMin); - int tmpMax = -1; - parseInt(details, Conf::CONFIG_ACCOUNT_AUDIO_PORT_MAX, tmpMax); - updateRange(tmpMin, tmpMax, audioPortRange_); -#ifdef ENABLE_VIDEO - tmpMin = -1; - parseInt(details, Conf::CONFIG_ACCOUNT_VIDEO_PORT_MIN, tmpMin); - tmpMax = -1; - parseInt(details, Conf::CONFIG_ACCOUNT_VIDEO_PORT_MAX, tmpMax); - updateRange(tmpMin, tmpMax, videoPortRange_); -#endif - - // ICE - STUN - parseBool(details, Conf::CONFIG_STUN_ENABLE, stunEnabled_); - parseString(details, Conf::CONFIG_STUN_SERVER, stunServer_); - - // ICE - TURN - parseBool(details, Conf::CONFIG_TURN_ENABLE, turnEnabled_); - parseString(details, Conf::CONFIG_TURN_SERVER, turnServer_); - parseString(details, Conf::CONFIG_TURN_SERVER_UNAME, turnServerUserName_); - parseString(details, Conf::CONFIG_TURN_SERVER_PWD, turnServerPwd_); - parseString(details, Conf::CONFIG_TURN_SERVER_REALM, turnServerRealm_); -} - -std::map<std::string, std::string> -SIPAccountBase::getAccountDetails() const -{ - auto a = Account::getAccountDetails(); - a.emplace(Conf::CONFIG_VIDEO_ENABLED, videoEnabled_ ? TRUE_STR : FALSE_STR); - - addRangeToDetails(a, - Conf::CONFIG_ACCOUNT_AUDIO_PORT_MIN, - Conf::CONFIG_ACCOUNT_AUDIO_PORT_MAX, - audioPortRange_); -#ifdef ENABLE_VIDEO - addRangeToDetails(a, - Conf::CONFIG_ACCOUNT_VIDEO_PORT_MIN, - Conf::CONFIG_ACCOUNT_VIDEO_PORT_MAX, - videoPortRange_); -#endif - - a.emplace(Conf::CONFIG_ACCOUNT_DTMF_TYPE, dtmfType_); - a.emplace(Conf::CONFIG_LOCAL_INTERFACE, interface_); - a.emplace(Conf::CONFIG_PUBLISHED_PORT, std::to_string(publishedPort_)); - a.emplace(Conf::CONFIG_PUBLISHED_SAMEAS_LOCAL, publishedSameasLocal_ ? TRUE_STR : FALSE_STR); - a.emplace(Conf::CONFIG_PUBLISHED_ADDRESS, publishedIpAddress_); - a.emplace(Conf::CONFIG_STUN_ENABLE, stunEnabled_ ? TRUE_STR : FALSE_STR); - a.emplace(Conf::CONFIG_STUN_SERVER, stunServer_); - a.emplace(Conf::CONFIG_TURN_ENABLE, turnEnabled_ ? TRUE_STR : FALSE_STR); - a.emplace(Conf::CONFIG_TURN_SERVER, turnServer_); - a.emplace(Conf::CONFIG_TURN_SERVER_UNAME, turnServerUserName_); - a.emplace(Conf::CONFIG_TURN_SERVER_PWD, turnServerPwd_); - a.emplace(Conf::CONFIG_TURN_SERVER_REALM, turnServerRealm_); - - return a; } std::map<std::string, std::string> @@ -404,14 +219,14 @@ SIPAccountBase::releasePort(uint16_t port) noexcept uint16_t SIPAccountBase::generateAudioPort() const { - return acquireRandomEvenPort(audioPortRange_); + return acquireRandomEvenPort(config().audioPortRange); } #ifdef ENABLE_VIDEO uint16_t SIPAccountBase::generateVideoPort() const { - return acquireRandomEvenPort(videoPortRange_); + return acquireRandomEvenPort(config().videoPortRange); } #endif @@ -421,18 +236,18 @@ SIPAccountBase::getIceOptions() const noexcept IceTransportOptions opts; opts.upnpEnable = getUPnPActive(); - if (stunEnabled_) - opts.stunServers.emplace_back(StunServerInfo().setUri(stunServer_)); - if (turnEnabled_) { + //if (config().stunEnabled) + // opts.stunServers.emplace_back(StunServerInfo().setUri(stunServer_)); + if (config().turnEnabled) { auto cached = false; std::lock_guard<std::mutex> lk(cachedTurnMutex_); cached = cacheTurnV4_ || cacheTurnV6_; if (cacheTurnV4_ && *cacheTurnV4_) { opts.turnServers.emplace_back(TurnServerInfo() .setUri(cacheTurnV4_->toString(true)) - .setUsername(turnServerUserName_) - .setPassword(turnServerPwd_) - .setRealm(turnServerRealm_)); + .setUsername(config().turnServerUserName) + .setPassword(config().turnServerPwd) + .setRealm(config().turnServerRealm)); } // NOTE: first test with ipv6 turn was not concluant and resulted in multiple // co issues. So this needs some debug. for now just disable @@ -446,10 +261,10 @@ SIPAccountBase::getIceOptions() const noexcept // Nothing cached, so do the resolution if (!cached) { opts.turnServers.emplace_back(TurnServerInfo() - .setUri(turnServer_) - .setUsername(turnServerUserName_) - .setPassword(turnServerPwd_) - .setRealm(turnServerRealm_)); + .setUri(config().turnServer) + .setUsername(config().turnServerUserName) + .setPassword(config().turnServerPwd) + .setRealm(config().turnServerRealm)); } } return opts; diff --git a/src/sip/sipaccountbase.h b/src/sip/sipaccountbase.h index 011360606be58cc02337519d33d3f3367984b47d..a660d8266400e11f30886f2a90f284bb81fc39a4 100644 --- a/src/sip/sipaccountbase.h +++ b/src/sip/sipaccountbase.h @@ -30,6 +30,7 @@ #include "connectivity/ip_utils.h" #include "noncopyable.h" #include "im/message_engine.h" +#include "sipaccountbase_config.h" #include <array> #include <deque> @@ -58,54 +59,6 @@ namespace jami { class SipTransport; class Task; -namespace Conf { -// SIP specific configuration keys -const char* const BIND_ADDRESS_KEY = "bindAddress"; -const char* const INTERFACE_KEY = "interface"; -const char* const PORT_KEY = "port"; -const char* const PUBLISH_ADDR_KEY = "publishAddr"; -const char* const PUBLISH_PORT_KEY = "publishPort"; -const char* const SAME_AS_LOCAL_KEY = "sameasLocal"; -const char* const DTMF_TYPE_KEY = "dtmfType"; -const char* const SERVICE_ROUTE_KEY = "serviceRoute"; -const char* const ALLOW_IP_AUTO_REWRITE = "allowIPAutoRewrite"; -const char* const PRESENCE_ENABLED_KEY = "presenceEnabled"; -const char* const PRESENCE_PUBLISH_SUPPORTED_KEY = "presencePublishSupported"; -const char* const PRESENCE_SUBSCRIBE_SUPPORTED_KEY = "presenceSubscribeSupported"; -const char* const PRESENCE_STATUS_KEY = "presenceStatus"; -const char* const PRESENCE_NOTE_KEY = "presenceNote"; - -// TODO: write an object to store tls params which implement serializable -const char* const TLS_KEY = "tls"; -const char* const TLS_PORT_KEY = "tlsPort"; -const char* const CERTIFICATE_KEY = "certificate"; -const char* const CALIST_KEY = "calist"; -const char* const CIPHERS_KEY = "ciphers"; -const char* const TLS_ENABLE_KEY = "enable"; -const char* const METHOD_KEY = "method"; -const char* const TIMEOUT_KEY = "timeout"; -const char* const TLS_PASSWORD_KEY = "password"; -const char* const PRIVATE_KEY_KEY = "privateKey"; -const char* const REQUIRE_CERTIF_KEY = "requireCertif"; -const char* const DISABLE_SECURE_DLG_CHECK = "disableSecureDlgCheck"; -const char* const SERVER_KEY = "server"; -const char* const VERIFY_CLIENT_KEY = "verifyClient"; -const char* const VERIFY_SERVER_KEY = "verifyServer"; - -const char* const STUN_ENABLED_KEY = "stunEnabled"; -const char* const STUN_SERVER_KEY = "stunServer"; -const char* const TURN_ENABLED_KEY = "turnEnabled"; -const char* const TURN_SERVER_KEY = "turnServer"; -const char* const TURN_SERVER_UNAME_KEY = "turnServerUserName"; -const char* const TURN_SERVER_PWD_KEY = "turnServerPassword"; -const char* const TURN_SERVER_REALM_KEY = "turnServerRealm"; -const char* const CRED_KEY = "credential"; -const char* const AUDIO_PORT_MIN_KEY = "audioPortMin"; -const char* const AUDIO_PORT_MAX_KEY = "audioPortMax"; -const char* const VIDEO_PORT_MIN_KEY = "videoPortMin"; -const char* const VIDEO_PORT_MAX_KEY = "videoPortMax"; -} // namespace Conf - typedef std::vector<pj_ssl_cipher> CipherArray; class SIPVoIPLink; @@ -121,8 +74,6 @@ enum class MatchRank { NONE, PARTIAL, FULL }; class SIPAccountBase : public Account { public: - constexpr static const char* const OVERRTP_STR = "overrtp"; - constexpr static const char* const SIPINFO_STR = "sipinfo"; constexpr static unsigned MAX_PORT {65536}; constexpr static unsigned HALF_MAX_PORT {MAX_PORT / 2}; @@ -134,6 +85,12 @@ public: virtual ~SIPAccountBase() noexcept; + const SipAccountBaseConfig& config() const { + return *static_cast<const SipAccountBaseConfig*>(&Account::config()); + } + + void loadConfig() override; + /** * Create incoming SIPCall. * @param[in] from The origin of the call @@ -148,11 +105,9 @@ public: virtual bool isStunEnabled() const { return false; } - virtual pj_str_t getStunServerName() const { return pj_str_t {nullptr, 0}; }; - virtual pj_uint16_t getStunPort() const { return 0; }; - virtual std::string getDtmfType() const { return dtmfType_; } + virtual std::string getDtmfType() const { return config().dtmfType; } /** * Determine if TLS is enabled for this account. TLS provides a secured channel for @@ -176,7 +131,7 @@ public: /** * Get the local interface name on which this account is bound. */ - const std::string& getLocalInterface() const { return interface_; } + const std::string& getLocalInterface() const { return config().interface; } /** * Get the public IP address set by the user for this account. @@ -184,32 +139,18 @@ public: * will be used. * @return std::string The public IPv4 or IPv6 address formatted in standard notation. */ - std::string getPublishedAddress() const { return publishedIpAddress_; } + std::string getPublishedAddress() const { return config().publishedIp; } IpAddr getPublishedIpAddress(uint16_t family = PF_UNSPEC) const; void setPublishedAddress(const IpAddr& ip_addr); - /** - * Get the published port, which is the port to be advertised as the port - * for the chosen SIP transport. - * @return pj_uint16 The port used for that account - */ - pj_uint16_t getPublishedPort() const { return (pj_uint16_t) publishedPort_; } - - /** - * Set the published port, which is the port to be advertised as the port - * for the chosen SIP transport. - * @pram port The port used by this account. - */ - void setPublishedPort(pj_uint16_t port) { publishedPort_ = port; } - /** * Get a flag which determine the usage in sip headers of either the local * IP address and port (_localAddress and localPort_) or to an address set * manually (_publishedAddress and publishedPort_). */ - bool getPublishedSameasLocal() const { return publishedSameasLocal_; } + bool getPublishedSameasLocal() const { return config().publishedSameasLocal; } virtual bool isSrtpEnabled() const = 0; @@ -229,21 +170,6 @@ public: #endif static void releasePort(uint16_t port) noexcept; - /** - * @return pj_str_t , filled from the configuration - * file, that can be used directly by PJSIP to initialize - * an alternate UDP transport. - */ - std::string getStunServer() const - { - return stunServer_; - } - - void setStunServer(const std::string& srv) - { - stunServer_ = srv; - } - IceTransportOptions getIceOptions() const noexcept; virtual void sendMessage(const std::string& to, @@ -306,14 +232,6 @@ public: // overloaded methods } protected: - virtual void serialize(YAML::Emitter& out) const override; - virtual void serializeTls(YAML::Emitter& out) const; - virtual void unserialize(const YAML::Node& node) override; - - virtual void setAccountDetails(const std::map<std::string, std::string>& details) override; - - virtual std::map<std::string, std::string> getAccountDetails() const override; - /** * Retrieve volatile details such as recent registration errors * @return std::map< std::string, std::string > The account volatile details @@ -331,17 +249,6 @@ protected: */ SIPVoIPLink& link_; - /** - * interface name on which this account is bound - */ - std::string interface_ {"default"}; - - /** - * Flag which determine if localIpAddress_ or publishedIpAddress_ is used in - * sip headers - */ - bool publishedSameasLocal_ {true}; - /** * Published IPv4/IPv6 addresses, used only if defined by the user in account * configuration @@ -349,64 +256,9 @@ protected: */ IpAddr publishedIp_[2] {}; - // This will be stored in the configuration - std::string publishedIpAddress_ {}; - - /** - * Published port, used only if defined by the user - */ - pj_uint16_t publishedPort_ {sip_utils::DEFAULT_SIP_PORT}; - - /** - * Determine if STUN public address resolution is required to register this account. In this - * case a STUN server hostname must be specified. - */ - bool stunEnabled_ {false}; - - /** - * The STUN server hostname (optional), used to provide the public IP address in case the - * softphone stay behind a NAT. - */ - std::string stunServer_ {}; - - /** - * Determine if TURN public address resolution is required to register this account. In this - * case a TURN server hostname must be specified. - */ - bool turnEnabled_ {false}; - - /** - * The TURN server hostname (optional), used to provide the public IP address in case the - * softphone stay behind a NAT. - */ - std::string turnServer_; - std::string turnServerUserName_; - std::string turnServerPwd_; - std::string turnServerRealm_; - - std::string tlsCaListFile_; - std::string tlsCertificateFile_; - std::string tlsPrivateKeyFile_; - std::string tlsPassword_; - - /** - * DTMF type used for this account SIPINFO or RTP - */ - std::string dtmfType_ {OVERRTP_STR}; - pj_status_t transportStatus_ {PJSIP_SC_TRYING}; std::string transportError_ {}; - /* - * Port range for audio RTP ports - */ - std::pair<uint16_t, uint16_t> audioPortRange_ {16384, 32766}; - - /** - * Port range for video RTP ports - */ - std::pair<uint16_t, uint16_t> videoPortRange_ {49152, (MAX_PORT) -2}; - static std::array<bool, HALF_MAX_PORT>& getPortsReservation() noexcept; static uint16_t acquirePort(uint16_t port); uint16_t getRandomEvenPort(const std::pair<uint16_t, uint16_t>& range) const; diff --git a/src/sip/sipaccountbase_config.cpp b/src/sip/sipaccountbase_config.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a8a668b0ba297450b3a1442764899e7b65ec2c38 --- /dev/null +++ b/src/sip/sipaccountbase_config.cpp @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2004-2022 Savoir-faire Linux Inc. + * + * 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, 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 <https://www.gnu.org/licenses/>. + */ +#include "sipaccountbase_config.h" +#include "account_const.h" +#include "account_schema.h" +#include "config/yamlparser.h" + +namespace jami { + +namespace Conf { +// SIP specific configuration keys +const char* const BIND_ADDRESS_KEY = "bindAddress"; +const char* const INTERFACE_KEY = "interface"; +const char* const PORT_KEY = "port"; +const char* const PUBLISH_ADDR_KEY = "publishAddr"; +const char* const PUBLISH_PORT_KEY = "publishPort"; +const char* const SAME_AS_LOCAL_KEY = "sameasLocal"; +const char* const DTMF_TYPE_KEY = "dtmfType"; +const char* const SERVICE_ROUTE_KEY = "serviceRoute"; +const char* const ALLOW_IP_AUTO_REWRITE = "allowIPAutoRewrite"; +const char* const PRESENCE_ENABLED_KEY = "presenceEnabled"; +const char* const PRESENCE_PUBLISH_SUPPORTED_KEY = "presencePublishSupported"; +const char* const PRESENCE_SUBSCRIBE_SUPPORTED_KEY = "presenceSubscribeSupported"; +const char* const PRESENCE_STATUS_KEY = "presenceStatus"; +const char* const PRESENCE_NOTE_KEY = "presenceNote"; +const char* const STUN_ENABLED_KEY = "stunEnabled"; +const char* const STUN_SERVER_KEY = "stunServer"; +const char* const TURN_ENABLED_KEY = "turnEnabled"; +const char* const TURN_SERVER_KEY = "turnServer"; +const char* const TURN_SERVER_UNAME_KEY = "turnServerUserName"; +const char* const TURN_SERVER_PWD_KEY = "turnServerPassword"; +const char* const TURN_SERVER_REALM_KEY = "turnServerRealm"; +const char* const CRED_KEY = "credential"; +const char* const AUDIO_PORT_MIN_KEY = "audioPortMin"; +const char* const AUDIO_PORT_MAX_KEY = "audioPortMax"; +const char* const VIDEO_PORT_MIN_KEY = "videoPortMin"; +const char* const VIDEO_PORT_MAX_KEY = "videoPortMax"; +} // namespace Conf + +using yaml_utils::parseValueOptional; + +static void +unserializeRange(const YAML::Node& node, + const char* minKey, + const char* maxKey, + std::pair<uint16_t, uint16_t>& range) +{ + int tmpMin = yaml_utils::parseValueOptional(node, minKey, tmpMin); + int tmpMax = yaml_utils::parseValueOptional(node, maxKey, tmpMax); + updateRange(tmpMin, tmpMax, range); +} + +static void +addRangeToDetails(std::map<std::string, std::string>& a, + const char* minKey, + const char* maxKey, + const std::pair<uint16_t, uint16_t>& range) +{ + a.emplace(minKey, std::to_string(range.first)); + a.emplace(maxKey, std::to_string(range.second)); +} + +void +SipAccountBaseConfig::serialize(YAML::Emitter& out) const +{ + AccountConfig::serialize(out); + out << YAML::Key << Conf::AUDIO_PORT_MAX_KEY << YAML::Value << audioPortRange.second; + out << YAML::Key << Conf::AUDIO_PORT_MIN_KEY << YAML::Value << audioPortRange.first; + out << YAML::Key << Conf::DTMF_TYPE_KEY << YAML::Value << dtmfType; + out << YAML::Key << Conf::INTERFACE_KEY << YAML::Value << interface; + out << YAML::Key << Conf::PUBLISH_ADDR_KEY << YAML::Value << publishedIp; + //out << YAML::Key << Conf::PUBLISH_PORT_KEY << YAML::Value << publishedPort; + out << YAML::Key << Conf::SAME_AS_LOCAL_KEY << YAML::Value << publishedSameasLocal; + + out << YAML::Key << Conf::VIDEO_PORT_MAX_KEY << YAML::Value << videoPortRange.second; + out << YAML::Key << Conf::VIDEO_PORT_MIN_KEY << YAML::Value << videoPortRange.first; + + out << YAML::Key << Conf::TURN_ENABLED_KEY << YAML::Value << turnEnabled; + out << YAML::Key << Conf::TURN_SERVER_KEY << YAML::Value << turnServer; + out << YAML::Key << Conf::TURN_SERVER_UNAME_KEY << YAML::Value << turnServerUserName; + out << YAML::Key << Conf::TURN_SERVER_PWD_KEY << YAML::Value << turnServerPwd; + out << YAML::Key << Conf::TURN_SERVER_REALM_KEY << YAML::Value << turnServerRealm; + + /*out << YAML::Key << Conf::CALIST_KEY << YAML::Value << tlsCaListFile; + out << YAML::Key << Conf::CERTIFICATE_KEY << YAML::Value << tlsCertificateFile; + out << YAML::Key << Conf::TLS_PASSWORD_KEY << YAML::Value << tlsPassword; + out << YAML::Key << Conf::PRIVATE_KEY_KEY << YAML::Value << tlsPrivateKeyFile;*/ +} + + +void +SipAccountBaseConfig::unserialize(const YAML::Node& node) +{ + AccountConfig::unserialize(node); + parseValueOptional(node, Conf::INTERFACE_KEY, interface); + parseValueOptional(node, Conf::SAME_AS_LOCAL_KEY, publishedSameasLocal); + parseValueOptional(node, Conf::PUBLISH_ADDR_KEY, publishedIp); + parseValueOptional(node, Conf::DTMF_TYPE_KEY, dtmfType); + unserializeRange(node, Conf::AUDIO_PORT_MIN_KEY, Conf::AUDIO_PORT_MAX_KEY, audioPortRange); + unserializeRange(node, Conf::VIDEO_PORT_MIN_KEY, Conf::VIDEO_PORT_MAX_KEY, videoPortRange); + + // ICE - STUN/TURN + //parseValueOptional(node, Conf::STUN_ENABLED_KEY, stunEnabled); + //parseValueOptional(node, Conf::STUN_SERVER_KEY, stunServer); + parseValueOptional(node, Conf::TURN_ENABLED_KEY, turnEnabled); + parseValueOptional(node, Conf::TURN_SERVER_KEY, turnServer); + parseValueOptional(node, Conf::TURN_SERVER_UNAME_KEY, turnServerUserName); + parseValueOptional(node, Conf::TURN_SERVER_PWD_KEY, turnServerPwd); + parseValueOptional(node, Conf::TURN_SERVER_REALM_KEY, turnServerRealm); +} + +std::map<std::string, std::string> +SipAccountBaseConfig::toMap() const +{ + auto a = AccountConfig::toMap(); + + addRangeToDetails(a, + Conf::CONFIG_ACCOUNT_AUDIO_PORT_MIN, + Conf::CONFIG_ACCOUNT_AUDIO_PORT_MAX, + audioPortRange); + addRangeToDetails(a, + Conf::CONFIG_ACCOUNT_VIDEO_PORT_MIN, + Conf::CONFIG_ACCOUNT_VIDEO_PORT_MAX, + videoPortRange); + + a.emplace(Conf::CONFIG_ACCOUNT_DTMF_TYPE, dtmfType); + a.emplace(Conf::CONFIG_LOCAL_INTERFACE, interface); + a.emplace(Conf::CONFIG_PUBLISHED_SAMEAS_LOCAL, publishedSameasLocal ? TRUE_STR : FALSE_STR); + a.emplace(Conf::CONFIG_PUBLISHED_ADDRESS, publishedIp); + + a.emplace(Conf::CONFIG_TURN_ENABLE, turnEnabled ? TRUE_STR : FALSE_STR); + a.emplace(Conf::CONFIG_TURN_SERVER, turnServer); + a.emplace(Conf::CONFIG_TURN_SERVER_UNAME, turnServerUserName); + a.emplace(Conf::CONFIG_TURN_SERVER_PWD, turnServerPwd); + a.emplace(Conf::CONFIG_TURN_SERVER_REALM, turnServerRealm); + return a; +} + +void +SipAccountBaseConfig::fromMap(const std::map<std::string, std::string>& details) +{ + AccountConfig::fromMap(details); + + // general sip settings + parseString(details, Conf::CONFIG_LOCAL_INTERFACE, interface); + parseBool(details, Conf::CONFIG_PUBLISHED_SAMEAS_LOCAL, publishedSameasLocal); + parseString(details, Conf::CONFIG_PUBLISHED_ADDRESS, publishedIp); + parseString(details, Conf::CONFIG_ACCOUNT_DTMF_TYPE, dtmfType); + + int tmpMin = -1; + parseInt(details, Conf::CONFIG_ACCOUNT_AUDIO_PORT_MIN, tmpMin); + int tmpMax = -1; + parseInt(details, Conf::CONFIG_ACCOUNT_AUDIO_PORT_MAX, tmpMax); + updateRange(tmpMin, tmpMax, audioPortRange); + tmpMin = -1; + parseInt(details, Conf::CONFIG_ACCOUNT_VIDEO_PORT_MIN, tmpMin); + tmpMax = -1; + parseInt(details, Conf::CONFIG_ACCOUNT_VIDEO_PORT_MAX, tmpMax); + updateRange(tmpMin, tmpMax, videoPortRange); + + // ICE - STUN + //parseBool(details, Conf::CONFIG_STUN_ENABLE, stunEnabled); + //parseString(details, Conf::CONFIG_STUN_SERVER, stunServer); + + // ICE - TURN + parseBool(details, Conf::CONFIG_TURN_ENABLE, turnEnabled); + parseString(details, Conf::CONFIG_TURN_SERVER, turnServer); + parseString(details, Conf::CONFIG_TURN_SERVER_UNAME, turnServerUserName); + parseString(details, Conf::CONFIG_TURN_SERVER_PWD, turnServerPwd); + parseString(details, Conf::CONFIG_TURN_SERVER_REALM, turnServerRealm); +} + +} diff --git a/src/sip/sipaccountbase_config.h b/src/sip/sipaccountbase_config.h new file mode 100644 index 0000000000000000000000000000000000000000..2a60efa4e1c1cc6a8d8a2609c2ae96333b484a7e --- /dev/null +++ b/src/sip/sipaccountbase_config.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2004-2022 Savoir-faire Linux Inc. + * + * 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, 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 <https://www.gnu.org/licenses/>. + */ +#pragma once +#include "account_config.h" + +namespace jami { +constexpr static const char* const OVERRTP_STR = "overrtp"; +constexpr static const char* const SIPINFO_STR = "sipinfo"; +constexpr static unsigned MAX_PORT {65536}; +constexpr static unsigned HALF_MAX_PORT {MAX_PORT / 2}; + +struct SipAccountBaseConfig: public AccountConfig { + SipAccountBaseConfig(const std::string& type, const std::string& id, const std::string& path): AccountConfig(type, id, path) {} + + void serialize(YAML::Emitter& out) const override; + void unserialize(const YAML::Node& node) override; + + std::map<std::string, std::string> toMap() const override; + void fromMap(const std::map<std::string, std::string>&) override; + + /** + * interface name on which this account is bound + */ + std::string interface {"default"}; + + /** + * Flag which determine if localIpAddress_ or publishedIpAddress_ is used in + * sip headers + */ + bool publishedSameasLocal {true}; + + std::string publishedIp; + + /** + * Determine if TURN public address resolution is required to register this account. In this + * case a TURN server hostname must be specified. + */ + bool turnEnabled {false}; + + /** + * The TURN server hostname (optional), used to provide the public IP address in case the + * softphone stay behind a NAT. + */ + std::string turnServer; + std::string turnServerUserName; + std::string turnServerPwd; + std::string turnServerRealm; + + std::string tlsCaListFile; + std::string tlsCertificateFile; + std::string tlsPrivateKeyFile; + std::string tlsPassword; + + std::string dtmfType {OVERRTP_STR}; + /* + * Port range for audio RTP ports + */ + std::pair<uint16_t, uint16_t> audioPortRange {16384, 32766}; + + /** + * Port range for video RTP ports + */ + std::pair<uint16_t, uint16_t> videoPortRange {49152, (65536) -2}; +}; + +inline void +updateRange(uint16_t min, uint16_t max, std::pair<uint16_t, uint16_t>& range) +{ + if (min > 0 and (max > min) and max <= MAX_PORT - 2) { + range.first = min; + range.second = max; + } +} + +} diff --git a/test/unitTest/ice/ice_media_cand_exchange.cpp b/test/unitTest/ice/ice_media_cand_exchange.cpp index 47d24078ef55bc80427e026d762df23d6c665459..fe94ccc515721502caf35e7891490826e77ceb31 100644 --- a/test/unitTest/ice/ice_media_cand_exchange.cpp +++ b/test/unitTest/ice/ice_media_cand_exchange.cpp @@ -355,9 +355,6 @@ IceMediaCandExchangeTest::setupSipAccount(CallData& user) details = account->getAccountDetails(); user.userName_ = details[ConfProperties::USERNAME]; user.alias_ = details[ConfProperties::ALIAS]; - - account->enableIceForMedia(true); - user.dest_ = ip_utils::getLocalAddr(AF_INET); user.dest_.setPort(user.listeningPort_); } diff --git a/test/unitTest/ice/ice_sdp_parser.cpp b/test/unitTest/ice/ice_sdp_parser.cpp index bc9be629282b1332aae436d28350d95a2bb60680..6ab8e9318432df3cb02cb789fb14ec900beddb74 100644 --- a/test/unitTest/ice/ice_sdp_parser.cpp +++ b/test/unitTest/ice/ice_sdp_parser.cpp @@ -439,7 +439,6 @@ IceSdpParsingTest::configureTest(CallData& aliceData, CallData& bobData) aliceData.userName_ = account->getAccountDetails()[ConfProperties::USERNAME]; aliceData.alias_ = account->getAccountDetails()[ConfProperties::ALIAS]; account->setLocalPort(aliceData.listeningPort_); - account->enableIceForMedia(true); account->enableIceCompIdRfc5245Compliance(aliceData.compliancyEnabled_); } diff --git a/test/unitTest/media_negotiation/auto_answer.cpp b/test/unitTest/media_negotiation/auto_answer.cpp index d8a15a0d366fe7a382b16b434f0a27c3b4416089..21f1d67655ed055bf33d19ed7dcff75a5401ef2a 100644 --- a/test/unitTest/media_negotiation/auto_answer.cpp +++ b/test/unitTest/media_negotiation/auto_answer.cpp @@ -599,7 +599,6 @@ AutoAnswerMediaNegoTest::configureScenario() auto const& account = Manager::instance().getAccount<Account>(aliceData_.accountId_); aliceData_.userName_ = account->getAccountDetails()[ConfProperties::USERNAME]; aliceData_.alias_ = account->getAccountDetails()[ConfProperties::ALIAS]; - account->enableIceForMedia(true); if (isSipAccount_) { auto sipAccount = std::dynamic_pointer_cast<SIPAccount>(account); CPPUNIT_ASSERT(sipAccount); @@ -613,7 +612,6 @@ AutoAnswerMediaNegoTest::configureScenario() auto const& account = Manager::instance().getAccount<Account>(bobData_.accountId_); bobData_.userName_ = account->getAccountDetails()[ConfProperties::USERNAME]; bobData_.alias_ = account->getAccountDetails()[ConfProperties::ALIAS]; - account->enableIceForMedia(true); CPPUNIT_ASSERT(account->isAutoAnswerEnabled()); if (isSipAccount_) { diff --git a/test/unitTest/media_negotiation/media_negotiation.cpp b/test/unitTest/media_negotiation/media_negotiation.cpp index 52ff0ceac318f21c986a402030ee96312e155b99..52c8c6128fcdf276907ee7e019c6084bbe3072af 100644 --- a/test/unitTest/media_negotiation/media_negotiation.cpp +++ b/test/unitTest/media_negotiation/media_negotiation.cpp @@ -676,7 +676,6 @@ MediaNegotiationTest::configureScenario() callDataMap_["ALICE"].accountId_); callDataMap_["ALICE"].userName_ = account->getAccountDetails()[ConfProperties::USERNAME]; callDataMap_["ALICE"].alias_ = account->getAccountDetails()[ConfProperties::ALIAS]; - account->enableIceForMedia(true); if (isSipAccount_) { auto sipAccount = std::dynamic_pointer_cast<SIPAccount>(account); CPPUNIT_ASSERT(sipAccount); @@ -691,7 +690,6 @@ MediaNegotiationTest::configureScenario() callDataMap_["BOB"].accountId_); callDataMap_["BOB"].userName_ = account->getAccountDetails()[ConfProperties::USERNAME]; callDataMap_["BOB"].alias_ = account->getAccountDetails()[ConfProperties::ALIAS]; - account->enableIceForMedia(true); if (isSipAccount_) { auto sipAccount = std::dynamic_pointer_cast<SIPAccount>(account); diff --git a/test/unitTest/plugins/plugins.cpp b/test/unitTest/plugins/plugins.cpp index 642b2f52ac6882a7e6e3e7d528d3eddacab3245d..820717219b18c9eb7b096b84f7a3d7e76c74de71 100644 --- a/test/unitTest/plugins/plugins.cpp +++ b/test/unitTest/plugins/plugins.cpp @@ -210,7 +210,6 @@ PluginsTest::setUp() aliceData.accountId_); aliceData.userName_ = account->getAccountDetails()[ConfProperties::USERNAME]; aliceData.alias_ = account->getAccountDetails()[ConfProperties::ALIAS]; - account->enableIceForMedia(true); } // Configure Bob @@ -220,7 +219,6 @@ PluginsTest::setUp() bobData.accountId_); bobData.userName_ = account->getAccountDetails()[ConfProperties::USERNAME]; bobData.alias_ = account->getAccountDetails()[ConfProperties::ALIAS]; - account->enableIceForMedia(true); } std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> signalHandlers; diff --git a/test/unitTest/sip_account/sip_basic_calls.cpp b/test/unitTest/sip_account/sip_basic_calls.cpp index ebea7bd62efee3952c3c3fb8bfe9deb7917e9de8..44c466b897bd632f8afcb8a152e4e1266727fc4c 100644 --- a/test/unitTest/sip_account/sip_basic_calls.cpp +++ b/test/unitTest/sip_account/sip_basic_calls.cpp @@ -402,7 +402,6 @@ SipBasicCallTest::configureTest() auto const& account = Manager::instance().getAccount<SIPAccount>( callDataMap_["ALICE"].accountId_); account->setLocalPort(callDataMap_["ALICE"].listeningPort_); - account->enableIceForMedia(true); } { diff --git a/test/unitTest/sip_account/sip_srtp.cpp b/test/unitTest/sip_account/sip_srtp.cpp index fbe1d81735c5efbd0cabd439c71d44c811a113d8..2ef865dc9cb1035be363be3618a48cdc09e54990 100644 --- a/test/unitTest/sip_account/sip_srtp.cpp +++ b/test/unitTest/sip_account/sip_srtp.cpp @@ -361,7 +361,6 @@ SipSrtpTest::configureTest(CallData& aliceData, CallData& bobData) aliceData.userName_ = account->getAccountDetails()[ConfProperties::USERNAME]; aliceData.alias_ = account->getAccountDetails()[ConfProperties::ALIAS]; account->setLocalPort(aliceData.listeningPort_); - account->enableIceForMedia(true); } {