blob: a64fac65642c80fa8ef8ff2804d598990e556861 [file] [log] [blame]
/*
* Copyright (C) 2004-2021 Savoir-faire Linux Inc.
*
* Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com>
* Author: Yan Morin <yan.morin@savoirfairelinux.com>
* Author: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef ACCOUNT_H
#define ACCOUNT_H
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "client/ring_signal.h"
#include "configurationmanager_interface.h"
#include "noncopyable.h"
#include "config/serializable.h"
#include "registration_states.h"
#include "im/message_engine.h"
#include "ip_utils.h"
#include "media_codec.h"
#include "media/media_attribute.h"
#include "logger.h"
#include "compiler_intrinsics.h" // include the "UNUSED" macro
#include "call_set.h"
#include <functional>
#include <string>
#include <vector>
#include <memory>
#include <map>
#include <set>
#include <random>
#include <stdexcept>
#include <atomic>
#include <mutex>
#include <chrono>
namespace YAML {
class Emitter;
class Node;
} // namespace YAML
namespace Json {
class Value;
}
namespace jami {
static constexpr uint64_t JAMI_ID_MAX_VAL = 9007199254740992;
namespace upnp {
class Controller;
} // namespace upnp
class Call;
class SystemCodecContainer;
struct IceTransportOptions;
class VoipLinkException : public std::runtime_error
{
public:
VoipLinkException(const std::string& str = "")
: std::runtime_error("VoipLinkException occurred: " + str)
{}
};
/**
* @file account.h
* @brief Interface to protocol account (ex: SIPAccount)
* It can be enable on loading or activate after.
* It contains account, configuration, VoIP Link and Calls (inside the VoIPLink)
*/
class Account : public Serializable, public std::enable_shared_from_this<Account>
{
public:
Account(const std::string& accountID);
/**
* Virtual destructor
*/
virtual ~Account();
/**
* Free all ressources related to this account.
* ***Current calls using this account are HANG-UP***
*/
void hangupCalls();
virtual void setAccountDetails(const std::map<std::string, std::string>& details);
virtual std::map<std::string, std::string> getAccountDetails() const;
virtual std::map<std::string, std::string> getVolatileAccountDetails() const;
virtual std::string getFromUri() const = 0;
/**
* Load the settings for this account.
*/
virtual void loadConfig() = 0;
virtual void serialize(YAML::Emitter& out) const;
virtual void unserialize(const YAML::Node& node);
/**
* Get the account ID
* @return constant account id
*/
const std::string& getAccountID() const { return accountID_; }
virtual const char* getAccountType() const = 0;
/**
* Returns true if this is the IP2IP account
*/
virtual bool isIP2IP() const { return false; }
/**
* Register the account.
* This should update the getRegistrationState() return value.
*/
virtual void doRegister() = 0;
/**
* Unregister the account.
* This should update the getRegistrationState() return value.
*/
virtual void doUnregister(std::function<void(bool)> cb = std::function<void(bool)>()) = 0;
RegistrationState getRegistrationState() const { return registrationState_; }
/**
* Create a new outgoing call.
*
* @param toUrl The address to call
* @param mediaList A list of media
* @return The created call
*/
virtual std::shared_ptr<Call> newOutgoingCall(std::string_view toUrl,
const std::vector<DRing::MediaMap>& mediaList)
= 0;
/**
* If supported, send a text message from this account.
* @return a token to query the message status
*/
virtual uint64_t sendTextMessage(const std::string& /*to*/,
const std::map<std::string, std::string>& /*payloads*/)
{
return 0;
}
virtual void setIsComposing(const std::string& /*conversationUri*/, bool /*isWriting*/) {};
virtual bool setMessageDisplayed(const std::string& /*conversationUri*/,
const std::string& /*messageId*/,
int /*status*/)
{
return false;
};
virtual std::vector<DRing::Message> getLastMessages(const uint64_t& /*base_timestamp*/)
{
return {};
}
virtual std::map<std::string, std::string> getNearbyPeers() const { return {}; }
/**
* Return the status corresponding to the token.
*/
virtual im::MessageStatus getMessageStatus(uint64_t /*id*/) const
{
return im::MessageStatus::UNKNOWN;
}
virtual bool cancelMessage(uint64_t /*id*/) { return false; }
virtual void setPushNotificationToken(const std::string& pushDeviceToken = "")
{
deviceKey_ = pushDeviceToken;
}
/**
* Tell if the account is enable or not.
* @return true if enabled, false otherwise
*/
bool isEnabled() const noexcept { return enabled_; }
void setEnabled(bool enable) noexcept { enabled_ = enable; }
/**
* Tell if the account is activated
* (can currently be used).
*/
bool isActive() const noexcept { return active_; }
void setActive(bool active) noexcept { active_ = active; }
bool isUsable() const noexcept { return enabled_ and active_; }
void enableVideo(bool enable) { videoEnabled_ = enable; }
bool isVideoEnabled() const noexcept { return videoEnabled_; }
/**
* Set the registration state of the specified link
* @param state The registration state of underlying VoIPLink
*/
virtual void setRegistrationState(RegistrationState state,
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; }
static std::vector<unsigned> getDefaultCodecsId();
static std::map<std::string, std::string> getDefaultCodecDetails(const unsigned& codecId);
/* Accessor to data structures
* @return The list that reflects the user's choice
*/
std::vector<unsigned> getActiveCodecs(MediaType mediaType = MEDIA_ALL) const;
bool hasActiveCodec(MediaType mediaType) const;
/**
* Update both the codec order structure and the codec string used for
* SDP offer and configuration respectively
*/
virtual void setActiveCodecs(const std::vector<unsigned>& list);
std::shared_ptr<AccountCodecInfo> searchCodecById(unsigned codecId, MediaType mediaType);
std::vector<std::shared_ptr<AccountCodecInfo>> getActiveAccountCodecInfoList(
MediaType mediaType) const;
std::shared_ptr<AccountCodecInfo> searchCodecByPayload(unsigned payload, MediaType mediaType);
std::string getRingtonePath() const { return ringtonePath_; }
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;
/**
* returns whether or not UPnP is enabled and active
* ie: if it is able to make port mappings
*/
bool getUPnPActive() const;
/**
* Get the UPnP IP (external router) address.
* If use UPnP is set to false, the address will be empty.
*/
IpAddr getUPnPIpAddress() const;
/**
* Random generator engine
* Logical account state shall never rely on the state of the random generator.
*/
mutable std::mt19937_64 rand;
/**
* Inform the account that the network status has changed.
*/
virtual void connectivityChanged() {};
virtual bool handleMessage(const std::string& /*from*/,
const std::pair<std::string, std::string>& /*message*/)
{
return false;
};
/**
* Helper function used to load the default codec order from the codec factory
*/
void loadDefaultCodecs();
void setCodecActive(unsigned codecId);
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_; }
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;
}
// 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; }
// Check if a Daemon version (typically peer's version) satisfies the
// minimum required version. This check is typically used to disable a
// feature if it's not backward compatible with the peer's version.
static bool meetMinimumRequiredVersion(const std::vector<unsigned>& jamiVersion,
const std::vector<unsigned>& minRequiredVersion);
// Enable/disable compliancy with RFC-5245 for component IDs format.
// The ICE component IDs are enumerated relative to the SDP session,
// i.e., starts from 1 and incremented for each component.
// However, RFC-5245 requires that the ICE component IDs are enumerated
// relative to the media stream, e.g., component IDs 1 and 2 for audio,
// and component IDs 1 and 2 for video. This non-conformity can cause
// inter-operability issues.
// When the compliancy feature is enabled, the component ID in the
// generated SDP will be compliant to RFC-5245. This feature should be
// enabled only when the peer is compliant to RFC-5245 as well.
// The current version is able to correctly parse both formats.
// This feature is needed for backward compatiblity, and should be removed
// once the backward compatibility is no more required.
bool isIceCompIdRfc5245Compliant() const { return iceCompIdRfc5245Compliant_; }
void enableIceCompIdRfc5245Compliance(bool enable) { iceCompIdRfc5245Compliant_ = enable; }
std::shared_ptr<Call> getCall(const std::string& callId) const
{
return callSet_.getCall(callId);
}
std::vector<std::string> getCallList() const { return callSet_.getCallIds(); }
std::shared_ptr<Conference> getConference(const std::string& confId) const
{
return callSet_.getConference(confId);
}
std::vector<std::string> getConferenceList() const { return callSet_.getConferenceIds(); }
void attach(const std::shared_ptr<Call>& call) { callSet_.add(call); }
bool detach(const std::shared_ptr<Call>& call) { return callSet_.remove(call); }
void attach(const std::shared_ptr<Conference>& conf) { callSet_.add(conf); }
bool removeConference(const std::string& confId)
{
auto result = callSet_.removeConference(confId);
if (result)
emitSignal<DRing::CallSignal::ConferenceRemoved>(getAccountID(), confId);
return result;
}
public:
// virtual methods that has to be implemented by concrete classes
/**
* This method is called to request removal of possible account traces on the system,
* like internal account setup files.
*/
virtual void flush() {/* nothing to do here - overload */};
private:
NON_COPYABLE(Account);
/**
* Set of calls attached to the account.
*/
CallSet callSet_;
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());
}
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 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);
/**
* Build the user-agent string
*/
static std::string setDefaultUserAgent();
/**
* Account ID are assign in constructor and shall not changed
*/
const std::string accountID_;
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.
* When an account is unactivated, doUnregister must be called.
*/
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
*/
RegistrationState registrationState_;
/**
* Vector containing all system codecs (with default parameters)
*/
std::shared_ptr<SystemCodecContainer> systemCodecContainer_;
/**
* Vector containing all account codecs (set of system codecs with custom parameters)
*/
std::vector<std::shared_ptr<AccountCodecInfo>> accountCodecInfoList_;
/**
* Ringtone .au file used for this account
*/
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_ {};
/**
* private account codec searching functions
*/
std::shared_ptr<AccountCodecInfo> searchCodecByName(const std::string& name,
MediaType mediaType);
std::vector<unsigned> getAccountCodecInfoIdList(MediaType mediaType) const;
void setAllCodecsActive(MediaType mediaType, bool active);
void sortCodec();
};
static inline std::ostream&
operator<<(std::ostream& os, const Account& acc)
{
os << "[Account " << acc.getAccountID() << "] ";
return os;
}
} // namespace jami
#endif