-
Andrea Gianarda authored
Assign to every participant a sequence number. The first time a participant is added to a conference, its sequence number is 0 and it is incremented by one every time the conference is updated. The sequence number is sent to every participant as a parameter of the ics field ATTENDEE Add conference scheduler cancel conference Add test to verify edition of conference while it is active
3a8b1b37
/*
* Copyright (c) 2010-2019 Belledonne Communications SARL.
*
* This file is part of Liblinphone.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <cstdlib>
#include <iomanip>
#include <list>
#include <sstream>
#include <bctoolbox/port.h>
#include <bctoolbox/charconv.h>
#include "linphone/utils/utils.h"
#include "logger/logger.h"
#include "private.h"
#ifdef HAVE_ADVANCED_IM
#include "xml/resource-lists.h"
#endif
// =============================================================================
using namespace std;
LINPHONE_BEGIN_NAMESPACE
// -----------------------------------------------------------------------------
bool Utils::iequals (const string &a, const string &b) {
size_t size = a.size();
if (b.size() != size)
return false;
for (size_t i = 0; i < size; ++i) {
if (tolower(a[i]) != tolower(b[i]))
return false;
}
return true;
}
// -----------------------------------------------------------------------------
#ifndef __ANDROID__
#define TO_STRING_IMPL(TYPE) \
string Utils::toString (TYPE val) { \
return to_string(val); \
}
#else
#define TO_STRING_IMPL(TYPE) \
string Utils::toString (TYPE val) { \
ostringstream os; \
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
os << val; \
return os.str(); \
}
#endif // ifndef __ANDROID__
TO_STRING_IMPL(int)
TO_STRING_IMPL(long)
TO_STRING_IMPL(long long)
TO_STRING_IMPL(unsigned)
TO_STRING_IMPL(unsigned long)
TO_STRING_IMPL(unsigned long long)
TO_STRING_IMPL(float)
TO_STRING_IMPL(double)
TO_STRING_IMPL(long double)
#undef TO_STRING_IMPL
string Utils::toString (const void *val) {
ostringstream ss;
ss << val;
return ss.str();
}
// -----------------------------------------------------------------------------
#define STRING_TO_NUMBER_IMPL(TYPE, SUFFIX) \
TYPE Utils::sto ## SUFFIX (const string &str, size_t *idx, int base) { \
return sto ## SUFFIX(str.c_str(), idx, base); \
} \
TYPE Utils::sto ## SUFFIX (const char *str, size_t *idx, int base) { \
char *p; \
TYPE v = strto ## SUFFIX(str, &p, base); \
if (idx) \
*idx = static_cast<size_t>(p - str); \
return v; \
} \
#define STRING_TO_NUMBER_IMPL_BASE_LESS(TYPE, SUFFIX) \
TYPE Utils::sto ## SUFFIX(const string &str, size_t * idx) { \
return sto ## SUFFIX(str.c_str(), idx); \
} \
TYPE Utils::sto ## SUFFIX(const char *str, size_t * idx) { \
char *p; \
TYPE v = strto ## SUFFIX(str, &p); \
if (idx) \
*idx = static_cast<size_t>(p - str); \
return v; \
} \
#define strtoi(STR, IDX, BASE) static_cast<int>(strtol(STR, IDX, BASE))
STRING_TO_NUMBER_IMPL(int, i)
#undef strtoi
STRING_TO_NUMBER_IMPL(long long, ll)
STRING_TO_NUMBER_IMPL(unsigned long long, ull)
STRING_TO_NUMBER_IMPL_BASE_LESS(double, d)
STRING_TO_NUMBER_IMPL_BASE_LESS(float, f)
#undef STRING_TO_NUMBER_IMPL
#undef STRING_TO_NUMBER_IMPL_BASE_LESS
bool Utils::stob (const string &str) {
const string lowerStr = stringToLower(str);
return !lowerStr.empty() && (lowerStr == "true" || lowerStr == "1");
}
// -----------------------------------------------------------------------------
string Utils::stringToLower (const string &str) {
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
string result(str.size(), ' ');
transform(str.cbegin(), str.cend(), result.begin(), ::tolower);
return result;
}
// -----------------------------------------------------------------------------
string Utils::unicodeToUtf8 (uint32_t ic) {
string result;
result.resize(5);
size_t size = 0;
if (ic < 0x80) {
result[0] = static_cast<char>(ic);
size = 1;
} else if (ic < 0x800) {
result[1] = static_cast<char>(0x80 + ((ic & 0x3F)));
result[0] = static_cast<char>(0xC0 + ((ic >> 6) & 0x1F));
size = 2;
} else if (ic < 0x10000) {
result[2] = static_cast<char>(0x80 + (ic & 0x3F));
result[1] = static_cast<char>(0x80 + ((ic >> 6) & 0x3F));
result[0] = static_cast<char>(0xE0 + ((ic >> 12) & 0xF));
size = 3;
} else if (ic < 0x110000) {
result[3] = static_cast<char>(0x80 + (ic & 0x3F));
result[2] = static_cast<char>(0x80 + ((ic >> 6) & 0x3F));
result[1] = static_cast<char>(0x80 + ((ic >> 12) & 0x3F));
result[0] = static_cast<char>(0xF0 + ((ic >> 18) & 0x7));
size = 4;
}
result.resize(size);
return result;
}
/*
* TODO: not optmized at all. Good enough for small vectors.
*/
std::string Utils::unicodeToUtf8 (const std::vector<uint32_t>& chars) {
std::ostringstream ss;
for (auto character : chars) {
ss << Utils::unicodeToUtf8(character);
}
return ss.str();
}
string Utils::trim (const string &str) {
auto itFront = find_if_not(str.begin(), str.end(), [] (int c) { return isspace(c); });
auto itBack = find_if_not(str.rbegin(), str.rend(), [] (int c) { return isspace(c); }).base();
return (itBack <= itFront ? string() : string(itFront, itBack));
}
std::string Utils::normalizeFilename(const std::string& str){
std::string result(str);
#ifdef _WIN32
const std::string illegalCharacters = "\\/:*\"<>|";
#elif defined(__APPLE__)
const std::string illegalCharacters = ":/";
#else
const std::string illegalCharacters = "/";
#endif
// Invisible and illegal characters should not be part of a filename
result.erase(std::remove_if(result.begin(), result.end(), [illegalCharacters](const unsigned char& c){
return c < ' ' || illegalCharacters.find((char)c) != std::string::npos;
}), result.end());
return result;
}
// -----------------------------------------------------------------------------
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
tm Utils::getTimeTAsTm (time_t t) {
#ifdef _WIN32
return *gmtime(&t);
#else
tm result;
return *gmtime_r(&t, &result);
#endif
}
time_t Utils::getTmAsTimeT (const tm &t) {
tm tCopy = t;
time_t result;
#if defined(LINPHONE_WINDOWS_UNIVERSAL) || defined(LINPHONE_MSC_VER_GREATER_19)
long adjustTimezone;
#else
time_t adjustTimezone;
#endif
#if TARGET_IPHONE_SIMULATOR
result = timegm(&tCopy);
adjustTimezone = 0;
#else
// mktime uses local time => It's necessary to adjust the timezone to get an UTC time.
result = mktime(&tCopy);
#if defined(LINPHONE_WINDOWS_UNIVERSAL) || defined(LINPHONE_MSC_VER_GREATER_19)
_get_timezone(&adjustTimezone);
#else
adjustTimezone = timezone;
#endif
#endif
if (result == time_t(-1)) {
lError() << "timegm/mktime failed: " << strerror(errno);
return time_t(-1);
}
return result - time_t(adjustTimezone);
}
std::string Utils::getTimeAsString (const std::string &format, time_t t) {
tm dateTime = getTimeTAsTm(t);
std::ostringstream os;
os << std::put_time(&dateTime, format.c_str());
return os.str();
}
time_t Utils::getStringToTime (const std::string &format, const std::string &s) {
#ifndef _WIN32
tm dateTime;
if (strptime(s.c_str(), format.c_str(), &dateTime)) {
return getTmAsTimeT(dateTime);
}
#endif
return 0;
}
// -----------------------------------------------------------------------------
// TODO: Improve perf!!! Avoid c <--> cpp string conversions.
string Utils::localeToUtf8 (const string &str) {
char *cStr = bctbx_locale_to_utf8(str.c_str());
string utf8Str = cStringToCppString(cStr);
bctbx_free(cStr);
return utf8Str;
}
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
string Utils::utf8ToLocale (const string &str) {
char *cStr = bctbx_utf8_to_locale(str.c_str());
string localeStr = cStringToCppString(cStr);
bctbx_free(cStr);
return localeStr;
}
string Utils::convertAnyToUtf8 (const string &str, const string &encoding) {
char *cStr = bctbx_convert_any_to_utf8(str.c_str(), encoding.c_str());
string convertedStr = cStringToCppString(cStr);
bctbx_free(cStr);
return convertedStr;
}
string Utils::quoteStringIfNotAlready(const string &str){
if (str.empty() || str[0] == '"') return str;
return string("\"") + str + string("\"");
}
map<string, Utils::Version> Utils::parseCapabilityDescriptor(const string &descriptor){
map<string, Utils::Version> result;
istringstream istr(descriptor);
string cap;
string version;
while (std::getline(istr, cap, ',')){
istringstream capversion(cap);
if (std::getline(capversion, cap, '/') && std::getline(capversion, version, '/')){
result[cap] = Utils::Version(version);
}else result[cap] = Utils::Version(1, 0);
}
return result;
}
std::string Utils::getSipFragAddress(const Content & content) {
if (content.getContentType() != ContentType::SipFrag) {
lError() << "Content type is not SipFrag hence " << __func__ << " is unable to extract the address";
return std::string();
}
// Extract Contact header from sipfrag content
auto id = content.getBodyAsUtf8String();
std::string toErase = "From: ";
size_t contactPosition = id.find(toErase);
if (contactPosition != std::string::npos) id.erase(contactPosition, toErase.length());
IdentityAddress tmpIdentityAddress(id);
return tmpIdentityAddress.asString();
}
std::string Utils::getResourceLists (const std::list<IdentityAddress> &addresses) {
#ifdef HAVE_ADVANCED_IM
Xsd::ResourceLists::ResourceLists rl = Xsd::ResourceLists::ResourceLists();
Xsd::ResourceLists::ListType l = Xsd::ResourceLists::ListType();
for (const auto &addr : addresses) {
Xsd::ResourceLists::EntryType entry = Xsd::ResourceLists::EntryType(addr.asString());
l.getEntry().push_back(entry);
}
rl.getList().push_back(l);
Xsd::XmlSchema::NamespaceInfomap map;
std::stringstream xmlBody;
serializeResourceLists(xmlBody, rl, map);
return xmlBody.str();
#else
lWarning() << "Advanced IM such as group chat is disabled!";
return "";
#endif
}
// -----------------------------------------------------------------------------
351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
std::list<IdentityAddress> Utils::parseResourceLists (const Content &content) {
#ifdef HAVE_ADVANCED_IM
if ((content.getContentType() == ContentType::ResourceLists)
&& ((content.getContentDisposition().weakEqual(ContentDisposition::RecipientList))
|| (content.getContentDisposition().weakEqual(ContentDisposition::RecipientListHistory))
)
) {
std::istringstream data(content.getBodyAsString());
std::unique_ptr<Xsd::ResourceLists::ResourceLists> rl(Xsd::ResourceLists::parseResourceLists(
data,
Xsd::XmlSchema::Flags::dont_validate
));
std::list<IdentityAddress> addresses;
for (const auto &l : rl->getList()) {
for (const auto &entry : l.getEntry()) {
IdentityAddress addr(entry.getUri());
addresses.push_back(std::move(addr));
}
}
return addresses;
}
return std::list<IdentityAddress>();
#else
lWarning() << "Advanced IM such as group chat is disabled!";
return std::list<IdentityAddress>();
#endif
}
std::shared_ptr<ConferenceInfo> Utils::createConferenceInfoFromOp (SalCallOp *op, bool remote) {
std::shared_ptr<ConferenceInfo> info = ConferenceInfo::create();
if (!op) return info;
const auto sipfrag = op->getContentInRemote(ContentType::SipFrag);
const auto resourceList = op->getContentInRemote(ContentType::ResourceLists);
if (!sipfrag.isEmpty()) {
auto organizer = Utils::getSipFragAddress(sipfrag);
info->setOrganizer(IdentityAddress(organizer));
}
if (!resourceList.isEmpty()) {
auto invitees = Utils::parseResourceLists(resourceList);
for (const auto &i : invitees) {
info->addParticipant(i);
}
}
char * remoteContactAddressStr = sal_address_as_string(remote ? op->getRemoteContactAddress() : op->getContactAddress());
const ConferenceAddress conferenceAddress(remoteContactAddressStr);
ms_free(remoteContactAddressStr);
if (conferenceAddress.isValid()) {
info->setUri(conferenceAddress);
}
auto & md = remote ? op->getRemoteMediaDescription() : op->getLocalMediaDescription();
if (md && md->times.size() > 0) {
const auto & timePair = md->times.front();
auto startTime = timePair.first;
auto endTime = timePair.second;
if (startTime >= 0) {
info->setDateTime(startTime);
} else {
info->setDateTime(ms_time(NULL));
}
if ((startTime >= 0) && (endTime >= 0) && (endTime > startTime)) {
unsigned int duration = (static_cast<unsigned int>(endTime - startTime)) / 60;
info->setDuration(duration);
}
}
info->setSubject(op->getSubject());
421422423424425426
return info;
}
LINPHONE_END_NAMESPACE