mirror of
https://github.com/BelledonneCommunications/linphone-sdk
synced 2026-08-07 00:23:14 -04:00
Extend presence by adding permanent activities.
This commit is contained in:
parent
41d4102735
commit
51ca55ba76
19 changed files with 1029 additions and 252 deletions
|
|
@ -11,6 +11,11 @@ that bundles liblinphone and its dependencies as git submodules.
|
|||
Please refer to CHANGELOG.md files of submodules (mainly: *liblinphone*, *mediastreamer2*, *ortp*) for the actual
|
||||
changes made to these components.
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Added
|
||||
- Support of permanent activities in the presence. This enables the ability for the presence server to store
|
||||
activities permanently, even if the PUBLISH expires.
|
||||
|
||||
### Changes
|
||||
- Upgrade of Opus source code to version 1.5.2 to activate OSCE (deep PLC and LACE/noLACE).
|
||||
|
|
|
|||
|
|
@ -1021,7 +1021,7 @@ LINPHONE_PUBLIC void linphone_core_set_add_admin_information_to_contact(Linphone
|
|||
LinphonePresenceModel *
|
||||
linphone_core_create_presence_model_for_consolidated_presence(BCTBX_UNUSED(LinphoneCore *lc),
|
||||
LinphoneConsolidatedPresence presence);
|
||||
char *linphone_presence_model_to_xml(LinphonePresenceModel *model);
|
||||
LINPHONE_PUBLIC char *linphone_presence_model_to_xml(LinphonePresenceModel *model);
|
||||
|
||||
LinphoneVideoDefinition *linphone_video_definition_new(unsigned int width, unsigned int height, const char *name);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2025 Belledonne Communications SARL.
|
||||
* Copyright (c) 2010-2026 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of Liblinphone
|
||||
* (see https://gitlab.linphone.org/BC/public/liblinphone).
|
||||
|
|
@ -1040,6 +1040,27 @@ LINPHONE_PUBLIC bool_t linphone_account_params_dtls_srtp_verify_cert_enabled(con
|
|||
*/
|
||||
LINPHONE_PUBLIC void linphone_account_params_enable_dtls_srtp_verify_cert(LinphoneAccountParams *params, bool_t enable);
|
||||
|
||||
/**
|
||||
* Returns whether the echoed presence subscription is enabled or not.
|
||||
* The echoed presence is the presence as it is returned by the presence server. If a user has multiple devices, one of
|
||||
* these devices may set some permanent activities. This echoed presence will reflect these permanent activities on the
|
||||
* other devices that did not set the permanent activities.
|
||||
* @param params The #LinphoneAccountParams object. @notnil
|
||||
* @return TRUE if the echoed presence subscription is enabled.
|
||||
**/
|
||||
LINPHONE_PUBLIC bool_t
|
||||
linphone_account_params_echoed_presence_subscription_enabled(const LinphoneAccountParams *params);
|
||||
|
||||
/**
|
||||
* Enables or disables the echoed presence subscription on the given #LinphoneAccountParams.
|
||||
* Enabling does NOT trigger an additional SUBSCRIBE or PUBLISH; it modifies the existing
|
||||
* presence SUBSCRIBE (adding local account identity).
|
||||
* @param params The #LinphoneAccountParams object. @notnil
|
||||
* @param enable TRUE to enable, FALSE to disable.
|
||||
*/
|
||||
LINPHONE_PUBLIC void linphone_account_params_enable_echoed_presence_subscription(LinphoneAccountParams *params,
|
||||
bool_t enable);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2022 Belledonne Communications SARL.
|
||||
* Copyright (c) 2010-2026 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of Liblinphone
|
||||
* (see https://gitlab.linphone.org/BC/public/liblinphone).
|
||||
|
|
@ -389,6 +389,16 @@ LINPHONE_PUBLIC LinphoneConsolidatedPresence linphone_account_get_consolidated_p
|
|||
LINPHONE_PUBLIC void linphone_account_set_consolidated_presence(LinphoneAccount *account,
|
||||
LinphoneConsolidatedPresence presence);
|
||||
|
||||
/**
|
||||
* Gets the echoed presence model for the given account.
|
||||
* The echoed presence model reflects the presence information received from the presence server,
|
||||
* including permanent activities, when the echoed presence subscription is enabled.
|
||||
* @param account The #LinphoneAccount object. @notnil
|
||||
* @return The #LinphonePresenceModel for the echoed presence, or NULL if echoed presence subscription is not enabled.
|
||||
* @maybenil
|
||||
*/
|
||||
LINPHONE_PUBLIC LinphonePresenceModel *linphone_account_get_echoed_presence_model(const LinphoneAccount *account);
|
||||
|
||||
/**
|
||||
* Detect if the given input is a phone number or not.
|
||||
* @param account The #LinphoneAccount object, unused yet but may contain useful data. Can be NULL. @maybenil
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2022 Belledonne Communications SARL.
|
||||
* Copyright (c) 2010-2026 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of Liblinphone
|
||||
* (see https://gitlab.linphone.org/BC/public/liblinphone).
|
||||
|
|
@ -584,6 +584,72 @@ LINPHONE_PUBLIC LinphoneStatus linphone_presence_person_add_activities_note(Linp
|
|||
*/
|
||||
LINPHONE_PUBLIC LinphoneStatus linphone_presence_person_clear_activities_notes(LinphonePresencePerson *person);
|
||||
|
||||
/**
|
||||
* Adds a permanent activity to a presence person.
|
||||
* @param person The #LinphonePresencePerson object for which to add a permanent activity. @notnil
|
||||
* @param activity The #LinphonePresenceActivity object to add to the person. @notnil
|
||||
* @return 0 if successful, a value < 0 in case of error.
|
||||
*/
|
||||
LINPHONE_PUBLIC LinphoneStatus linphone_presence_person_add_permanent_activity(LinphonePresencePerson *person,
|
||||
LinphonePresenceActivity *activity);
|
||||
|
||||
/**
|
||||
* Gets the nth permanent activity of a presence person.
|
||||
* @param person The #LinphonePresencePerson object to get the activity from. @notnil
|
||||
* @param index The index of the permanent activity to get (the first activity having the index 0).
|
||||
* @return A pointer to a #LinphonePresenceActivity object if successful, NULL otherwise. @maybenil
|
||||
*/
|
||||
LINPHONE_PUBLIC LinphonePresenceActivity *
|
||||
linphone_presence_person_get_nth_permanent_activity(const LinphonePresencePerson *person, unsigned int index);
|
||||
|
||||
/**
|
||||
* Gets the number of permanent activities included in the presence person.
|
||||
* @param person The #LinphonePresencePerson object to get the number of permanent activities from. @notnil
|
||||
* @return The number of permanent activities included in the #LinphonePresencePerson object.
|
||||
*/
|
||||
LINPHONE_PUBLIC unsigned int linphone_presence_person_get_nb_permanent_activities(const LinphonePresencePerson *person);
|
||||
|
||||
/**
|
||||
* Clears all the permanent activities of a presence person.
|
||||
* @param person The #LinphonePresencePerson object for which to clear the permanent activities. @notnil
|
||||
* @return 0 if successful, a value < 0 in case of error.
|
||||
*/
|
||||
LINPHONE_PUBLIC LinphoneStatus linphone_presence_person_clear_permanent_activities(LinphonePresencePerson *person);
|
||||
|
||||
/**
|
||||
* Adds a permanent activities note to a presence person.
|
||||
* @param person The #LinphonePresencePerson object for which to add a permanent activities note. @notnil
|
||||
* @param note The #LinphonePresenceNote object to add to the person. @notnil
|
||||
* @return 0 if successful, a value < 0 in case of error.
|
||||
*/
|
||||
LINPHONE_PUBLIC LinphoneStatus linphone_presence_person_add_permanent_activities_note(LinphonePresencePerson *person,
|
||||
LinphonePresenceNote *note);
|
||||
|
||||
/**
|
||||
* Gets the nth permanent activities note of a presence person.
|
||||
* @param person The #LinphonePresencePerson object to get the activities note from. @notnil
|
||||
* @param index The index of the permanent activities note to get (the first note having the index 0).
|
||||
* @return A pointer to a #LinphonePresenceNote object if successful, NULL otherwise. @maybenil
|
||||
*/
|
||||
LINPHONE_PUBLIC LinphonePresenceNote *
|
||||
linphone_presence_person_get_nth_permanent_activities_note(const LinphonePresencePerson *person, unsigned int index);
|
||||
|
||||
/**
|
||||
* Gets the number of permanent activities notes included in the presence person.
|
||||
* @param person The #LinphonePresencePerson object to get the number of permanent activities notes from. @notnil
|
||||
* @return The number of permanent activities notes included in the #LinphonePresencePerson object.
|
||||
*/
|
||||
LINPHONE_PUBLIC unsigned int
|
||||
linphone_presence_person_get_nb_permanent_activities_notes(const LinphonePresencePerson *person);
|
||||
|
||||
/**
|
||||
* Clears all the permanent activities notes of a presence person.
|
||||
* @param person The #LinphonePresencePerson object for which to clear the permanent activities notes. @notnil
|
||||
* @return 0 if successful, a value < 0 in case of error.
|
||||
*/
|
||||
LINPHONE_PUBLIC LinphoneStatus
|
||||
linphone_presence_person_clear_permanent_activities_notes(LinphonePresencePerson *person);
|
||||
|
||||
/*****************************************************************************
|
||||
* PRESENCE ACTIVITY FUNCTIONS TO GET ACCESS TO ALL FUNCTIONALITIES *
|
||||
****************************************************************************/
|
||||
|
|
@ -730,7 +796,7 @@ LINPHONE_PUBLIC int linphone_presence_model_get_capabilities(const LinphonePrese
|
|||
* @return whether or not the #LinphonePresenceModel object has a given capability.
|
||||
*/
|
||||
LINPHONE_PUBLIC bool_t linphone_presence_model_has_capability(const LinphonePresenceModel *model,
|
||||
const LinphoneFriendCapability capability);
|
||||
LinphoneFriendCapability capability);
|
||||
|
||||
/**
|
||||
* Returns whether or not the #LinphonePresenceModel object has a given capability with a certain version.
|
||||
|
|
@ -740,7 +806,7 @@ LINPHONE_PUBLIC bool_t linphone_presence_model_has_capability(const LinphonePres
|
|||
* @return whether or not the #LinphonePresenceModel object has a given capability with a certain version.
|
||||
*/
|
||||
LINPHONE_PUBLIC bool_t linphone_presence_model_has_capability_with_version(const LinphonePresenceModel *model,
|
||||
const LinphoneFriendCapability capability,
|
||||
LinphoneFriendCapability capability,
|
||||
float version);
|
||||
|
||||
/**
|
||||
|
|
@ -750,8 +816,9 @@ LINPHONE_PUBLIC bool_t linphone_presence_model_has_capability_with_version(const
|
|||
* @param version The wanted version to test.
|
||||
* @return whether or not the #LinphonePresenceModel object has a given capability with a certain version or more.
|
||||
*/
|
||||
LINPHONE_PUBLIC bool_t linphone_presence_model_has_capability_with_version_or_more(
|
||||
const LinphonePresenceModel *model, const LinphoneFriendCapability capability, float version);
|
||||
LINPHONE_PUBLIC bool_t linphone_presence_model_has_capability_with_version_or_more(const LinphonePresenceModel *model,
|
||||
LinphoneFriendCapability capability,
|
||||
float version);
|
||||
|
||||
/**
|
||||
* Returns the version of the capability of a #LinphonePresenceModel.
|
||||
|
|
@ -760,7 +827,7 @@ LINPHONE_PUBLIC bool_t linphone_presence_model_has_capability_with_version_or_mo
|
|||
* @return the version of the capability of a #LinphonePresenceModel or -1.0 if the model has not the capability.
|
||||
*/
|
||||
LINPHONE_PUBLIC float linphone_presence_model_get_capability_version(const LinphonePresenceModel *model,
|
||||
const LinphoneFriendCapability capability);
|
||||
LinphoneFriendCapability capability);
|
||||
|
||||
/**
|
||||
* Increase the reference count of the #LinphonePresenceService object.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2025 Belledonne Communications SARL.
|
||||
* Copyright (c) 2010-2026 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of Liblinphone
|
||||
* (see https://gitlab.linphone.org/BC/public/liblinphone).
|
||||
|
|
@ -20,8 +20,6 @@
|
|||
|
||||
#include "account-params.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "c-wrapper/internal/c-tools.h"
|
||||
#include "core/core.h"
|
||||
#include "linphone/types.h"
|
||||
|
|
@ -220,6 +218,11 @@ AccountParams::AccountParams(LinphoneCore *lc, bool useDefaultValues) {
|
|||
mInstantMessagingEncryptionMandatory =
|
||||
useDefaultValues ? !!linphone_config_get_default_int(lc->config, "proxy", "im_encryption_mandatory", 0) : 0;
|
||||
|
||||
mEchoedPresenceSubscriptionEnabled =
|
||||
useDefaultValues
|
||||
? linphone_config_get_default_int(lc->config, "proxy", "echoed_presence_subscription", FALSE) == TRUE
|
||||
: false;
|
||||
|
||||
string supportedTags = lc ? linphone_config_get_default_string(lc->config, "proxy", "supported", "empty") : "empty";
|
||||
if (useDefaultValues && supportedTags != "empty") {
|
||||
vector<string> splitTags = bctoolbox::Utils::split(supportedTags, ",");
|
||||
|
|
@ -352,6 +355,10 @@ AccountParams::AccountParams(LinphoneCore *lc, int index) : AccountParams(lc, fa
|
|||
|
||||
setPictureUri(linphone_config_get_string(config, key, "picture_uri", mPictureUri.c_str()));
|
||||
|
||||
enableEchoedPresenceSubscription(linphone_config_get_bool(config, key, "echoed_presence_subscription",
|
||||
echoedPresenceSubscriptionEnabled() ? TRUE : FALSE) ==
|
||||
TRUE);
|
||||
|
||||
string mwiServerUri = linphone_config_get_string(config, key, "mwi_server_uri", "");
|
||||
mMwiServerAddress = nullptr;
|
||||
if (!mwiServerUri.empty()) {
|
||||
|
|
@ -470,6 +477,8 @@ AccountParams::AccountParams(const AccountParams &other) : HybridObject(other),
|
|||
|
||||
mSupportedTagsList = other.mSupportedTagsList;
|
||||
mUseSupportedTags = other.mUseSupportedTags;
|
||||
|
||||
mEchoedPresenceSubscriptionEnabled = other.mEchoedPresenceSubscriptionEnabled;
|
||||
}
|
||||
|
||||
AccountParams::~AccountParams() {
|
||||
|
|
@ -1046,6 +1055,14 @@ std::shared_ptr<const Address> AccountParams::getVoicemailAddress() const {
|
|||
return mVoicemailAddress;
|
||||
}
|
||||
|
||||
bool AccountParams::echoedPresenceSubscriptionEnabled() const {
|
||||
return mEchoedPresenceSubscriptionEnabled;
|
||||
}
|
||||
|
||||
void AccountParams::enableEchoedPresenceSubscription(bool enabled) {
|
||||
mEchoedPresenceSubscriptionEnabled = enabled;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
LinphoneStatus AccountParams::setServerAddress(const std::shared_ptr<const Address> &serverAddr) {
|
||||
|
|
@ -1268,6 +1285,8 @@ void AccountParams::writeToConfigFile(LinphoneConfig *config, int index) {
|
|||
if (mVoicemailAddress) {
|
||||
linphone_config_set_string(config, key, "voicemail_uri", getVoicemailAddressCstr());
|
||||
}
|
||||
linphone_config_set_bool(config, key, "echoed_presence_subscription",
|
||||
mEchoedPresenceSubscriptionEnabled ? TRUE : FALSE);
|
||||
|
||||
linphone_config_set_bool(config, key, "im_encryption_mandatory", mInstantMessagingEncryptionMandatory);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2025 Belledonne Communications SARL.
|
||||
* Copyright (c) 2010-2026 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of Liblinphone
|
||||
* (see https://gitlab.linphone.org/BC/public/liblinphone).
|
||||
|
|
@ -25,7 +25,6 @@
|
|||
|
||||
#include "address/address.h"
|
||||
#include "c-wrapper/list-holder.h"
|
||||
#include "linphone/api/c-push-notification-config.h"
|
||||
#include "linphone/api/c-types.h"
|
||||
#include "linphone/types.h"
|
||||
#include "utils/custom-params.h"
|
||||
|
|
@ -104,6 +103,7 @@ public:
|
|||
void setInstantMessagingEncryptionMandatory(bool mandatory);
|
||||
void setSupportedTagsList(const std::list<std::string> &supportedTagsList);
|
||||
void enableDtlsSrtpVerifyCert(bool flag);
|
||||
void enableEchoedPresenceSubscription(bool enabled);
|
||||
|
||||
// Getters
|
||||
int getExpires() const;
|
||||
|
|
@ -165,6 +165,7 @@ public:
|
|||
const bctbx_list_t *getSupportedTagsCList() const;
|
||||
bool useSupportedTags() const;
|
||||
bool dtlsSrtpVerifyCertEnabled() const;
|
||||
bool echoedPresenceSubscriptionEnabled() const;
|
||||
|
||||
// Other
|
||||
LinphoneStatus setServerAddress(const std::shared_ptr<const Address> &serverAddr);
|
||||
|
|
@ -206,6 +207,7 @@ private:
|
|||
bool mDtlsSrtpVerifyCertEnabled;
|
||||
bool mAllowCpimMessagesInBasicChatRooms;
|
||||
bool mInstantMessagingEncryptionMandatory;
|
||||
bool mEchoedPresenceSubscriptionEnabled;
|
||||
|
||||
void *mUserData;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2025 Belledonne Communications SARL.
|
||||
* Copyright (c) 2010-2026 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of Liblinphone
|
||||
* (see https://gitlab.linphone.org/BC/public/liblinphone).
|
||||
|
|
@ -22,31 +22,21 @@
|
|||
|
||||
#include "account.h"
|
||||
|
||||
#include "c-wrapper/c-wrapper.h"
|
||||
#include "c-wrapper/internal/c-tools.h"
|
||||
#include "chat/ics/ics.h"
|
||||
#include "conference/client-conference.h"
|
||||
#include "content/content.h"
|
||||
#include "core/core.h"
|
||||
#include "linphone/api/c-account-params.h"
|
||||
#include "linphone/api/c-account.h"
|
||||
#include "linphone/api/c-address.h"
|
||||
#include "linphone/utils/algorithm.h"
|
||||
#include "push-notification/push-notification-config.h"
|
||||
#ifdef HAVE_ADVANCED_IM
|
||||
#ifdef HAVE_LIME_X3DH
|
||||
#include "chat/encryption/lime-x3dh-encryption-engine.h"
|
||||
#endif // HAVE_LIME_X3DH
|
||||
#endif // HAVE_ADVANCED_IM
|
||||
#include "c-wrapper/c-wrapper.h"
|
||||
#include "c-wrapper/internal/c-tools.h"
|
||||
#include "core/core-p.h"
|
||||
#include "core/core.h"
|
||||
#include "db/main-db-p.h"
|
||||
#include "event/event-publish.h"
|
||||
#include "friend/friend.h"
|
||||
#include "linphone/core.h"
|
||||
#include "linphone/utils/algorithm.h"
|
||||
#include "presence/presence-model.h"
|
||||
#include "presence/presence-service.h"
|
||||
#include "private.h"
|
||||
#include "utils/custom-params.h"
|
||||
#include "push-notification/push-notification-config.h"
|
||||
#include "utils/fsm-integrity-checker.h"
|
||||
#include "utils/xml-utils.h"
|
||||
#ifdef HAVE_XERCESC
|
||||
|
|
@ -1447,6 +1437,14 @@ LinphoneConsolidatedPresence Account::getConsolidatedPresence() const {
|
|||
: LinphoneConsolidatedPresenceOffline;
|
||||
}
|
||||
|
||||
const std::shared_ptr<PresenceModel> &Account::getEchoedPresenceModel() const {
|
||||
return mEchoedPresenceModel;
|
||||
}
|
||||
|
||||
void Account::setEchoedPresenceModel(const std::shared_ptr<PresenceModel> &presenceModel) {
|
||||
mEchoedPresenceModel = presenceModel;
|
||||
}
|
||||
|
||||
int Account::sendPublish() {
|
||||
if (!mPresenceModel) {
|
||||
lError() << "No presence model has been set for " << *this << ", can't send the PUBLISH";
|
||||
|
|
@ -1483,7 +1481,7 @@ int Account::sendPublish() {
|
|||
|
||||
LinphoneConfig *config = linphone_core_get_config(getCCore());
|
||||
if (linphone_config_get_bool(config, "sip", "update_presence_model_timestamp_before_publish_expires_refresh",
|
||||
FALSE)) {
|
||||
FALSE) == TRUE) {
|
||||
unsigned int nbServices = mPresenceModel->getNbServices();
|
||||
if (nbServices > 0) {
|
||||
std::shared_ptr<PresenceService> latestService = mPresenceModel->getNthService(nbServices - 1);
|
||||
|
|
@ -1531,6 +1529,7 @@ int Account::sendPublish() {
|
|||
content->setContentType(contentType);
|
||||
|
||||
err = mPresencePublishEvent->send(content);
|
||||
mPresenceModel->hasBeenPublished();
|
||||
|
||||
if (presentityAddress) {
|
||||
lInfo() << "Restoring previous presentity address " << *presentityAddress << " for model ["
|
||||
|
|
@ -1538,8 +1537,9 @@ int Account::sendPublish() {
|
|||
mPresenceModel->setPresentity(presentityAddress);
|
||||
mPresenceModel->setContact(contact);
|
||||
}
|
||||
} else
|
||||
} else {
|
||||
setSendPublish(true); /*otherwise do not send publish if registration is in progress, this will be done later*/
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2025 Belledonne Communications SARL.
|
||||
* Copyright (c) 2010-2026 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of Liblinphone
|
||||
* (see https://gitlab.linphone.org/BC/public/liblinphone).
|
||||
|
|
@ -101,6 +101,7 @@ public:
|
|||
void setPresenceModel(const std::shared_ptr<PresenceModel> &presence, bool needToSendPublish = true);
|
||||
void setConsolidatedPresence(LinphoneConsolidatedPresence presence);
|
||||
void setRemoveAuthInfoOnRegistrationCleared(bool removeAuthOnCleared);
|
||||
void setEchoedPresenceModel(const std::shared_ptr<PresenceModel> &presenceModel);
|
||||
|
||||
// Getters
|
||||
int getAuthFailure() const;
|
||||
|
|
@ -121,6 +122,7 @@ public:
|
|||
LimeUserAccountStatus getLimeUserAccountStatus() const;
|
||||
const std::shared_ptr<PresenceModel> &getPresenceModel() const;
|
||||
LinphoneConsolidatedPresence getConsolidatedPresence() const;
|
||||
const std::shared_ptr<PresenceModel> &getEchoedPresenceModel() const;
|
||||
|
||||
int getUnreadChatMessageCount() const;
|
||||
const std::list<std::shared_ptr<AbstractChatRoom>> &getChatRooms() const;
|
||||
|
|
@ -131,7 +133,7 @@ public:
|
|||
std::list<std::shared_ptr<CallLog>> getCallLogs() const;
|
||||
std::list<std::shared_ptr<CallLog>> getCallLogsForAddress(const std::shared_ptr<const Address> &) const;
|
||||
std::list<std::shared_ptr<ConferenceInfo>>
|
||||
getConferenceInfos(const std::list<LinphoneStreamType> capabilities = {}) const;
|
||||
getConferenceInfos(std::list<LinphoneStreamType> capabilities = {}) const;
|
||||
void addConferenceInfo(const std::shared_ptr<ConferenceInfo> &info);
|
||||
void ccmpConferenceInformationResponseReceived();
|
||||
void ccmpConferenceInformationRequestSent();
|
||||
|
|
@ -173,7 +175,7 @@ public:
|
|||
static LinphoneAccountAddressComparisonResult compareLinphoneAddresses(const std::shared_ptr<const Address> &a,
|
||||
const std::shared_ptr<const Address> &b);
|
||||
|
||||
static void writeAllToConfigFile(const std::shared_ptr<Core> core);
|
||||
static void writeAllToConfigFile(std::shared_ptr<Core> core);
|
||||
static void writeToConfigFile(LpConfig *config, const std::shared_ptr<Account> &account, int index);
|
||||
|
||||
// To be removed when not using proxy config anymore
|
||||
|
|
@ -252,6 +254,7 @@ private:
|
|||
|
||||
std::shared_ptr<EventPublish> mPresencePublishEvent;
|
||||
std::shared_ptr<PresenceModel> mPresenceModel;
|
||||
std::shared_ptr<PresenceModel> mEchoedPresenceModel;
|
||||
|
||||
std::shared_ptr<Account> mDependency;
|
||||
std::weak_ptr<Account> mDependee;
|
||||
|
|
@ -292,7 +295,7 @@ private:
|
|||
class AccountLogContextualizer : public CoreLogContextualizer {
|
||||
public:
|
||||
AccountLogContextualizer(const LinphoneAccount *account)
|
||||
: CoreLogContextualizer(account ? Account::toCpp(account) : nullptr) {
|
||||
: CoreLogContextualizer((account != nullptr) ? Account::toCpp(account) : nullptr) {
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2025 Belledonne Communications SARL.
|
||||
* Copyright (c) 2010-2026 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of Liblinphone
|
||||
* (see https://gitlab.linphone.org/BC/public/liblinphone).
|
||||
|
|
@ -537,3 +537,11 @@ bool_t linphone_account_params_dtls_srtp_verify_cert_enabled(const LinphoneAccou
|
|||
void linphone_account_params_enable_dtls_srtp_verify_cert(LinphoneAccountParams *params, bool_t enable) {
|
||||
AccountParams::toCpp(params)->enableDtlsSrtpVerifyCert(enable);
|
||||
}
|
||||
|
||||
bool_t linphone_account_params_echoed_presence_subscription_enabled(const LinphoneAccountParams *params) {
|
||||
return AccountParams::toCpp(params)->echoedPresenceSubscriptionEnabled() ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
void linphone_account_params_enable_echoed_presence_subscription(LinphoneAccountParams *params, bool_t enable) {
|
||||
AccountParams::toCpp(params)->enableEchoedPresenceSubscription(enable == TRUE);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2022 Belledonne Communications SARL.
|
||||
* Copyright (c) 2010-2026 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of Liblinphone
|
||||
* (see https://gitlab.linphone.org/BC/public/liblinphone).
|
||||
|
|
@ -25,7 +25,6 @@
|
|||
#include "account/account-params.h"
|
||||
#include "account/account.h"
|
||||
#include "c-wrapper/c-wrapper.h"
|
||||
#include "core/core-p.h"
|
||||
#include "dial-plan/dial-plan.h"
|
||||
#include "linphone/api/c-account-cbs.h"
|
||||
#include "linphone/api/c-account-params.h"
|
||||
|
|
@ -304,6 +303,14 @@ void linphone_account_set_consolidated_presence(LinphoneAccount *account, Linpho
|
|||
Account::toCpp(account)->setConsolidatedPresence(presence);
|
||||
}
|
||||
|
||||
LinphonePresenceModel *linphone_account_get_echoed_presence_model(const LinphoneAccount *account) {
|
||||
const auto &presenceModel = Account::toCpp(account)->getEchoedPresenceModel();
|
||||
if (presenceModel) {
|
||||
return presenceModel->toC();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void linphone_account_set_presence_model(LinphoneAccount *account, LinphonePresenceModel *presence_model) {
|
||||
linphone_account_set_presence_model_with_publish_toggle(account, presence_model, TRUE);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2023 Belledonne Communications SARL.
|
||||
* Copyright (c) 2010-2026 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of Liblinphone
|
||||
* (see https://gitlab.linphone.org/BC/public/liblinphone).
|
||||
|
|
@ -18,8 +18,6 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <bctoolbox/defs.h>
|
||||
#include <bctoolbox/map.h>
|
||||
|
||||
|
|
@ -28,7 +26,6 @@
|
|||
#include "linphone/api/c-address.h"
|
||||
#include "linphone/core.h"
|
||||
#include "linphone/lpconfig.h"
|
||||
#include "linphone/presence.h"
|
||||
#include "linphone/types.h"
|
||||
#include "presence/presence-activity.h"
|
||||
#include "presence/presence-model.h"
|
||||
|
|
@ -408,6 +405,70 @@ LinphoneStatus linphone_presence_person_clear_activities_notes(LinphonePresenceP
|
|||
return 0;
|
||||
}
|
||||
|
||||
unsigned int linphone_presence_person_get_nb_permanent_activities(const LinphonePresencePerson *person) {
|
||||
if (person == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
return PresencePerson::toCpp(person)->getNbPermanentActivities();
|
||||
}
|
||||
|
||||
LinphonePresenceActivity *linphone_presence_person_get_nth_permanent_activity(const LinphonePresencePerson *person,
|
||||
unsigned int index) {
|
||||
if (person == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
std::shared_ptr<PresenceActivity> activity = PresencePerson::toCpp(person)->getNthPermanentActivity(index);
|
||||
return activity ? activity->toC() : nullptr;
|
||||
}
|
||||
|
||||
LinphoneStatus linphone_presence_person_add_permanent_activity(LinphonePresencePerson *person,
|
||||
LinphonePresenceActivity *activity) {
|
||||
if (person == nullptr || activity == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
return PresencePerson::toCpp(person)->addPermanentActivity(PresenceActivity::getSharedFromThis(activity));
|
||||
}
|
||||
|
||||
LinphoneStatus linphone_presence_person_clear_permanent_activities(LinphonePresencePerson *person) {
|
||||
if (person == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
PresencePerson::toCpp(person)->clearPermanentActivities();
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int linphone_presence_person_get_nb_permanent_activities_notes(const LinphonePresencePerson *person) {
|
||||
if (person == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
return PresencePerson::toCpp(person)->getNbPermanentActivitiesNotes();
|
||||
}
|
||||
|
||||
LinphonePresenceNote *linphone_presence_person_get_nth_permanent_activities_note(const LinphonePresencePerson *person,
|
||||
unsigned int index) {
|
||||
if (person == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
std::shared_ptr<PresenceNote> note = PresencePerson::toCpp(person)->getNthPermanentActivitiesNote(index);
|
||||
return note ? note->toC() : nullptr;
|
||||
}
|
||||
|
||||
LinphoneStatus linphone_presence_person_add_permanent_activities_note(LinphonePresencePerson *person,
|
||||
LinphonePresenceNote *note) {
|
||||
if (person == nullptr || note == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
return PresencePerson::toCpp(person)->addPermanentActivitiesNote(PresenceNote::getSharedFromThis(note));
|
||||
}
|
||||
|
||||
LinphoneStatus linphone_presence_person_clear_permanent_activities_notes(LinphonePresencePerson *person) {
|
||||
if (person == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
PresencePerson::toCpp(person)->clearPermanentActivitiesNotes();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* PRESENCE ACTIVITY FUNCTIONS TO GET ACCESS TO ALL FUNCTIONALITIES *
|
||||
****************************************************************************/
|
||||
|
|
@ -707,8 +768,10 @@ void linphone_notify_parse_presence(const char *content_type,
|
|||
const char *content_subtype,
|
||||
const char *body,
|
||||
SalPresenceModel **result) {
|
||||
PresenceModel::parsePresence(L_C_TO_STRING(content_type), L_C_TO_STRING(content_subtype), L_C_TO_STRING(body),
|
||||
result);
|
||||
if (result != nullptr) {
|
||||
*result = PresenceModel::parsePresence(L_C_TO_STRING(content_type), L_C_TO_STRING(content_subtype),
|
||||
L_C_TO_STRING(body));
|
||||
}
|
||||
}
|
||||
|
||||
void linphone_notify_recv(LinphoneCore *lc, SalOp *op, SalSubscribeStatus ss, SalPresenceModel *model) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2023 Belledonne Communications SARL.
|
||||
* Copyright (c) 2010-2026 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of Liblinphone
|
||||
* (see https://gitlab.linphone.org/BC/public/liblinphone).
|
||||
|
|
@ -581,46 +581,79 @@ std::string FriendList::createResourceListXml() const {
|
|||
std::string xmlContent;
|
||||
if (mFriendsMapByUri.empty()) {
|
||||
lWarning() << __FUNCTION__ << ": Empty list in subscription, ignored.";
|
||||
return std::string();
|
||||
return {};
|
||||
}
|
||||
xmlBufferPtr buf = xmlBufferCreate();
|
||||
if (!buf) {
|
||||
if (buf == nullptr) {
|
||||
lError() << __FUNCTION__ << ": Error creating the XML buffer";
|
||||
return std::string();
|
||||
return {};
|
||||
}
|
||||
xmlTextWriterPtr writer = xmlNewTextWriterMemory(buf, 0);
|
||||
if (!writer) {
|
||||
if (writer == nullptr) {
|
||||
lError() << __FUNCTION__ << ": Error creating the XML writer";
|
||||
xmlBufferFree(buf);
|
||||
return std::string();
|
||||
return {};
|
||||
}
|
||||
xmlTextWriterSetIndent(writer, 1);
|
||||
int err = xmlTextWriterStartDocument(writer, "1.0", "UTF-8", nullptr);
|
||||
if (err >= 0)
|
||||
if (err >= 0) {
|
||||
err = xmlTextWriterStartElementNS(writer, nullptr, (const xmlChar *)"resource-lists",
|
||||
(const xmlChar *)"urn:ietf:params:xml:ns:resource-lists");
|
||||
if (err >= 0)
|
||||
}
|
||||
if (err >= 0) {
|
||||
err = xmlTextWriterWriteAttributeNS(writer, (const xmlChar *)"xmlns", (const xmlChar *)"xsi", nullptr,
|
||||
(const xmlChar *)"http://www.w3.org/2001/XMLSchema-instance");
|
||||
}
|
||||
if (err >= 0) {
|
||||
err = xmlTextWriterStartElement(writer, (const xmlChar *)"list");
|
||||
}
|
||||
|
||||
// Add local identify urls if echoed presence subscription is enabled to get the echoed model from the presence
|
||||
// server. This is to be able to get a coherent view of the permanent activities across all the devices.
|
||||
for (const auto &account : getCore()->getAccounts()) {
|
||||
if (account->getAccountParams()->echoedPresenceSubscriptionEnabled()) {
|
||||
const std::shared_ptr<Address> localIdentity = account->getAccountParams()->getIdentityAddress();
|
||||
if (localIdentity && !localIdentity->asStringUriOnly().empty()) {
|
||||
if (err >= 0) {
|
||||
err = xmlTextWriterStartElement(writer, (const xmlChar *)"entry");
|
||||
}
|
||||
if (err >= 0) {
|
||||
err = xmlTextWriterWriteAttribute(writer, (const xmlChar *)"uri",
|
||||
(const xmlChar *)localIdentity->asStringUriOnly().c_str());
|
||||
}
|
||||
if (err >= 0) {
|
||||
err = xmlTextWriterEndElement(writer); // Close the "entry" element.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string previousEntry;
|
||||
for (const auto &entry : mFriendsMapByUri) {
|
||||
// Map is sorted, prevent duplicates
|
||||
if (previousEntry.empty() || (previousEntry != entry.first)) {
|
||||
if (err >= 0) err = xmlTextWriterStartElement(writer, (const xmlChar *)"entry");
|
||||
if (err >= 0)
|
||||
if (err >= 0) {
|
||||
err = xmlTextWriterStartElement(writer, (const xmlChar *)"entry");
|
||||
}
|
||||
if (err >= 0) {
|
||||
err = xmlTextWriterWriteAttribute(writer, (const xmlChar *)"uri", (const xmlChar *)entry.first.c_str());
|
||||
if (err >= 0) err = xmlTextWriterEndElement(writer); // Close the "entry" element.
|
||||
}
|
||||
if (err >= 0) {
|
||||
err = xmlTextWriterEndElement(writer); // Close the "entry" element.
|
||||
}
|
||||
}
|
||||
previousEntry = entry.first;
|
||||
}
|
||||
|
||||
if (err >= 0) err = xmlTextWriterEndElement(writer); // Close the "list" element.
|
||||
if (err >= 0) err = xmlTextWriterEndElement(writer); // Close the "resource-lists" element.
|
||||
if (err >= 0) err = xmlTextWriterEndDocument(writer);
|
||||
if (err >= 0) {
|
||||
err = xmlTextWriterEndElement(writer); // Close the "list" element.
|
||||
}
|
||||
if (err >= 0) {
|
||||
err = xmlTextWriterEndElement(writer); // Close the "resource-lists" element.
|
||||
}
|
||||
if (err >= 0) {
|
||||
err = xmlTextWriterEndDocument(writer);
|
||||
}
|
||||
if (err > 0) {
|
||||
// xmlTextWriterEndDocument returns the size of the content.
|
||||
xmlContent = (char *)buf->content;
|
||||
|
|
@ -634,7 +667,7 @@ std::string FriendList::createResourceListXml() const {
|
|||
|
||||
std::string FriendList::createResourceListXml() const {
|
||||
lWarning() << "FriendList::createResourceListXml() is stubbed.";
|
||||
return std::string();
|
||||
return {};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -764,7 +797,7 @@ class FriendListXmlException : public std::exception {
|
|||
public:
|
||||
FriendListXmlException(const char *msg) : mMessage(msg) {
|
||||
}
|
||||
const char *what() const throw() override {
|
||||
[[nodiscard]] const char *what() const noexcept override {
|
||||
return mMessage;
|
||||
}
|
||||
|
||||
|
|
@ -785,7 +818,9 @@ void FriendList::parseMultipartRelatedBody(const std::shared_ptr<const Content>
|
|||
xmlXPathRegisterNs(xmlCtx.getXpathContext(), reinterpret_cast<const xmlChar *>("rlmi"),
|
||||
reinterpret_cast<const xmlChar *>("urn:ietf:params:xml:ns:rlmi"));
|
||||
std::string versionStr = xmlCtx.getAttributeTextContent("/rlmi:list", "version");
|
||||
if (versionStr.empty()) throw FriendListXmlException("rlmi+xml: No version attribute in list");
|
||||
if (versionStr.empty()) {
|
||||
throw FriendListXmlException("rlmi+xml: No version attribute in list");
|
||||
}
|
||||
int version = atoi(versionStr.c_str());
|
||||
if (version < mExpectedNotificationVersion) {
|
||||
// No longer an error as dialog may be silently restarting by the refresher
|
||||
|
|
@ -793,42 +828,54 @@ void FriendList::parseMultipartRelatedBody(const std::shared_ptr<const Content>
|
|||
<< mExpectedNotificationVersion << ", dialog may have been reseted";
|
||||
}
|
||||
std::string fullStateStr = xmlCtx.getAttributeTextContent("/rlmi:list", "fullState");
|
||||
if (fullStateStr.empty()) throw FriendListXmlException("rlmi+xml: No fullState attribute in list");
|
||||
if (fullStateStr.empty()) {
|
||||
throw FriendListXmlException("rlmi+xml: No fullState attribute in list");
|
||||
}
|
||||
bool fullState = false;
|
||||
std::string fullStateString(fullStateStr);
|
||||
if ((fullStateString == "true") || (fullStateString == "1")) {
|
||||
fullState = true;
|
||||
for (const auto &lf : mFriendsList.mList)
|
||||
for (const auto &lf : mFriendsList.mList) {
|
||||
lf->clearPresenceModels();
|
||||
}
|
||||
}
|
||||
if ((mExpectedNotificationVersion == 0) && !fullState)
|
||||
if ((mExpectedNotificationVersion == 0) && !fullState) {
|
||||
throw FriendListXmlException("rlmi+xml: Notification with version 0 is not full state, this is not valid");
|
||||
}
|
||||
mExpectedNotificationVersion = version + 1;
|
||||
|
||||
xmlXPathObjectPtr nameObject = xmlCtx.getXpathObjectForNodeList("/rlmi:list/rlmi:resource/rlmi:name/..");
|
||||
if (nameObject && nameObject->nodesetval) {
|
||||
if (nameObject != nullptr && nameObject->nodesetval != nullptr) {
|
||||
for (int i = 1; i <= nameObject->nodesetval->nodeNr; i++) {
|
||||
xmlCtx.setXpathContextNode(xmlXPathNodeSetItem(nameObject->nodesetval, i - 1));
|
||||
std::string name = xmlCtx.getTextContent("./rlmi:name");
|
||||
std::string uri = xmlCtx.getTextContent("./@uri");
|
||||
if (uri.empty()) continue;
|
||||
if (uri.empty()) {
|
||||
continue;
|
||||
}
|
||||
std::shared_ptr<Address> addr = Address::create(uri);
|
||||
if (!addr) continue;
|
||||
if (!addr) {
|
||||
continue;
|
||||
}
|
||||
std::shared_ptr<Friend> lf = findFriendByAddress(addr);
|
||||
if (!lf && mBodylessSubscription) {
|
||||
lf = Friend::create(getCore(), uri);
|
||||
addFriend(lf);
|
||||
}
|
||||
if (!name.empty()) lf->setName(name);
|
||||
if (!name.empty()) {
|
||||
lf->setName(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nameObject) xmlXPathFreeObject(nameObject);
|
||||
if (nameObject != nullptr) {
|
||||
xmlXPathFreeObject(nameObject);
|
||||
}
|
||||
|
||||
std::set<std::shared_ptr<Friend>> listFriendsPresenceReceived;
|
||||
bctbx_list_t *parts = linphone_content_get_parts(content->toC());
|
||||
xmlXPathObjectPtr resourceObject =
|
||||
xmlCtx.getXpathObjectForNodeList("/rlmi:list/rlmi:resource/rlmi:instance[@state=\"active\"]/..");
|
||||
if (resourceObject && resourceObject->nodesetval) {
|
||||
if (resourceObject != nullptr && resourceObject->nodesetval != nullptr) {
|
||||
for (int i = 1; i <= resourceObject->nodesetval->nodeNr; i++) {
|
||||
xmlCtx.setXpathContextNode(xmlXPathNodeSetItem(resourceObject->nodesetval, i - 1));
|
||||
std::string cid = xmlCtx.getTextContent("./rlmi:instance/@cid");
|
||||
|
|
@ -836,9 +883,9 @@ void FriendList::parseMultipartRelatedBody(const std::shared_ptr<const Content>
|
|||
std::shared_ptr<Content> presencePart = nullptr;
|
||||
bctbx_list_t *it = parts;
|
||||
while (it != nullptr) {
|
||||
LinphoneContent *content = (LinphoneContent *)it->data;
|
||||
auto *content = (LinphoneContent *)it->data;
|
||||
const char *header = linphone_content_get_custom_header(content, "Content-Id");
|
||||
if (header && Utils::iequalsIgnoreBrakets(header, cid)) {
|
||||
if (header != nullptr && Utils::iequalsIgnoreBrakets(header, cid)) {
|
||||
presencePart = Content::toCpp(content)->getSharedFromThis();
|
||||
break;
|
||||
}
|
||||
|
|
@ -847,21 +894,26 @@ void FriendList::parseMultipartRelatedBody(const std::shared_ptr<const Content>
|
|||
if (!presencePart) {
|
||||
lWarning() << "rlmi+xml: Cannot find part with Content-Id: " << cid;
|
||||
} else {
|
||||
SalPresenceModel *presence = nullptr;
|
||||
const ContentType &presencePartContentType = presencePart->getContentType();
|
||||
PresenceModel::parsePresence(presencePartContentType.getType(),
|
||||
presencePartContentType.getSubType(),
|
||||
presencePart->getBodyAsUtf8String(), &presence);
|
||||
if (presence) {
|
||||
SalPresenceModel *presence = PresenceModel::parsePresence(presencePartContentType.getType(),
|
||||
presencePartContentType.getSubType(),
|
||||
presencePart->getBodyAsUtf8String());
|
||||
if (presence != nullptr) {
|
||||
// Try to reduce CPU cost of linphone_address_new and find_friend_by_address by only doing
|
||||
// it when we know for sure we have a presence to notify
|
||||
std::string uri = xmlCtx.getTextContent("./@uri");
|
||||
if (uri.empty()) continue;
|
||||
if (uri.empty()) {
|
||||
continue;
|
||||
}
|
||||
std::shared_ptr<Address> addr = Address::create(uri);
|
||||
if (!addr) continue;
|
||||
if (!addr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Clean the URI
|
||||
if (addr->hasUriParam("gr")) addr->removeUriParam("gr");
|
||||
if (addr->hasUriParam("gr")) {
|
||||
addr->removeUriParam("gr");
|
||||
}
|
||||
uri = addr->asStringUriOnly();
|
||||
|
||||
const auto [first, last] = mFriendsMapByUri.equal_range(uri);
|
||||
|
|
@ -873,13 +925,27 @@ void FriendList::parseMultipartRelatedBody(const std::shared_ptr<const Content>
|
|||
getSharedFromThis(), uri,
|
||||
PresenceModel::toCpp((LinphonePresenceModel *)presence)->getSharedFromThis());
|
||||
listFriendsPresenceReceived.insert(lf);
|
||||
} else {
|
||||
for (const auto &account : getCore()->getAccounts()) {
|
||||
if (account->getAccountParams()->echoedPresenceSubscriptionEnabled()) {
|
||||
const std::shared_ptr<Address> localIdentity =
|
||||
account->getAccountParams()->getIdentityAddress();
|
||||
if (localIdentity && localIdentity->asStringUriOnly() == uri) {
|
||||
account->setEchoedPresenceModel(
|
||||
PresenceModel::toCpp(
|
||||
reinterpret_cast<LinphonePresenceModel *>(presence))
|
||||
->getSharedFromThis());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Save the equal_range iterators for looping because mFriendsMapByUri might
|
||||
// change during the loop, leading to wrong presence notifications
|
||||
std::list<std::multimap<std::string, std::shared_ptr<Friend>>::iterator> its;
|
||||
for (auto it = first; it != last; it++)
|
||||
for (auto it = first; it != last; it++) {
|
||||
its.push_back(it);
|
||||
}
|
||||
for (const auto &it : its) {
|
||||
it->second->presenceReceived(
|
||||
getSharedFromThis(), uri,
|
||||
|
|
@ -897,15 +963,18 @@ void FriendList::parseMultipartRelatedBody(const std::shared_ptr<const Content>
|
|||
// Notify list with all friends for which we received presence information
|
||||
if (!listFriendsPresenceReceived.empty()) {
|
||||
bctbx_list_t *l = nullptr;
|
||||
for (const auto &lf : listFriendsPresenceReceived)
|
||||
for (const auto &lf : listFriendsPresenceReceived) {
|
||||
l = bctbx_list_append(l, lf->toC());
|
||||
}
|
||||
LINPHONE_HYBRID_OBJECT_INVOKE_CBS(FriendList, this, linphone_friend_list_cbs_get_presence_received, l);
|
||||
bctbx_list_free(l);
|
||||
}
|
||||
}
|
||||
|
||||
bctbx_list_free_with_data(parts, (void (*)(void *))linphone_content_unref);
|
||||
if (resourceObject) xmlXPathFreeObject(resourceObject);
|
||||
if (resourceObject != nullptr) {
|
||||
xmlXPathFreeObject(resourceObject);
|
||||
}
|
||||
} catch (FriendListXmlException &e) {
|
||||
lWarning() << e.what();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2023 Belledonne Communications SARL.
|
||||
* Copyright (c) 2010-2026 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of Liblinphone
|
||||
* (see https://gitlab.linphone.org/BC/public/liblinphone).
|
||||
|
|
@ -18,6 +18,13 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Prevent compilation error with std::max, because Visual Studio defines a max macro.
|
||||
// This define prevents Visual Studio from defining with max macro.
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
|
||||
#include <bctoolbox/defs.h>
|
||||
|
|
@ -127,7 +134,9 @@ std::shared_ptr<PresenceActivity> PresenceModel::getActivity() const {
|
|||
/* Suppose that if at least one service is open, then the model is open. */
|
||||
LinphonePresenceBasicStatus PresenceModel::getBasicStatus() const {
|
||||
for (const auto &service : mServices) {
|
||||
if (service->getBasicStatus() == LinphonePresenceBasicStatusOpen) return LinphonePresenceBasicStatusOpen;
|
||||
if (service->getBasicStatus() == LinphonePresenceBasicStatusOpen) {
|
||||
return LinphonePresenceBasicStatusOpen;
|
||||
}
|
||||
}
|
||||
return LinphonePresenceBasicStatusClosed;
|
||||
}
|
||||
|
|
@ -137,7 +146,9 @@ int PresenceModel::getCapabilities() const {
|
|||
for (const auto &service : mServices) {
|
||||
for (const auto &pair : service->mCapabilities) {
|
||||
LinphoneFriendCapability capability = Friend::nameToCapability(pair.first);
|
||||
if (capability != LinphoneFriendCapabilityNone) capabilities |= capability;
|
||||
if (capability != LinphoneFriendCapabilityNone) {
|
||||
capabilities |= capability;
|
||||
}
|
||||
}
|
||||
}
|
||||
return capabilities;
|
||||
|
|
@ -147,15 +158,21 @@ float PresenceModel::getCapabilityVersion(const LinphoneFriendCapability capabil
|
|||
auto it = std::max_element(mServices.cbegin(), mServices.cend(), [&](const auto &a, const auto &b) {
|
||||
return a->getCapabilityVersion(capability) < b->getCapabilityVersion(capability);
|
||||
});
|
||||
if (it == mServices.cend()) return -1;
|
||||
if (it == mServices.cend()) {
|
||||
return -1;
|
||||
}
|
||||
return (*it)->getCapabilityVersion(capability);
|
||||
}
|
||||
|
||||
LinphoneConsolidatedPresence PresenceModel::getConsolidatedPresence() const {
|
||||
if (isOnline()) return LinphoneConsolidatedPresenceOnline;
|
||||
if (isOnline()) {
|
||||
return LinphoneConsolidatedPresenceOnline;
|
||||
}
|
||||
if (getBasicStatus() == LinphonePresenceBasicStatusClosed) {
|
||||
unsigned int nbActivities = getNbActivities();
|
||||
if (nbActivities == 0) return LinphoneConsolidatedPresenceOffline;
|
||||
if (nbActivities == 0) {
|
||||
return LinphoneConsolidatedPresenceOffline;
|
||||
}
|
||||
return LinphoneConsolidatedPresenceDoNotDisturb;
|
||||
}
|
||||
return LinphoneConsolidatedPresenceBusy;
|
||||
|
|
@ -164,7 +181,9 @@ LinphoneConsolidatedPresence PresenceModel::getConsolidatedPresence() const {
|
|||
const std::string &PresenceModel::getContact() const {
|
||||
for (const auto &service : mServices) {
|
||||
const std::string &contact = service->getContact();
|
||||
if (!contact.empty()) return contact;
|
||||
if (!contact.empty()) {
|
||||
return contact;
|
||||
}
|
||||
}
|
||||
return emptyString;
|
||||
}
|
||||
|
|
@ -172,12 +191,14 @@ const std::string &PresenceModel::getContact() const {
|
|||
time_t PresenceModel::getLatestActivityTimestamp() const {
|
||||
auto it = std::max_element(mPersons.cbegin(), mPersons.cend(),
|
||||
[](const auto &a, const auto &b) { return a->getTimestamp() < b->getTimestamp(); });
|
||||
if (it == mPersons.cend()) return static_cast<time_t>(-1);
|
||||
if (it == mPersons.cend()) {
|
||||
return static_cast<time_t>(-1);
|
||||
}
|
||||
return (*it)->getTimestamp();
|
||||
}
|
||||
|
||||
unsigned int PresenceModel::getNbActivities() const {
|
||||
return std::accumulate(mPersons.cbegin(), mPersons.cend(), 0u,
|
||||
return std::accumulate(mPersons.cbegin(), mPersons.cend(), 0U,
|
||||
[](unsigned int a, const auto &person) { return a + person->getNbActivities(); });
|
||||
}
|
||||
|
||||
|
|
@ -192,22 +213,32 @@ unsigned int PresenceModel::getNbServices() const {
|
|||
std::shared_ptr<PresenceNote> PresenceModel::getNote(const std::string &lang) const {
|
||||
/* First try to find a note in the specified language exactly. */
|
||||
std::shared_ptr<PresenceNote> note = findNoteWithLang(lang);
|
||||
if (note) return note;
|
||||
if (note) {
|
||||
return note;
|
||||
}
|
||||
|
||||
/* No notes in the specified language has been found, try to find one without language. */
|
||||
note = findNoteWithLang(std::string());
|
||||
if (note) return note;
|
||||
if (note) {
|
||||
return note;
|
||||
}
|
||||
|
||||
/* Still no result, so get the first note even if it is not in the specified language. */
|
||||
for (const auto &person : mPersons) {
|
||||
note = person->getNthActivitiesNote(0);
|
||||
if (note) return note;
|
||||
if (note) {
|
||||
return note;
|
||||
}
|
||||
note = person->getNthNote(0);
|
||||
if (note) return note;
|
||||
if (note) {
|
||||
return note;
|
||||
}
|
||||
}
|
||||
for (const auto &service : mServices) {
|
||||
note = service->getNthNote(0);
|
||||
if (note) return note;
|
||||
if (note) {
|
||||
return note;
|
||||
}
|
||||
}
|
||||
return getNthNote(0);
|
||||
}
|
||||
|
|
@ -252,12 +283,12 @@ const std::shared_ptr<Address> &PresenceModel::getPresentity() const {
|
|||
}
|
||||
|
||||
time_t PresenceModel::getTimestamp() const {
|
||||
time_t timestamp = static_cast<time_t>(-1);
|
||||
auto timestamp = static_cast<time_t>(-1);
|
||||
for (const auto &service : mServices) {
|
||||
if (service->getTimestamp() > timestamp) timestamp = service->getTimestamp();
|
||||
timestamp = std::max(service->getTimestamp(), timestamp);
|
||||
}
|
||||
for (const auto &person : mPersons) {
|
||||
if (person->getTimestamp() > timestamp) timestamp = person->getTimestamp();
|
||||
timestamp = std::max(person->getTimestamp(), timestamp);
|
||||
}
|
||||
return timestamp;
|
||||
}
|
||||
|
|
@ -265,7 +296,9 @@ time_t PresenceModel::getTimestamp() const {
|
|||
// -----------------------------------------------------------------------------
|
||||
|
||||
LinphoneStatus PresenceModel::addActivity(const std::shared_ptr<PresenceActivity> &activity) {
|
||||
if (!activity) return -1;
|
||||
if (!activity) {
|
||||
return -1;
|
||||
}
|
||||
std::shared_ptr<PresencePerson> person;
|
||||
if (getNbPersons() == 0) {
|
||||
/* There is no person in the presence model, add one. */
|
||||
|
|
@ -280,7 +313,9 @@ LinphoneStatus PresenceModel::addActivity(const std::shared_ptr<PresenceActivity
|
|||
}
|
||||
|
||||
LinphoneStatus PresenceModel::addNote(const std::string &content, const std::string &lang) {
|
||||
if (content.empty()) return -1;
|
||||
if (content.empty()) {
|
||||
return -1;
|
||||
}
|
||||
/* Will put the note in the first service. */
|
||||
std::shared_ptr<PresenceService> service = getNthService(0);
|
||||
if (!service) {
|
||||
|
|
@ -302,7 +337,9 @@ LinphoneStatus PresenceModel::addNote(const std::string &content, const std::str
|
|||
}
|
||||
|
||||
LinphoneStatus PresenceModel::addPerson(const std::shared_ptr<PresencePerson> &person) {
|
||||
if (!person) return -1;
|
||||
if (!person) {
|
||||
return -1;
|
||||
}
|
||||
auto it = std::lower_bound(mPersons.cbegin(), mPersons.cend(), person->getTimestamp(),
|
||||
[](const auto &person, time_t timestamp) { return person->getTimestamp() < timestamp; });
|
||||
mPersons.insert(it, person);
|
||||
|
|
@ -310,21 +347,26 @@ LinphoneStatus PresenceModel::addPerson(const std::shared_ptr<PresencePerson> &p
|
|||
}
|
||||
|
||||
LinphoneStatus PresenceModel::addService(const std::shared_ptr<PresenceService> &service) {
|
||||
if (!service) return -1;
|
||||
if (!service) {
|
||||
return -1;
|
||||
}
|
||||
mServices.push_back(service);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PresenceModel::clearActivities() {
|
||||
for (auto &person : mPersons)
|
||||
for (auto &person : mPersons) {
|
||||
person->clearActivities();
|
||||
}
|
||||
}
|
||||
|
||||
void PresenceModel::clearNotes() {
|
||||
for (auto &person : mPersons)
|
||||
for (auto &person : mPersons) {
|
||||
person->clearNotes();
|
||||
for (auto &service : mServices)
|
||||
}
|
||||
for (auto &service : mServices) {
|
||||
service->clearNotes();
|
||||
}
|
||||
mNotes.clear();
|
||||
}
|
||||
|
||||
|
|
@ -337,68 +379,89 @@ void PresenceModel::clearServices() {
|
|||
}
|
||||
|
||||
bool PresenceModel::hasCapability(const LinphoneFriendCapability capability) const {
|
||||
return getCapabilities() & capability;
|
||||
return (getCapabilities() & capability) != 0;
|
||||
}
|
||||
|
||||
bool PresenceModel::hasCapabilityWithVersion(const LinphoneFriendCapability capability, float version) const {
|
||||
for (const auto &service : mServices) {
|
||||
if (service->hasCapabilityWithVersion(capability, version)) return true;
|
||||
}
|
||||
return false;
|
||||
return std::any_of(mServices.begin(), mServices.end(), [capability, version](const auto &service) {
|
||||
return service->hasCapabilityWithVersion(capability, version);
|
||||
});
|
||||
}
|
||||
|
||||
bool PresenceModel::hasCapabilityWithVersionOrMore(const LinphoneFriendCapability capability, float version) const {
|
||||
for (const auto &service : mServices) {
|
||||
if (service->hasCapabilityWithVersionOrMore(capability, version)) return true;
|
||||
}
|
||||
return false;
|
||||
return std::any_of(mServices.begin(), mServices.end(), [capability, version](const auto &service) {
|
||||
return service->hasCapabilityWithVersionOrMore(capability, version);
|
||||
});
|
||||
}
|
||||
|
||||
bool PresenceModel::isOnline() const {
|
||||
return (mIsOnline || ((getBasicStatus() == LinphonePresenceBasicStatusOpen) && (getNbActivities() == 0)));
|
||||
}
|
||||
|
||||
void PresenceModel::hasBeenPublished() {
|
||||
for (const auto &person : mPersons) {
|
||||
person->mPermanentActivitiesToBePublished = false;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_XML2
|
||||
|
||||
int PresenceModel::parsePidfXmlPresenceNotes(XmlParsingContext &xmlContext) {
|
||||
std::stringstream ss;
|
||||
xmlXPathObjectPtr noteObject = xmlContext.getXpathObjectForNodeList("/pidf:presence/pidf:note");
|
||||
if (noteObject && noteObject->nodesetval) {
|
||||
if ((noteObject != nullptr) && (noteObject->nodesetval != nullptr)) {
|
||||
for (int i = 1; i <= noteObject->nodesetval->nodeNr; i++) {
|
||||
ss.clear(), ss.str(std::string()), ss << "/pidf:presence/pidf:note[" << i << "]";
|
||||
std::string noteStr = xmlContext.getTextContent(ss.str());
|
||||
if (noteStr.empty()) continue;
|
||||
if (noteStr.empty()) {
|
||||
continue;
|
||||
}
|
||||
ss.clear(), ss.str(std::string()), ss << "/pidf:presence/pidf:note[" << i << "]/@xml:lang";
|
||||
std::string lang = xmlContext.getTextContent(ss.str());
|
||||
std::shared_ptr<PresenceNote> note = PresenceNote::create(noteStr, lang);
|
||||
mNotes.push_back(note);
|
||||
}
|
||||
}
|
||||
if (noteObject) xmlXPathFreeObject(noteObject);
|
||||
if (noteObject != nullptr) {
|
||||
xmlXPathFreeObject(noteObject);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PresenceModel::parsePidfXmlPresencePersons(XmlParsingContext &xmlContext) {
|
||||
stringstream ss;
|
||||
time_t timestamp = static_cast<time_t>(-1);
|
||||
auto timestamp = static_cast<time_t>(-1);
|
||||
int err = 0;
|
||||
xmlXPathObjectPtr personObject = xmlContext.getXpathObjectForNodeList(PresencePerson::pidfXmlPrefix.data());
|
||||
if (personObject && personObject->nodesetval) {
|
||||
xmlXPathObjectPtr personObject = xmlContext.getXpathObjectForNodeList(std::string(PresencePerson::pidfXmlPrefix));
|
||||
if ((personObject != nullptr) && (personObject->nodesetval != nullptr)) {
|
||||
for (int i = 1; i <= personObject->nodesetval->nodeNr; i++) {
|
||||
ss.clear(), ss.str(std::string()), ss << PresencePerson::pidfXmlPrefix.data() << "[" << i << "]/@id";
|
||||
std::string personIdStr = xmlContext.getTextContent(ss.str());
|
||||
ss.clear(), ss.str(std::string()),
|
||||
ss << PresencePerson::pidfXmlPrefix.data() << "[" << i << "]/dm:timestamp";
|
||||
std::string personTimestampStr = xmlContext.getTextContent(ss.str());
|
||||
if (!personTimestampStr.empty()) timestamp = PresenceModel::parseTimestamp(personTimestampStr);
|
||||
if (!personTimestampStr.empty()) {
|
||||
timestamp = PresenceModel::parseTimestamp(personTimestampStr);
|
||||
}
|
||||
std::shared_ptr<PresencePerson> person = PresencePerson::create(personIdStr, timestamp);
|
||||
err = person->parsePidfXmlPresenceActivities(xmlContext, (unsigned int)i);
|
||||
if (err == 0) err = person->parsePidfXmlPresenceNotes(xmlContext, (unsigned int)i);
|
||||
if (err == 0) addPerson(person);
|
||||
if (err != 0) break;
|
||||
if (err == 0) {
|
||||
err = person->parsePidfXmlPresencePermanentActivities(xmlContext, (unsigned int)i);
|
||||
}
|
||||
if (err == 0) {
|
||||
err = person->parsePidfXmlPresenceNotes(xmlContext, (unsigned int)i);
|
||||
}
|
||||
if (err == 0) {
|
||||
addPerson(person);
|
||||
}
|
||||
if (err != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (personObject) xmlXPathFreeObject(personObject);
|
||||
if (personObject != nullptr) {
|
||||
xmlXPathFreeObject(personObject);
|
||||
}
|
||||
if (err < 0) {
|
||||
/* Remove all the persons added since there was an error. */
|
||||
clearPersons();
|
||||
|
|
@ -410,22 +473,30 @@ int PresenceModel::parsePidfXmlPresenceServices(XmlParsingContext &xmlContext) {
|
|||
stringstream ss;
|
||||
LinphonePresenceBasicStatus basicStatus;
|
||||
|
||||
xmlXPathObjectPtr serviceObject = xmlContext.getXpathObjectForNodeList(PresenceService::pidfXmlPrefix.data());
|
||||
if (serviceObject && serviceObject->nodesetval) {
|
||||
xmlXPathObjectPtr serviceObject = xmlContext.getXpathObjectForNodeList(std::string(PresenceService::pidfXmlPrefix));
|
||||
if ((serviceObject != nullptr) && (serviceObject->nodesetval != nullptr)) {
|
||||
for (int i = 1; i <= serviceObject->nodesetval->nodeNr; i++) {
|
||||
ss.clear(), ss.str(std::string()),
|
||||
ss << PresenceService::pidfXmlPrefix.data() << "[" << i << "]/pidf:status/pidf:basic";
|
||||
std::string basicStatusStr = xmlContext.getTextContent(ss.str());
|
||||
if (basicStatusStr.empty()) continue;
|
||||
if (basicStatusStr == "open") basicStatus = LinphonePresenceBasicStatusOpen;
|
||||
else if (basicStatusStr == "closed") basicStatus = LinphonePresenceBasicStatusClosed;
|
||||
else return -1; /* Invalid value for basic status. */
|
||||
if (basicStatusStr.empty()) {
|
||||
continue;
|
||||
}
|
||||
if (basicStatusStr == "open") {
|
||||
basicStatus = LinphonePresenceBasicStatusOpen;
|
||||
} else if (basicStatusStr == "closed") {
|
||||
basicStatus = LinphonePresenceBasicStatusClosed;
|
||||
} else {
|
||||
return -1; /* Invalid value for basic status. */
|
||||
}
|
||||
|
||||
ss.clear(), ss.str(std::string()),
|
||||
ss << PresenceService::pidfXmlPrefix.data() << "[" << i << "]/pidf:status/pidfonline:online";
|
||||
xmlXPathObjectPtr pidfonlineObject = xmlContext.getXpathObjectForNodeList(ss.str());
|
||||
if (pidfonlineObject) {
|
||||
if (pidfonlineObject->nodesetval && pidfonlineObject->nodesetval->nodeNr > 0) mIsOnline = true;
|
||||
if (pidfonlineObject != nullptr) {
|
||||
if ((pidfonlineObject->nodesetval != nullptr) && pidfonlineObject->nodesetval->nodeNr > 0) {
|
||||
mIsOnline = true;
|
||||
}
|
||||
xmlXPathFreeObject(pidfonlineObject);
|
||||
}
|
||||
|
||||
|
|
@ -445,8 +516,8 @@ int PresenceModel::parsePidfXmlPresenceServices(XmlParsingContext &xmlContext) {
|
|||
ss << PresenceService::pidfXmlPrefix.data() << "[" << i << "]/oma-pres:service-description";
|
||||
xmlXPathObjectPtr descriptionsObject = xmlContext.getXpathObjectForNodeList(ss.str());
|
||||
std::list<std::string> descriptions;
|
||||
if (descriptionsObject) {
|
||||
if (descriptionsObject->nodesetval) {
|
||||
if (descriptionsObject != nullptr) {
|
||||
if (descriptionsObject->nodesetval != nullptr) {
|
||||
for (int j = 1; j <= descriptionsObject->nodesetval->nodeNr; j++) {
|
||||
xmlContext.setXpathContextNode(xmlXPathNodeSetItem(descriptionsObject->nodesetval, j - 1));
|
||||
std::string serviceId = xmlContext.getTextContent("./oma-pres:service-id");
|
||||
|
|
@ -460,14 +531,22 @@ int PresenceModel::parsePidfXmlPresenceServices(XmlParsingContext &xmlContext) {
|
|||
xmlXPathFreeObject(descriptionsObject);
|
||||
}
|
||||
|
||||
if (!timestampStr.empty()) service->setTimestamp(PresenceModel::parseTimestamp(timestampStr));
|
||||
if (!contactStr.empty()) service->setContact(contactStr);
|
||||
if (!descriptions.empty()) service->setDescriptions(descriptions);
|
||||
if (!timestampStr.empty()) {
|
||||
service->setTimestamp(PresenceModel::parseTimestamp(timestampStr));
|
||||
}
|
||||
if (!contactStr.empty()) {
|
||||
service->setContact(contactStr);
|
||||
}
|
||||
if (!descriptions.empty()) {
|
||||
service->setDescriptions(descriptions);
|
||||
}
|
||||
service->parsePidfXmlPresenceNotes(xmlContext, (unsigned int)i);
|
||||
addService(service);
|
||||
}
|
||||
}
|
||||
if (serviceObject) xmlXPathFreeObject(serviceObject);
|
||||
if (serviceObject != nullptr) {
|
||||
xmlXPathFreeObject(serviceObject);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -482,20 +561,28 @@ std::shared_ptr<PresenceNote> PresenceModel::findNoteWithLang(const std::string
|
|||
const auto activityNoteIterator =
|
||||
std::find_if(person->mActivitiesNotes.cbegin(), person->mActivitiesNotes.cend(),
|
||||
[&](const auto ¬e) { return note->getLang() == lang; });
|
||||
if (activityNoteIterator != person->mActivitiesNotes.cend()) return *activityNoteIterator;
|
||||
if (activityNoteIterator != person->mActivitiesNotes.cend()) {
|
||||
return *activityNoteIterator;
|
||||
}
|
||||
/* ... then look in the person notes. */
|
||||
const auto noteIterator = std::find_if(person->mNotes.cbegin(), person->mNotes.cend(),
|
||||
[&](const auto ¬e) { return note->getLang() == lang; });
|
||||
if (noteIterator != person->mNotes.cend()) return *noteIterator;
|
||||
if (noteIterator != person->mNotes.cend()) {
|
||||
return *noteIterator;
|
||||
}
|
||||
}
|
||||
for (const auto &service : mServices) {
|
||||
const auto it = std::find_if(service->mNotes.cbegin(), service->mNotes.cend(),
|
||||
[&](const auto ¬e) { return note->getLang() == lang; });
|
||||
if (it != service->mNotes.cend()) return *it;
|
||||
if (it != service->mNotes.cend()) {
|
||||
return *it;
|
||||
}
|
||||
}
|
||||
const auto noteIterator =
|
||||
std::find_if(mNotes.cbegin(), mNotes.cend(), [&](const auto ¬e) { return note->getLang() == lang; });
|
||||
if (noteIterator != mNotes.cend()) return *noteIterator;
|
||||
if (noteIterator != mNotes.cend()) {
|
||||
return *noteIterator;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
@ -519,12 +606,17 @@ std::string PresenceModel::toXml() const {
|
|||
std::string content;
|
||||
|
||||
try {
|
||||
if (!getPresentity())
|
||||
if (!getPresentity()) {
|
||||
throw PresenceModelXmlException("Cannot convert presence model to xml because no presentity set");
|
||||
}
|
||||
buf = xmlBufferCreate();
|
||||
if (!buf) throw PresenceModelXmlException("Error creating the XML buffer");
|
||||
if (buf == nullptr) {
|
||||
throw PresenceModelXmlException("Error creating the XML buffer");
|
||||
}
|
||||
writer = xmlNewTextWriterMemory(buf, 0);
|
||||
if (!writer) throw PresenceModelXmlException("Error creating the XML writer");
|
||||
if (writer == nullptr) {
|
||||
throw PresenceModelXmlException("Error creating the XML writer");
|
||||
}
|
||||
xmlTextWriterSetIndent(writer, 1);
|
||||
|
||||
int err = xmlTextWriterStartDocument(writer, "1.0", "UTF-8", nullptr);
|
||||
|
|
@ -556,18 +648,24 @@ std::string PresenceModel::toXml() const {
|
|||
err = PresenceService::toXml(nullptr, writer, contact, false);
|
||||
} else {
|
||||
for (const auto &service : mServices) {
|
||||
if (err >= 0) err = service->toXml(writer, contact, isOnline());
|
||||
if (err >= 0) {
|
||||
err = service->toXml(writer, contact, isOnline());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (err >= 0) {
|
||||
for (const auto &person : mPersons) {
|
||||
if (err >= 0) err = person->toXml(writer);
|
||||
if (err >= 0) {
|
||||
err = person->toXml(writer);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (err >= 0) {
|
||||
for (const auto ¬e : mNotes) {
|
||||
if (err >= 0) err = note->toXml(writer, "");
|
||||
if (err >= 0) {
|
||||
err = note->toXml(writer, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -586,8 +684,12 @@ std::string PresenceModel::toXml() const {
|
|||
ms_error("%s", e.what());
|
||||
}
|
||||
|
||||
if (writer) xmlFreeTextWriter(writer);
|
||||
if (buf) xmlBufferFree(buf);
|
||||
if (writer != nullptr) {
|
||||
xmlFreeTextWriter(writer);
|
||||
}
|
||||
if (buf != nullptr) {
|
||||
xmlBufferFree(buf);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
|
|
@ -595,7 +697,7 @@ std::string PresenceModel::toXml() const {
|
|||
|
||||
std::string PresenceModel::toXml() const {
|
||||
ms_warning("PresenceModel::toXml(): stubbed.");
|
||||
return std::string();
|
||||
return {};
|
||||
}
|
||||
|
||||
#endif /* HAVE_XML2 */
|
||||
|
|
@ -627,33 +729,35 @@ std::string PresenceModel::generatePresenceId() {
|
|||
|
||||
#ifdef HAVE_XML2
|
||||
|
||||
void PresenceModel::parsePresence(const std::string &contentType,
|
||||
const std::string &contentSubtype,
|
||||
const std::string &body,
|
||||
SalPresenceModel **result) {
|
||||
SalPresenceModel *PresenceModel::parsePresence(const std::string &contentType,
|
||||
const std::string &contentSubtype,
|
||||
const std::string &body) {
|
||||
if (contentType != "application") {
|
||||
*result = nullptr;
|
||||
return;
|
||||
return nullptr;
|
||||
}
|
||||
if (contentSubtype != "pidf+xml") {
|
||||
*result = nullptr;
|
||||
ms_error("Unknown content type '%s/%s' for presence", L_STRING_TO_C(contentType),
|
||||
L_STRING_TO_C(contentSubtype));
|
||||
return;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<PresenceModel> model = nullptr;
|
||||
XmlParsingContext xmlContext = XmlParsingContext(body);
|
||||
if (xmlContext.isValid()) model = PresenceModel::parsePidfXmlPresence(xmlContext);
|
||||
else ms_warning("Wrongly formatted presence XML: %s", xmlContext.getError().c_str());
|
||||
if (xmlContext.isValid()) {
|
||||
model = PresenceModel::parsePidfXmlPresence(xmlContext);
|
||||
} else {
|
||||
ms_warning("Wrongly formatted presence XML: %s", xmlContext.getError().c_str());
|
||||
}
|
||||
|
||||
*result = (SalPresenceModel *)(model ? linphone_presence_model_ref(model->toC()) : nullptr);
|
||||
return (SalPresenceModel *)(model ? linphone_presence_model_ref(model->toC()) : nullptr);
|
||||
}
|
||||
|
||||
std::shared_ptr<PresenceModel> PresenceModel::parsePidfXmlPresence(XmlParsingContext &xmlContext) {
|
||||
int err;
|
||||
|
||||
if (xmlContext.createXpathContext() < 0) return nullptr;
|
||||
if (xmlContext.createXpathContext() < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<PresenceModel> model = PresenceModel::create();
|
||||
xmlXPathRegisterNs(xmlContext.getXpathContext(), reinterpret_cast<const xmlChar *>("pidf"),
|
||||
|
|
@ -674,7 +778,9 @@ std::shared_ptr<PresenceModel> PresenceModel::parsePidfXmlPresence(XmlParsingCon
|
|||
err = model->parsePidfXmlPresenceNotes(xmlContext);
|
||||
}
|
||||
|
||||
if (err < 0) model = nullptr;
|
||||
if (err < 0) {
|
||||
model = nullptr;
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
|
|
@ -708,12 +814,11 @@ time_t PresenceModel::parseTimestamp(const std::string ×tamp) {
|
|||
|
||||
#else
|
||||
|
||||
void PresenceModel::parsePresence(BCTBX_UNUSED(const std::string &contentType),
|
||||
BCTBX_UNUSED(const std::string &contentSubtype),
|
||||
BCTBX_UNUSED(const std::string &body),
|
||||
SalPresenceModel **result) {
|
||||
if (result) *result = nullptr;
|
||||
SalPresenceModel *PresenceModel::parsePresence(BCTBX_UNUSED(const std::string &contentType),
|
||||
BCTBX_UNUSED(const std::string &contentSubtype),
|
||||
BCTBX_UNUSED(const std::string &body)) {
|
||||
ms_warning("PresenceModel::parsePresence(): stubbed.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#endif /* HAVE_XML2 */
|
||||
|
|
@ -728,7 +833,9 @@ int PresenceModel::timestampToXml(xmlTextWriterPtr writer, time_t timestamp, con
|
|||
err = xmlTextWriterWriteElementNS(writer, (const xmlChar *)L_STRING_TO_C(ns), (const xmlChar *)"timestamp",
|
||||
nullptr, (const xmlChar *)timestamp_str);
|
||||
}
|
||||
if (timestamp_str) ms_free(timestamp_str);
|
||||
if (timestamp_str != nullptr) {
|
||||
ms_free(timestamp_str);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
#endif /* HAVE_XML2 */
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2023 Belledonne Communications SARL.
|
||||
* Copyright (c) 2010-2026 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of Liblinphone
|
||||
* (see https://gitlab.linphone.org/BC/public/liblinphone).
|
||||
|
|
@ -82,7 +82,7 @@ public:
|
|||
std::shared_ptr<PresenceActivity> getActivity() const;
|
||||
LinphonePresenceBasicStatus getBasicStatus() const;
|
||||
int getCapabilities() const;
|
||||
float getCapabilityVersion(const LinphoneFriendCapability capability) const;
|
||||
float getCapabilityVersion(LinphoneFriendCapability capability) const;
|
||||
LinphoneConsolidatedPresence getConsolidatedPresence() const;
|
||||
const std::string &getContact() const;
|
||||
time_t getLatestActivityTimestamp() const;
|
||||
|
|
@ -106,26 +106,27 @@ public:
|
|||
void clearNotes();
|
||||
void clearPersons();
|
||||
void clearServices();
|
||||
bool hasCapability(const LinphoneFriendCapability capability) const;
|
||||
bool hasCapabilityWithVersion(const LinphoneFriendCapability capability, float version) const;
|
||||
bool hasCapabilityWithVersionOrMore(const LinphoneFriendCapability capability, float version) const;
|
||||
bool hasCapability(LinphoneFriendCapability capability) const;
|
||||
bool hasCapabilityWithVersion(LinphoneFriendCapability capability, float version) const;
|
||||
bool hasCapabilityWithVersionOrMore(LinphoneFriendCapability capability, float version) const;
|
||||
bool isOnline() const;
|
||||
void hasBeenPublished();
|
||||
|
||||
#ifdef HAVE_XML2
|
||||
int parsePidfXmlPresenceNotes(XmlParsingContext &xmlContext);
|
||||
int parsePidfXmlPresencePersons(XmlParsingContext &xmlContext);
|
||||
int parsePidfXmlPresenceServices(XmlParsingContext &xmlContext);
|
||||
|
||||
#endif /* HAVE_XML2 */
|
||||
|
||||
static SalPresenceModel *
|
||||
parsePresence(const std::string &contentType, const std::string &contentSubtype, const std::string &body);
|
||||
|
||||
private:
|
||||
std::shared_ptr<PresenceNote> findNoteWithLang(const std::string &lang) const;
|
||||
std::string toXml() const;
|
||||
static std::string basicStatusToString(const LinphonePresenceBasicStatus status);
|
||||
static std::string basicStatusToString(LinphonePresenceBasicStatus status);
|
||||
static std::string generatePresenceId();
|
||||
static void parsePresence(const std::string &contentType,
|
||||
const std::string &contentSubtype,
|
||||
const std::string &body,
|
||||
SalPresenceModel **result);
|
||||
static time_t parseTimestamp(const std::string ×tamp);
|
||||
|
||||
#ifdef HAVE_XML2
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2023 Belledonne Communications SARL.
|
||||
* Copyright (c) 2010-2026 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of Liblinphone
|
||||
* (see https://gitlab.linphone.org/BC/public/liblinphone).
|
||||
|
|
@ -47,8 +47,11 @@ PresencePerson *PresencePerson::clone() const {
|
|||
// -----------------------------------------------------------------------------
|
||||
|
||||
void PresencePerson::setId(const std::string &id) {
|
||||
if (id.empty()) mId = PresenceModel::generatePresenceId();
|
||||
else mId = id;
|
||||
if (id.empty()) {
|
||||
mId = PresenceModel::generatePresenceId();
|
||||
} else {
|
||||
mId = id;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
@ -69,7 +72,15 @@ unsigned int PresencePerson::getNbNotes() const {
|
|||
return static_cast<unsigned int>(mNotes.size());
|
||||
}
|
||||
|
||||
const std::shared_ptr<PresenceActivity> PresencePerson::getNthActivity(unsigned int idx) const {
|
||||
unsigned int PresencePerson::getNbPermanentActivities() const {
|
||||
return static_cast<unsigned int>(mPermanentActivities.size());
|
||||
}
|
||||
|
||||
unsigned int PresencePerson::getNbPermanentActivitiesNotes() const {
|
||||
return static_cast<unsigned int>(mPermanentActivitiesNotes.size());
|
||||
}
|
||||
|
||||
std::shared_ptr<PresenceActivity> PresencePerson::getNthActivity(unsigned int idx) const {
|
||||
try {
|
||||
return mActivities.at(idx);
|
||||
} catch (std::out_of_range &) {
|
||||
|
|
@ -77,7 +88,7 @@ const std::shared_ptr<PresenceActivity> PresencePerson::getNthActivity(unsigned
|
|||
}
|
||||
}
|
||||
|
||||
const std::shared_ptr<PresenceNote> PresencePerson::getNthActivitiesNote(unsigned int idx) const {
|
||||
std::shared_ptr<PresenceNote> PresencePerson::getNthActivitiesNote(unsigned int idx) const {
|
||||
try {
|
||||
return mActivitiesNotes.at(idx);
|
||||
} catch (std::out_of_range &) {
|
||||
|
|
@ -85,7 +96,7 @@ const std::shared_ptr<PresenceNote> PresencePerson::getNthActivitiesNote(unsigne
|
|||
}
|
||||
}
|
||||
|
||||
const std::shared_ptr<PresenceNote> PresencePerson::getNthNote(unsigned int idx) const {
|
||||
std::shared_ptr<PresenceNote> PresencePerson::getNthNote(unsigned int idx) const {
|
||||
try {
|
||||
return mNotes.at(idx);
|
||||
} catch (std::out_of_range &) {
|
||||
|
|
@ -93,6 +104,22 @@ const std::shared_ptr<PresenceNote> PresencePerson::getNthNote(unsigned int idx)
|
|||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<PresenceActivity> PresencePerson::getNthPermanentActivity(unsigned int idx) const {
|
||||
try {
|
||||
return mPermanentActivities.at(idx);
|
||||
} catch (std::out_of_range &) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<PresenceNote> PresencePerson::getNthPermanentActivitiesNote(unsigned int idx) const {
|
||||
try {
|
||||
return mPermanentActivitiesNotes.at(idx);
|
||||
} catch (std::out_of_range &) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
time_t PresencePerson::getTimestamp() const {
|
||||
return mTimestamp;
|
||||
}
|
||||
|
|
@ -100,24 +127,49 @@ time_t PresencePerson::getTimestamp() const {
|
|||
// -----------------------------------------------------------------------------
|
||||
|
||||
LinphoneStatus PresencePerson::addActivity(const std::shared_ptr<PresenceActivity> &activity) {
|
||||
if (activity == nullptr) return -1;
|
||||
if (activity == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
// Insert in first position since its the most recent activity!
|
||||
mActivities.insert(mActivities.cbegin(), activity);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LinphoneStatus PresencePerson::addActivitiesNote(const std::shared_ptr<PresenceNote> ¬e) {
|
||||
if (note == nullptr) return -1;
|
||||
if (note == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
mNotes.insert(mActivitiesNotes.cbegin(), note);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LinphoneStatus PresencePerson::addNote(const std::shared_ptr<PresenceNote> ¬e) {
|
||||
if (note == nullptr) return -1;
|
||||
if (note == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
mNotes.insert(mNotes.cbegin(), note);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LinphoneStatus PresencePerson::addPermanentActivity(const std::shared_ptr<PresenceActivity> &activity) {
|
||||
if (activity == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
// Insert in first position since its the most recent activity!
|
||||
mPermanentActivities.insert(mPermanentActivities.cbegin(), activity);
|
||||
mPermanentActivitiesToBePublished = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
LinphoneStatus PresencePerson::addPermanentActivitiesNote(const std::shared_ptr<PresenceNote> ¬e) {
|
||||
if (note == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
mPermanentActivitiesNotes.insert(mPermanentActivitiesNotes.cbegin(), note);
|
||||
mPermanentActivitiesToBePublished = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PresencePerson::clearActivities() {
|
||||
mActivities.clear();
|
||||
}
|
||||
|
|
@ -130,6 +182,20 @@ void PresencePerson::clearNotes() {
|
|||
mNotes.clear();
|
||||
}
|
||||
|
||||
void PresencePerson::clearPermanentActivities() {
|
||||
if (!mPermanentActivities.empty()) {
|
||||
mPermanentActivities.clear();
|
||||
mPermanentActivitiesToBePublished = true;
|
||||
}
|
||||
}
|
||||
|
||||
void PresencePerson::clearPermanentActivitiesNotes() {
|
||||
if (!mPermanentActivitiesNotes.empty()) {
|
||||
mPermanentActivitiesNotes.clear();
|
||||
mPermanentActivitiesToBePublished = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool PresencePerson::hasActivities() const {
|
||||
return !mActivities.empty();
|
||||
}
|
||||
|
|
@ -142,6 +208,14 @@ bool PresencePerson::hasNotes() const {
|
|||
return !mNotes.empty();
|
||||
}
|
||||
|
||||
bool PresencePerson::hasPermanentActivities() const {
|
||||
return !mPermanentActivities.empty();
|
||||
}
|
||||
|
||||
bool PresencePerson::hasPermanentActivitiesNotes() const {
|
||||
return !mPermanentActivitiesNotes.empty();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#ifdef HAVE_XML2
|
||||
|
|
@ -150,36 +224,47 @@ int PresencePerson::parsePidfXmlPresenceActivities(XmlParsingContext &xmlContext
|
|||
int err = 0;
|
||||
ss << PresencePerson::pidfXmlPrefix.data() << "[" << personIdx << "]/rpid:activities";
|
||||
xmlXPathObjectPtr activitiesNodesObject = xmlContext.getXpathObjectForNodeList(ss.str());
|
||||
if (activitiesNodesObject && activitiesNodesObject->nodesetval) {
|
||||
if ((activitiesNodesObject != nullptr) && (activitiesNodesObject->nodesetval != nullptr)) {
|
||||
for (int i = 1; i <= activitiesNodesObject->nodesetval->nodeNr; i++) {
|
||||
ss.clear(), ss.str(std::string()),
|
||||
ss << PresencePerson::pidfXmlPrefix.data() << "[" << personIdx << "]/rpid:activities[" << i
|
||||
<< "]/rpid:*";
|
||||
xmlXPathObjectPtr activitiesObject = xmlContext.getXpathObjectForNodeList(ss.str());
|
||||
if (activitiesObject && activitiesObject->nodesetval) {
|
||||
if ((activitiesObject != nullptr) && (activitiesObject->nodesetval != nullptr)) {
|
||||
for (int j = 0; j < activitiesObject->nodesetval->nodeNr; j++) {
|
||||
xmlNodePtr activityNode = activitiesObject->nodesetval->nodeTab[j];
|
||||
if (activityNode->name && PresenceActivity::isValidActivityName((const char *)activityNode->name)) {
|
||||
if ((activityNode->name != nullptr) &&
|
||||
PresenceActivity::isValidActivityName((const char *)activityNode->name)) {
|
||||
LinphonePresenceActivityType activityType;
|
||||
char *description = (char *)xmlNodeGetContent(activityNode);
|
||||
if (description && (description[0] == '\0')) {
|
||||
if ((description != nullptr) && (description[0] == '\0')) {
|
||||
xmlFree(description);
|
||||
description = nullptr;
|
||||
}
|
||||
err = PresenceActivity::activityNameToType((const char *)activityNode->name, &activityType);
|
||||
if (err < 0) break;
|
||||
if (err < 0) {
|
||||
break;
|
||||
}
|
||||
std::shared_ptr<PresenceActivity> activity =
|
||||
PresenceActivity::create(activityType, L_C_TO_STRING(description));
|
||||
addActivity(activity);
|
||||
if (description) xmlFree(description);
|
||||
if (description != nullptr) {
|
||||
xmlFree(description);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (activitiesObject) xmlXPathFreeObject(activitiesObject);
|
||||
if (err < 0) break;
|
||||
if (activitiesObject != nullptr) {
|
||||
xmlXPathFreeObject(activitiesObject);
|
||||
}
|
||||
if (err < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (activitiesNodesObject) xmlXPathFreeObject(activitiesNodesObject);
|
||||
if (activitiesNodesObject != nullptr) {
|
||||
xmlXPathFreeObject(activitiesNodesObject);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
@ -188,13 +273,15 @@ int PresencePerson::parsePidfXmlPresenceNotes(XmlParsingContext &xmlContext, uns
|
|||
stringstream ss;
|
||||
ss << PresencePerson::pidfXmlPrefix.data() << "[" << personIdx << "]/rpid:activities/rpid:note";
|
||||
xmlXPathObjectPtr noteObject = xmlContext.getXpathObjectForNodeList(ss.str());
|
||||
if (noteObject && noteObject->nodesetval) {
|
||||
if ((noteObject != nullptr) && (noteObject->nodesetval != nullptr)) {
|
||||
for (int i = 1; i <= noteObject->nodesetval->nodeNr; i++) {
|
||||
ss.clear(), ss.str(std::string()),
|
||||
ss << PresencePerson::pidfXmlPrefix.data() << "[" << personIdx << "]/rpid:activities/rpid:note[" << i
|
||||
<< "]";
|
||||
std::string noteStr = xmlContext.getTextContent(ss.str());
|
||||
if (noteStr.empty()) continue;
|
||||
if (noteStr.empty()) {
|
||||
continue;
|
||||
}
|
||||
ss.clear(), ss.str(std::string()),
|
||||
ss << PresencePerson::pidfXmlPrefix.data() << "[" << personIdx << "]/rpid:activities/rpid:note[" << i
|
||||
<< "]/@xml:lang";
|
||||
|
|
@ -203,15 +290,42 @@ int PresencePerson::parsePidfXmlPresenceNotes(XmlParsingContext &xmlContext, uns
|
|||
addActivitiesNote(note);
|
||||
}
|
||||
}
|
||||
if (noteObject) xmlXPathFreeObject(noteObject);
|
||||
if (noteObject != nullptr) {
|
||||
xmlXPathFreeObject(noteObject);
|
||||
}
|
||||
ss.clear(), ss.str(std::string()),
|
||||
ss << PresencePerson::pidfXmlPrefix.data() << "[" << personIdx << "]/rpid:permanent-activities/rpid:note";
|
||||
noteObject = xmlContext.getXpathObjectForNodeList(ss.str());
|
||||
if ((noteObject != nullptr) && (noteObject->nodesetval != nullptr)) {
|
||||
for (int i = 1; i <= noteObject->nodesetval->nodeNr; i++) {
|
||||
ss.clear(), ss.str(std::string()),
|
||||
ss << PresencePerson::pidfXmlPrefix.data() << "[" << personIdx
|
||||
<< "]/rpid:permanent-activities/rpid:note[" << i << "]";
|
||||
std::string noteStr = xmlContext.getTextContent(ss.str());
|
||||
if (noteStr.empty()) {
|
||||
continue;
|
||||
}
|
||||
ss.clear(), ss.str(std::string()),
|
||||
ss << PresencePerson::pidfXmlPrefix.data() << "[" << personIdx
|
||||
<< "]/rpid:permanent-activities/rpid:note[" << i << "]/@xml:lang";
|
||||
std::string lang = xmlContext.getTextContent(ss.str());
|
||||
std::shared_ptr<PresenceNote> note = PresenceNote::create(noteStr, lang);
|
||||
addPermanentActivitiesNote(note);
|
||||
}
|
||||
}
|
||||
if (noteObject != nullptr) {
|
||||
xmlXPathFreeObject(noteObject);
|
||||
}
|
||||
ss.clear(), ss.str(std::string()), ss << PresencePerson::pidfXmlPrefix.data() << "[" << personIdx << "]/dm:note";
|
||||
noteObject = xmlContext.getXpathObjectForNodeList(ss.str());
|
||||
if (noteObject && noteObject->nodesetval) {
|
||||
if ((noteObject != nullptr) && (noteObject->nodesetval != nullptr)) {
|
||||
for (int i = 1; i <= noteObject->nodesetval->nodeNr; i++) {
|
||||
ss.clear(), ss.str(std::string()),
|
||||
ss << PresencePerson::pidfXmlPrefix.data() << "[" << personIdx << "]/dm:note[" << i << "]";
|
||||
std::string noteStr = xmlContext.getTextContent(ss.str());
|
||||
if (noteStr.empty()) continue;
|
||||
if (noteStr.empty()) {
|
||||
continue;
|
||||
}
|
||||
ss.clear(), ss.str(std::string()),
|
||||
ss << PresencePerson::pidfXmlPrefix.data() << "[" << personIdx << "]/dm:note[" << i << "]/@xml:lang";
|
||||
std::string lang = xmlContext.getTextContent(ss.str());
|
||||
|
|
@ -219,27 +333,85 @@ int PresencePerson::parsePidfXmlPresenceNotes(XmlParsingContext &xmlContext, uns
|
|||
addNote(note);
|
||||
}
|
||||
}
|
||||
if (noteObject) xmlXPathFreeObject(noteObject);
|
||||
if (noteObject != nullptr) {
|
||||
xmlXPathFreeObject(noteObject);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PresencePerson::parsePidfXmlPresencePermanentActivities(XmlParsingContext &xmlContext, unsigned int personIdx) {
|
||||
stringstream ss;
|
||||
int err = 0;
|
||||
ss << PresencePerson::pidfXmlPrefix.data() << "[" << personIdx << "]/rpid:permanent-activities";
|
||||
xmlXPathObjectPtr permanentActivitiesNodesObject = xmlContext.getXpathObjectForNodeList(ss.str());
|
||||
if ((permanentActivitiesNodesObject != nullptr) && (permanentActivitiesNodesObject->nodesetval != nullptr)) {
|
||||
for (int i = 1; i <= permanentActivitiesNodesObject->nodesetval->nodeNr; i++) {
|
||||
ss.clear(), ss.str(std::string()),
|
||||
ss << PresencePerson::pidfXmlPrefix.data() << "[" << personIdx << "]/rpid:permanent-activities[" << i
|
||||
<< "]/rpid:*";
|
||||
xmlXPathObjectPtr permanentActivitiesObject = xmlContext.getXpathObjectForNodeList(ss.str());
|
||||
if ((permanentActivitiesObject != nullptr) && (permanentActivitiesObject->nodesetval != nullptr)) {
|
||||
for (int j = 0; j < permanentActivitiesObject->nodesetval->nodeNr; j++) {
|
||||
xmlNodePtr activityNode = permanentActivitiesObject->nodesetval->nodeTab[j];
|
||||
if ((activityNode->name != nullptr) &&
|
||||
PresenceActivity::isValidActivityName((const char *)activityNode->name)) {
|
||||
LinphonePresenceActivityType activityType;
|
||||
char *description = (char *)xmlNodeGetContent(activityNode);
|
||||
if ((description != nullptr) && (description[0] == '\0')) {
|
||||
xmlFree(description);
|
||||
description = nullptr;
|
||||
}
|
||||
err = PresenceActivity::activityNameToType((const char *)activityNode->name, &activityType);
|
||||
if (err < 0) {
|
||||
break;
|
||||
}
|
||||
std::shared_ptr<PresenceActivity> activity =
|
||||
PresenceActivity::create(activityType, L_C_TO_STRING(description));
|
||||
addPermanentActivity(activity);
|
||||
if (description != nullptr) {
|
||||
xmlFree(description);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (permanentActivitiesObject != nullptr) {
|
||||
xmlXPathFreeObject(permanentActivitiesObject);
|
||||
}
|
||||
if (err < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (permanentActivitiesNodesObject != nullptr) {
|
||||
xmlXPathFreeObject(permanentActivitiesNodesObject);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int PresencePerson::toXml(xmlTextWriterPtr writer) const {
|
||||
int err = xmlTextWriterStartElementNS(writer, (const xmlChar *)"dm", (const xmlChar *)"person", nullptr);
|
||||
if (err >= 0) {
|
||||
std::string id = getId();
|
||||
if (id.empty()) id = PresenceModel::generatePresenceId();
|
||||
if (id.empty()) {
|
||||
id = PresenceModel::generatePresenceId();
|
||||
}
|
||||
err = xmlTextWriterWriteAttribute(writer, (const xmlChar *)"id", (const xmlChar *)L_STRING_TO_C(id));
|
||||
}
|
||||
if ((err >= 0) && (hasActivitiesNotes() || hasActivities())) {
|
||||
err = xmlTextWriterStartElementNS(writer, (const xmlChar *)"rpid", (const xmlChar *)"activities", nullptr);
|
||||
if ((err >= 0) && hasActivitiesNotes()) {
|
||||
for (const auto ¬e : mActivitiesNotes) {
|
||||
if (err >= 0) err = note->toXml(writer, "rpid");
|
||||
if (err >= 0) {
|
||||
err = note->toXml(writer, "rpid");
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((err >= 0) && hasActivities()) {
|
||||
for (const auto &activity : mActivities) {
|
||||
if (err >= 0) err = activity->toXml(writer);
|
||||
if (err >= 0) {
|
||||
err = activity->toXml(writer);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (err >= 0) {
|
||||
|
|
@ -247,9 +419,33 @@ int PresencePerson::toXml(xmlTextWriterPtr writer) const {
|
|||
err = xmlTextWriterEndElement(writer);
|
||||
}
|
||||
}
|
||||
if ((err >= 0) && mPermanentActivitiesToBePublished) {
|
||||
err = xmlTextWriterStartElementNS(writer, (const xmlChar *)"rpid", (const xmlChar *)"permanent-activities",
|
||||
nullptr);
|
||||
if ((err >= 0) && hasPermanentActivitiesNotes()) {
|
||||
for (const auto ¬e : mPermanentActivitiesNotes) {
|
||||
if (err >= 0) {
|
||||
err = note->toXml(writer, "rpid");
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((err >= 0) && hasPermanentActivities()) {
|
||||
for (const auto &activity : mPermanentActivities) {
|
||||
if (err >= 0) {
|
||||
err = activity->toXml(writer);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (err >= 0) {
|
||||
/* Close the "permanent-activities" element. */
|
||||
err = xmlTextWriterEndElement(writer);
|
||||
}
|
||||
}
|
||||
if ((err >= 0) && hasNotes()) {
|
||||
for (const auto ¬e : mNotes) {
|
||||
if (err >= 0) err = note->toXml(writer, "dm");
|
||||
if (err >= 0) {
|
||||
err = note->toXml(writer, "dm");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (err >= 0) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2023 Belledonne Communications SARL.
|
||||
* Copyright (c) 2010-2026 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of Liblinphone
|
||||
* (see https://gitlab.linphone.org/BC/public/liblinphone).
|
||||
|
|
@ -59,26 +59,37 @@ public:
|
|||
unsigned int getNbActivities() const;
|
||||
unsigned int getNbActivitiesNotes() const;
|
||||
unsigned int getNbNotes() const;
|
||||
const std::shared_ptr<PresenceActivity> getNthActivity(unsigned int idx) const;
|
||||
const std::shared_ptr<PresenceNote> getNthActivitiesNote(unsigned int idx) const;
|
||||
const std::shared_ptr<PresenceNote> getNthNote(unsigned int idx) const;
|
||||
unsigned int getNbPermanentActivities() const;
|
||||
unsigned int getNbPermanentActivitiesNotes() const;
|
||||
std::shared_ptr<PresenceActivity> getNthActivity(unsigned int idx) const;
|
||||
std::shared_ptr<PresenceNote> getNthActivitiesNote(unsigned int idx) const;
|
||||
std::shared_ptr<PresenceNote> getNthNote(unsigned int idx) const;
|
||||
std::shared_ptr<PresenceActivity> getNthPermanentActivity(unsigned int idx) const;
|
||||
std::shared_ptr<PresenceNote> getNthPermanentActivitiesNote(unsigned int idx) const;
|
||||
time_t getTimestamp() const;
|
||||
|
||||
// Other
|
||||
LinphoneStatus addActivity(const std::shared_ptr<PresenceActivity> &activity);
|
||||
LinphoneStatus addActivitiesNote(const std::shared_ptr<PresenceNote> ¬e);
|
||||
LinphoneStatus addNote(const std::shared_ptr<PresenceNote> ¬e);
|
||||
LinphoneStatus addPermanentActivity(const std::shared_ptr<PresenceActivity> &activity);
|
||||
LinphoneStatus addPermanentActivitiesNote(const std::shared_ptr<PresenceNote> ¬e);
|
||||
void clearActivities();
|
||||
void clearActivitiesNotes();
|
||||
void clearNotes();
|
||||
void clearPermanentActivities();
|
||||
void clearPermanentActivitiesNotes();
|
||||
bool hasActivities() const;
|
||||
bool hasActivitiesNotes() const;
|
||||
bool hasNotes() const;
|
||||
bool hasPermanentActivities() const;
|
||||
bool hasPermanentActivitiesNotes() const;
|
||||
|
||||
private:
|
||||
#ifdef HAVE_XML2
|
||||
int parsePidfXmlPresenceActivities(XmlParsingContext &xmlContext, unsigned int personIdx);
|
||||
int parsePidfXmlPresenceNotes(XmlParsingContext &xmlContext, unsigned int personIdx);
|
||||
int parsePidfXmlPresencePermanentActivities(XmlParsingContext &xmlContext, unsigned int personIdx);
|
||||
int toXml(xmlTextWriterPtr writer) const;
|
||||
|
||||
static constexpr std::string_view pidfXmlPrefix = "/pidf:presence/dm:person";
|
||||
|
|
@ -89,6 +100,9 @@ private:
|
|||
std::vector<std::shared_ptr<PresenceActivity>> mActivities;
|
||||
std::vector<std::shared_ptr<PresenceNote>> mActivitiesNotes;
|
||||
std::vector<std::shared_ptr<PresenceNote>> mNotes;
|
||||
std::vector<std::shared_ptr<PresenceActivity>> mPermanentActivities;
|
||||
std::vector<std::shared_ptr<PresenceNote>> mPermanentActivitiesNotes;
|
||||
bool mPermanentActivitiesToBePublished = false;
|
||||
};
|
||||
|
||||
LINPHONE_END_NAMESPACE
|
||||
|
|
|
|||
|
|
@ -299,7 +299,6 @@ set(SOURCE_FILES_C
|
|||
log_file_tester.c
|
||||
message_tester.c
|
||||
player_tester.c
|
||||
presence_server_tester.c
|
||||
presence_tester.c
|
||||
proxy_config_tester.c
|
||||
quality_reporting_tester.c
|
||||
|
|
@ -363,6 +362,7 @@ set(SOURCE_FILES_CXX
|
|||
local-secure-chat-tester.cpp
|
||||
local-ephemeral-chat-tester.cpp
|
||||
potential_configuration_tester.cpp
|
||||
presence_server_tester.cpp
|
||||
turn-server-tester.cpp
|
||||
vcard_tester.cpp
|
||||
vfs-encryption-tester.cpp
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2022 Belledonne Communications SARL.
|
||||
* Copyright (c) 2010-2026 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of Liblinphone
|
||||
* (see https://gitlab.linphone.org/BC/public/liblinphone).
|
||||
|
|
@ -18,6 +18,9 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "bctoolbox/charconv.h"
|
||||
#include "bctoolbox/tester.h"
|
||||
#include "belle-sip/object.h"
|
||||
#include "liblinphone_tester.h"
|
||||
#include "linphone/api/c-account-params.h"
|
||||
#include "linphone/api/c-account.h"
|
||||
|
|
@ -25,9 +28,12 @@
|
|||
#include "linphone/api/c-dial-plan.h"
|
||||
#include "linphone/api/c-friend.h"
|
||||
#include "linphone/api/c-magic-search.h"
|
||||
#include "linphone/api/c-presence.h"
|
||||
#include "linphone/api/c-search-result.h"
|
||||
#include "linphone/core.h"
|
||||
#include "linphone/core_utils.h"
|
||||
#include "linphone/types.h"
|
||||
#include "presence/presence-model.h"
|
||||
#include "private_functions.h"
|
||||
#include "tester_utils.h"
|
||||
|
||||
// List coming from dialplan
|
||||
|
|
@ -135,10 +141,10 @@ char *generate_random_e164_phone(void) {
|
|||
!check_phone_country(atoi(linphone_dial_plan_get_country_calling_code(dialPlan))) ||
|
||||
(strcmp("52", linphone_dial_plan_get_country_calling_code(dialPlan)) == 0))
|
||||
; // fixme Linphone Mexican's dialplan has 2 ccc
|
||||
belle_sip_object_remove_from_leak_detector((void *)genericDialPlan);
|
||||
belle_sip_object_remove_from_leak_detector((belle_sip_object_t *)genericDialPlan);
|
||||
belle_sip_object_remove_from_leak_detector(
|
||||
(void *)dialPlan); // because mostCommon dial plan is a static object freed at the end of the process. This f is
|
||||
// only to avoid wrong leak detection.
|
||||
(belle_sip_object_t *)dialPlan); // because mostCommon dial plan is a static object freed at the end of the
|
||||
// process. This f is only to avoid wrong leak detection.
|
||||
|
||||
return generate_random_e164_phone_from_dial_plan(dialPlan);
|
||||
}
|
||||
|
|
@ -501,6 +507,7 @@ static void test_presence_list_base(bool_t enable_compression) {
|
|||
const char *pauline_identity;
|
||||
bctbx_list_t *lcs = NULL;
|
||||
LinphonePresenceModel *presence;
|
||||
int previous_laure_number_of_NotifyPresenceReceived = 0;
|
||||
|
||||
enable_publish_verified(marie, TRUE);
|
||||
enable_publish_verified(pauline, TRUE);
|
||||
|
|
@ -634,7 +641,7 @@ static void test_presence_list_base(bool_t enable_compression) {
|
|||
linphone_core_set_presence_model(marie->lc, presence);
|
||||
linphone_presence_model_unref(presence);
|
||||
|
||||
int previous_laure_number_of_NotifyPresenceReceived = laure->stat.number_of_NotifyPresenceReceived;
|
||||
previous_laure_number_of_NotifyPresenceReceived = laure->stat.number_of_NotifyPresenceReceived;
|
||||
wait_for_list(lcs, &laure->stat.number_of_NotifyPresenceReceived,
|
||||
previous_laure_number_of_NotifyPresenceReceived + 1, liblinphone_tester_sip_timeout);
|
||||
BC_ASSERT_EQUAL(laure->stat.number_of_NotifyPresenceReceived, previous_laure_number_of_NotifyPresenceReceived + 1,
|
||||
|
|
@ -809,6 +816,8 @@ static void test_presence_list_same_friend_two_addresses(void) {
|
|||
LinphoneFriend *lf;
|
||||
const char *marie_identity;
|
||||
bctbx_list_t *lcs = NULL;
|
||||
const LinphonePresenceModel *marie_presence_model = nullptr;
|
||||
LinphoneConsolidatedPresence marie_presence = LinphoneConsolidatedPresenceOffline;
|
||||
|
||||
enable_publish_verified(marie, TRUE);
|
||||
enable_publish_verified(pauline, TRUE);
|
||||
|
|
@ -851,11 +860,9 @@ static void test_presence_list_same_friend_two_addresses(void) {
|
|||
BC_ASSERT_EQUAL(linphone_friend_get_consolidated_presence(lf), LinphoneConsolidatedPresenceOnline, int, "%d");
|
||||
if (!BC_ASSERT_TRUE(linphone_friend_is_presence_received(lf))) goto end;
|
||||
|
||||
const LinphonePresenceModel *marie_presence_model =
|
||||
linphone_friend_get_presence_model_for_uri_or_tel(lf, marie_identity);
|
||||
marie_presence_model = linphone_friend_get_presence_model_for_uri_or_tel(lf, marie_identity);
|
||||
BC_ASSERT_PTR_NOT_NULL(marie_presence_model);
|
||||
LinphoneConsolidatedPresence marie_presence =
|
||||
linphone_presence_model_get_consolidated_presence(marie_presence_model);
|
||||
marie_presence = linphone_presence_model_get_consolidated_presence(marie_presence_model);
|
||||
BC_ASSERT_EQUAL(marie_presence, LinphoneConsolidatedPresenceBusy, int, "%d");
|
||||
|
||||
end:
|
||||
|
|
@ -1010,19 +1017,19 @@ static void test_presence_list_subscribe_with_error(bool_t io_error) {
|
|||
wait_for_until(laure->lc, pauline->lc, &laure->stat.number_of_LinphonePresenceActivityVacation, 2, 6000));
|
||||
if (io_error) {
|
||||
ms_message("Simulating socket error");
|
||||
sal_set_recv_error(linphone_core_get_sal(laure->lc), -1);
|
||||
linphone_core_get_sal(laure->lc)->setRecvError(-1);
|
||||
wait_for_list(lcs, &dummy, 1, 500); /* just time for socket to be closed */
|
||||
} else {
|
||||
ms_message("Simulating in/out packets losses");
|
||||
sal_set_send_error(linphone_core_get_sal(laure->lc),
|
||||
1500); /*make sure no refresh is sent, trash the message without generating error*/
|
||||
sal_set_recv_error(linphone_core_get_sal(laure->lc),
|
||||
1500); /*make sure server notify to close the dialog is also ignored*/
|
||||
linphone_core_get_sal(laure->lc)->setSendError(
|
||||
1500); /*make sure no refresh is sent, trash the message without generating error*/
|
||||
linphone_core_get_sal(laure->lc)->setRecvError(
|
||||
1500); /*make sure server notify to close the dialog is also ignored*/
|
||||
wait_for_list(lcs, &dummy, 1, 32000); /* Wait a little bit for the subscribe transaction to timeout */
|
||||
}
|
||||
/*restart normal behavior*/
|
||||
sal_set_send_error(linphone_core_get_sal(laure->lc), 0);
|
||||
sal_set_recv_error(linphone_core_get_sal(laure->lc), 1);
|
||||
linphone_core_get_sal(laure->lc)->setSendError(0);
|
||||
linphone_core_get_sal(laure->lc)->setRecvError(1);
|
||||
/*a new subscribe should be sent */
|
||||
|
||||
BC_ASSERT_TRUE(
|
||||
|
|
@ -1436,9 +1443,9 @@ static void long_term_presence_with_phone_without_sip(void) {
|
|||
|
||||
linphone_friend_unref(friend2);
|
||||
belle_sip_object_remove_from_leak_detector(
|
||||
(void *)dialPlan); // because mostCommon dial plan is a static object freed at the end of the process. This
|
||||
// f is only to avoid wrong leak detection.
|
||||
belle_sip_object_remove_from_leak_detector((void *)genericDialPlan);
|
||||
(belle_sip_object_t *)dialPlan); // because mostCommon dial plan is a static object freed at the end of the
|
||||
// process. This f is only to avoid wrong leak detection.
|
||||
belle_sip_object_remove_from_leak_detector((belle_sip_object_t *)genericDialPlan);
|
||||
linphone_core_manager_destroy(pauline);
|
||||
bctbx_free(e164);
|
||||
bctbx_free(phone);
|
||||
|
|
@ -2426,8 +2433,8 @@ static void notify_friend_capabilities_after_publish(void) {
|
|||
bctbx_list_t *lcs = NULL;
|
||||
bctbx_list_t *specs = NULL;
|
||||
|
||||
specs = bctbx_list_append(specs, "groupchat/1.1");
|
||||
specs = bctbx_list_append(specs, "lime/1.5");
|
||||
specs = bctbx_list_append(specs, (char *)"groupchat/1.1");
|
||||
specs = bctbx_list_append(specs, (char *)"lime/1.5");
|
||||
linphone_core_set_linphone_specs_list(pauline->lc, specs);
|
||||
bctbx_list_free(specs);
|
||||
|
||||
|
|
@ -2559,8 +2566,8 @@ static void notify_friend_capabilities_with_alias(void) {
|
|||
marie = linphone_core_manager_create2("marie_rc", e164Marie);
|
||||
linphone_config_set_bool(linphone_core_get_config(marie->lc), "lime", "enabled", FALSE);
|
||||
linphone_core_set_user_agent(marie->lc, "full-presence-support-bypass", NULL);
|
||||
specs = bctbx_list_append(specs, "groupchat/1.1");
|
||||
specs = bctbx_list_append(specs, "lime/1.5");
|
||||
specs = bctbx_list_append(specs, (char *)"groupchat/1.1");
|
||||
specs = bctbx_list_append(specs, (char *)"lime/1.5");
|
||||
linphone_core_set_linphone_specs_list(marie->lc, specs);
|
||||
bctbx_list_free(specs);
|
||||
linphone_core_manager_start(marie, TRUE);
|
||||
|
|
@ -2613,9 +2620,9 @@ static void notify_friend_capabilities_with_alias(void) {
|
|||
linphone_friend_unref(marieFriend);
|
||||
linphone_friend_unref(laureFriend);
|
||||
belle_sip_object_remove_from_leak_detector(
|
||||
(void *)dialPlan); // because mostCommon dial plan is a static object freed at the end of the process.
|
||||
// This f is only to avoid wrong leak detection.
|
||||
belle_sip_object_remove_from_leak_detector((void *)genericDialPlan);
|
||||
(belle_sip_object_t *)dialPlan); // because mostCommon dial plan is a static object freed at the end of the
|
||||
// process. This f is only to avoid wrong leak detection.
|
||||
belle_sip_object_remove_from_leak_detector((belle_sip_object_t *)genericDialPlan);
|
||||
linphone_core_manager_destroy(pauline);
|
||||
bctbx_free(e164Laure);
|
||||
bctbx_free(phoneLaure);
|
||||
|
|
@ -2648,8 +2655,8 @@ static void notify_search_result_capabilities_with_alias(void) {
|
|||
|
||||
marie = linphone_core_manager_create2("marie_rc", e164Marie);
|
||||
linphone_core_set_user_agent(marie->lc, "full-presence-support-bypass", NULL);
|
||||
specs = bctbx_list_append(specs, "groupchat/1.1");
|
||||
specs = bctbx_list_append(specs, "lime/1.5");
|
||||
specs = bctbx_list_append(specs, (char *)"groupchat/1.1");
|
||||
specs = bctbx_list_append(specs, (char *)"lime/1.5");
|
||||
linphone_core_set_linphone_specs_list(marie->lc, specs);
|
||||
bctbx_list_free(specs);
|
||||
linphone_core_manager_start(marie, TRUE);
|
||||
|
|
@ -2679,7 +2686,7 @@ static void notify_search_result_capabilities_with_alias(void) {
|
|||
|
||||
if (BC_ASSERT_PTR_NOT_NULL(resultList)) {
|
||||
BC_ASSERT_EQUAL((int)bctbx_list_size(resultList), 1, int, "%d");
|
||||
const LinphoneSearchResult *sr = bctbx_list_nth_data(resultList, 0);
|
||||
const auto *sr = reinterpret_cast<const LinphoneSearchResult *>(bctbx_list_nth_data(resultList, 0));
|
||||
BC_ASSERT_TRUE(linphone_search_result_has_capability(sr, LinphoneFriendCapabilityGroupChat));
|
||||
BC_ASSERT_TRUE(linphone_search_result_has_capability(sr, LinphoneFriendCapabilityLimeX3dh));
|
||||
|
||||
|
|
@ -2699,9 +2706,9 @@ static void notify_search_result_capabilities_with_alias(void) {
|
|||
linphone_friend_unref(marieFriend);
|
||||
|
||||
belle_sip_object_remove_from_leak_detector(
|
||||
(void *)dialPlan); // because mostCommon dial plan is a static object freed at the end of the process.
|
||||
// This f is only to avoid wrong leak detection.
|
||||
belle_sip_object_remove_from_leak_detector((void *)genericDialPlan);
|
||||
(belle_sip_object_t *)dialPlan); // because mostCommon dial plan is a static object freed at the end of the
|
||||
// process. This f is only to avoid wrong leak detection.
|
||||
belle_sip_object_remove_from_leak_detector((belle_sip_object_t *)genericDialPlan);
|
||||
linphone_core_manager_destroy(pauline);
|
||||
|
||||
bctbx_free(e164Marie);
|
||||
|
|
@ -2713,6 +2720,181 @@ static void notify_search_result_capabilities_with_alias(void) {
|
|||
}
|
||||
#endif
|
||||
|
||||
static void enable_echoed_presence_subscription(LinphoneCoreManager *mgr) {
|
||||
LinphoneAccountParams *account_params =
|
||||
linphone_account_params_clone(linphone_account_get_params(linphone_core_get_default_account(mgr->lc)));
|
||||
linphone_account_params_enable_echoed_presence_subscription(account_params, TRUE);
|
||||
linphone_account_set_params(linphone_core_get_default_account(mgr->lc), account_params);
|
||||
linphone_account_params_unref(account_params);
|
||||
}
|
||||
|
||||
/*
|
||||
* It is not possible to test a whole path of permanent activities since flexisip-presence does not handle it.
|
||||
* Test only the SUBSCRIBE path here, by checking that we get a NOTIFY for the local identity when the echoed presence
|
||||
* subscription is enabled. That validates that the local identity has correctly been added to the SUBSCRIBE.
|
||||
* Also check that we have an echoed presence model after receiving the NOTIFY.
|
||||
*/
|
||||
void permanent_activities_subscribe() {
|
||||
LinphoneCoreManager *laure = linphone_core_presence_manager_new("laure_tcp_rc");
|
||||
LinphoneCoreManager *pauline = linphone_core_presence_manager_new(
|
||||
transport_supported(LinphoneTransportTls) == TRUE ? "pauline_rc" : "pauline_tcp_rc");
|
||||
linphone_core_set_user_agent(laure->lc, "bypass", nullptr);
|
||||
linphone_core_set_user_agent(pauline->lc, "bypass", nullptr);
|
||||
|
||||
enable_echoed_presence_subscription(laure);
|
||||
enable_publish_verified(laure, TRUE);
|
||||
enable_publish_verified(pauline, TRUE);
|
||||
|
||||
const char *rls_uri = "sip:rls@sip.example.org";
|
||||
const char *pauline_identity = get_identity(pauline);
|
||||
bctbx_list_t *lcs = nullptr;
|
||||
lcs = bctbx_list_append(lcs, laure->lc);
|
||||
lcs = bctbx_list_append(lcs, pauline->lc);
|
||||
|
||||
LinphoneFriendList *lfl = linphone_core_create_friend_list(laure->lc);
|
||||
linphone_friend_list_set_rls_uri(lfl, rls_uri);
|
||||
LinphoneFriend *lf = linphone_core_create_friend_with_address(laure->lc, pauline_identity);
|
||||
linphone_friend_list_add_friend(lfl, lf);
|
||||
linphone_friend_unref(lf);
|
||||
linphone_core_add_friend_list(laure->lc, lfl);
|
||||
linphone_friend_list_update_subscriptions(lfl);
|
||||
linphone_friend_list_unref(lfl);
|
||||
|
||||
wait_for_list(lcs, &laure->stat.number_of_NotifyPresenceReceived, 2, liblinphone_tester_sip_timeout);
|
||||
|
||||
BC_ASSERT_PTR_NOT_NULL(linphone_account_get_echoed_presence_model(linphone_core_get_default_account(laure->lc)));
|
||||
|
||||
bctbx_list_free(lcs);
|
||||
linphone_core_manager_destroy(pauline);
|
||||
linphone_core_manager_destroy(laure);
|
||||
}
|
||||
|
||||
/*
|
||||
* It is not possible to test a whole path of permanent activities since flexisip-presence does not handle it.
|
||||
* Test only the PUBLISH path here, more precisely test the presence XML generation when adding and removing permanent
|
||||
* activities.
|
||||
*/
|
||||
void permanent_activities_publish() {
|
||||
LinphoneCoreManager *laure = linphone_core_manager_new("laure_tcp_rc");
|
||||
|
||||
LinphonePresenceModel *presence_model =
|
||||
linphone_presence_model_new_with_activity(LinphonePresenceActivityDinner, nullptr);
|
||||
linphone_presence_model_set_presentity(
|
||||
presence_model, linphone_account_params_get_identity_address(
|
||||
linphone_account_get_params(linphone_core_get_default_account(laure->lc))));
|
||||
|
||||
auto check_permanent_activities = [laure, presence_model](std::string lookup, bool expected_to_be_found) {
|
||||
char *xml = linphone_presence_model_to_xml(presence_model);
|
||||
if (expected_to_be_found) {
|
||||
BC_ASSERT(std::string(xml).find(lookup) != std::string::npos);
|
||||
} else {
|
||||
BC_ASSERT(std::string(xml).find(lookup) == std::string::npos);
|
||||
}
|
||||
bctbx_free(xml), xml = nullptr;
|
||||
linphone_account_set_presence_model(linphone_core_get_default_account(laure->lc), presence_model);
|
||||
};
|
||||
|
||||
// Check that there is no permanent activities at the start.
|
||||
check_permanent_activities("<rpid:permanent-activities>", false);
|
||||
|
||||
// Check that the permanent activities are present in the XML when adding one.
|
||||
auto *activity = linphone_presence_activity_new(LinphonePresenceActivityAway, nullptr);
|
||||
linphone_presence_person_add_permanent_activity(linphone_presence_model_get_nth_person(presence_model, 0),
|
||||
activity);
|
||||
linphone_presence_activity_unref(activity);
|
||||
check_permanent_activities("<rpid:permanent-activities>", true);
|
||||
|
||||
// Check that modifying something other than the permanent activities in the model does not put the permanent
|
||||
// activities section in the XML.
|
||||
auto *person = linphone_presence_model_get_nth_person(presence_model, 0);
|
||||
linphone_presence_person_clear_activities(person);
|
||||
activity = linphone_presence_activity_new(LinphonePresenceActivityMeeting, nullptr);
|
||||
linphone_presence_person_add_activity(person, activity);
|
||||
linphone_presence_activity_unref(activity);
|
||||
check_permanent_activities("<rpid:permanent-activities>", false);
|
||||
|
||||
// Check that the permanent activities are present in the XML when adding another one.
|
||||
activity = linphone_presence_activity_new(LinphonePresenceActivityInTransit, nullptr);
|
||||
linphone_presence_person_add_permanent_activity(linphone_presence_model_get_nth_person(presence_model, 0),
|
||||
activity);
|
||||
linphone_presence_activity_unref(activity);
|
||||
check_permanent_activities("<rpid:permanent-activities>", true);
|
||||
|
||||
// Check that the permanent activities are present in the XML when adding a permanent activity note.
|
||||
auto *note = linphone_presence_note_new("A simple note", "en");
|
||||
linphone_presence_person_add_permanent_activities_note(linphone_presence_model_get_nth_person(presence_model, 0),
|
||||
note);
|
||||
linphone_presence_note_unref(note);
|
||||
check_permanent_activities("<rpid:permanent-activities>", true);
|
||||
|
||||
// Check that the permanent activities section is still present in the XML when clearing the permanent activities,
|
||||
// because the previously added permanent activity note is still there.
|
||||
linphone_presence_person_clear_permanent_activities(linphone_presence_model_get_nth_person(presence_model, 0));
|
||||
check_permanent_activities("<rpid:permanent-activities>", true);
|
||||
|
||||
// Check that the permanent activities section is present but empty in the XML when clearing the permanent
|
||||
// activities notes.
|
||||
linphone_presence_person_clear_permanent_activities_notes(
|
||||
linphone_presence_model_get_nth_person(presence_model, 0));
|
||||
check_permanent_activities("<rpid:permanent-activities/>", true);
|
||||
|
||||
// Check one more time that modifying something other than the permanent activities in the model does not put the
|
||||
// permanent activities section in the XML.
|
||||
linphone_presence_person_clear_activities(linphone_presence_model_get_nth_person(presence_model, 0));
|
||||
check_permanent_activities("<rpid:permanent-activities>", false);
|
||||
|
||||
linphone_presence_model_unref(presence_model);
|
||||
|
||||
linphone_core_manager_destroy(laure);
|
||||
}
|
||||
|
||||
/*
|
||||
* It is not possible to test a whole path of permanent activities since flexisip-presence does not handle it.
|
||||
* Test only the NOTIFY path here, more precisely test the presence XML with permanent activities parsing.
|
||||
*/
|
||||
void permanent_activities_notify() {
|
||||
auto *presence_model = reinterpret_cast<LinphonePresenceModel *>(LinphonePrivate::PresenceModel::parsePresence(
|
||||
"application", "pidf+xml",
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||
"<presence xmlns:dm=\"urn:ietf:params:xml:ns:pidf:data-model\" xmlns:rpid=\"urn:ietf:params:xml:ns:pidf:rpid\" "
|
||||
"entity=\"sip:laure_agmtzpl@sip.example.org\" xmlns=\"urn:ietf:params:xml:ns:pidf\">\n"
|
||||
" <tuple id=\"ictb4s\">\n"
|
||||
" <status>\n"
|
||||
" <basic>open</basic>\n"
|
||||
" </status>\n"
|
||||
" <contact priority=\"0.8\">sip:laure_agmtzpl@sip.example.org</contact>\n"
|
||||
" <timestamp>2026-07-09T14:21:48Z</timestamp>\n"
|
||||
" </tuple>\n"
|
||||
" <dm:person id=\"xtashx\">"
|
||||
" <rpid:activities>\n"
|
||||
" <rpid:meeting/>\n"
|
||||
" </rpid:activities>\n"
|
||||
" <rpid:permanent-activities>\n"
|
||||
" <rpid:note xml:lang=\"en\">A simple note</rpid:note>\n"
|
||||
" <rpid:in-transit/>\n"
|
||||
" <rpid:away/>\n"
|
||||
" </rpid:permanent-activities>\n"
|
||||
" <dm:timestamp>2026-07-09T14:21:48Z</dm:timestamp>\n"
|
||||
" </dm:person>\n"
|
||||
"</presence>"));
|
||||
BC_ASSERT_PTR_NOT_NULL(presence_model);
|
||||
auto *person = linphone_presence_model_get_nth_person(presence_model, 0);
|
||||
BC_ASSERT_PTR_NOT_NULL(person);
|
||||
BC_ASSERT_EQUAL(linphone_presence_person_get_nb_permanent_activities(person), 2, int, "%d");
|
||||
auto *activity = linphone_presence_person_get_nth_permanent_activity(person, 0);
|
||||
BC_ASSERT_PTR_NOT_NULL(activity);
|
||||
BC_ASSERT_EQUAL(linphone_presence_activity_get_type(activity), LinphonePresenceActivityAway, int, "%d");
|
||||
activity = linphone_presence_person_get_nth_permanent_activity(person, 1);
|
||||
BC_ASSERT_PTR_NOT_NULL(activity);
|
||||
BC_ASSERT_EQUAL(linphone_presence_activity_get_type(activity), LinphonePresenceActivityInTransit, int, "%d");
|
||||
BC_ASSERT_EQUAL(linphone_presence_person_get_nb_permanent_activities_notes(person), 1, int, "%d");
|
||||
auto *note = linphone_presence_person_get_nth_permanent_activities_note(person, 0);
|
||||
BC_ASSERT_PTR_NOT_NULL(note);
|
||||
BC_ASSERT_STRING_EQUAL(linphone_presence_note_get_content(note), "A simple note");
|
||||
BC_ASSERT_STRING_EQUAL(linphone_presence_note_get_lang(note), "en");
|
||||
linphone_presence_model_unref(presence_model);
|
||||
}
|
||||
|
||||
test_t presence_server_tests[] = {
|
||||
TEST_NO_TAG("Simple Publish", simple_publish),
|
||||
TEST_NO_TAG("Publish with 2 identities", publish_with_dual_identity),
|
||||
|
|
@ -2767,11 +2949,14 @@ test_t presence_server_tests[] = {
|
|||
TEST_ONE_TAG(
|
||||
"Notify search result capabilities with alias", notify_search_result_capabilities_with_alias, "capabilities"),
|
||||
#endif
|
||||
TEST_ONE_TAG("Permanent activities SUBSCRIBE", permanent_activities_subscribe, "permanent-activities"),
|
||||
TEST_ONE_TAG("Permanent activities PUBLISH", permanent_activities_publish, "permanent-activities"),
|
||||
TEST_ONE_TAG("Permanent activities NOTIFY", permanent_activities_notify, "permanent-activities"),
|
||||
};
|
||||
|
||||
test_suite_t presence_server_test_suite = {"Presence using server",
|
||||
NULL,
|
||||
NULL,
|
||||
nullptr,
|
||||
nullptr,
|
||||
liblinphone_tester_before_each,
|
||||
liblinphone_tester_after_each,
|
||||
sizeof(presence_server_tests) / sizeof(presence_server_tests[0]),
|
||||
Loading…
Add table
Add a link
Reference in a new issue