From 20e8d7b341c25a724f4494f23d4d8e7fe8a97732 Mon Sep 17 00:00:00 2001 From: Hugo Lefeuvre <hugo.lefeuvre@savoirfairelinux.com> Date: Thu, 30 Aug 2018 16:48:34 -0400 Subject: [PATCH] contactmodel: fix broken uri support in searchContact We also make smartlist output more verbose in case of lookup failure. Also, this patch does a major cleanup in the URI class, applying new LRC style and improving RFC support. Change-Id: Ia82f9af75b4a685df7a895b335af9f55fa2cb355 Gitlab: #384 Reviewed-by: Sebastien Blin <sebastien.blin@savoirfairelinux.com> --- src/account.cpp | 1 + src/api/contactmodel.h | 1 - src/availableaccountmodel.cpp | 1 + src/contactmethod.cpp | 5 + src/contactmodel.cpp | 181 ++++---- src/conversationmodel.cpp | 8 +- src/uri.cpp | 760 +++++++++++++++------------------- src/uri.h | 326 +++++++-------- 8 files changed, 611 insertions(+), 672 deletions(-) diff --git a/src/account.cpp b/src/account.cpp index 242508a3..fc371a47 100644 --- a/src/account.cpp +++ b/src/account.cpp @@ -1395,6 +1395,7 @@ QVariant Account::roleData(int role) const bool Account::supportScheme( URI::SchemeType type ) const { switch(type) { + case URI::SchemeType::UNRECOGNIZED : case URI::SchemeType::NONE : if (protocol() == Account::Protocol::RING) /* the URIs which are supported by accounts of type RING are well diff --git a/src/api/contactmodel.h b/src/api/contactmodel.h index ed75f1e7..2ff58edb 100644 --- a/src/api/contactmodel.h +++ b/src/api/contactmodel.h @@ -163,7 +163,6 @@ Q_SIGNALS: private: std::unique_ptr<ContactModelPimpl> pimpl_; - }; } // namespace api diff --git a/src/availableaccountmodel.cpp b/src/availableaccountmodel.cpp index a0b3aa1b..a802d8c6 100644 --- a/src/availableaccountmodel.cpp +++ b/src/availableaccountmodel.cpp @@ -114,6 +114,7 @@ Account* AvailableAccountModel::currentDefaultAccount(ContactMethod* method) case URI::ProtocolHint::IP: type = URI::SchemeType::SIP; break; + case URI::ProtocolHint::UNRECOGNIZED: case URI::ProtocolHint::RING: case URI::ProtocolHint::RING_USERNAME: type = URI::SchemeType::RING; diff --git a/src/contactmethod.cpp b/src/contactmethod.cpp index b3a55399..5d0701c7 100644 --- a/src/contactmethod.cpp +++ b/src/contactmethod.cpp @@ -689,6 +689,7 @@ QString ContactMethod::toHash() const //There is no point in keeping the full URI, a Ring hash is unique uristr = uri().userinfo(); break; + case URI::ProtocolHint::UNRECOGNIZED: case URI::ProtocolHint::RING_USERNAME: case URI::ProtocolHint::SIP_OTHER: case URI::ProtocolHint::IP : @@ -866,6 +867,10 @@ bool ContactMethod::isReachable() const if (hasRing) return true; break; + case URI::ProtocolHint::UNRECOGNIZED: + if (hasRing || hasSip) + return true; + break; } return false; diff --git a/src/contactmodel.cpp b/src/contactmodel.cpp index 041e42c8..a279a75e 100644 --- a/src/contactmodel.cpp +++ b/src/contactmodel.cpp @@ -3,6 +3,7 @@ * Author: Nicolas Jäger <nicolas.jager@savoirfairelinux.com> * * Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com> * * Author: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com> * + * Author: Hugo Lefeuvre <hugo.lefeuvre@savoirfairelinux.com> * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * @@ -81,6 +82,15 @@ public: * @param banned whether contact is banned or not */ void addToContacts(ContactMethod* cm, const profile::Type& type, bool banned = false); + /** + * Helpers for searchContact. Search for a given RING or SIP contact. + */ + void searchRingContact(const std::string& query); + void searchSipContact(const std::string& query); + /** + * Update temporary item to display a given message about a given uri. + */ + void updateTemporaryMessage(const std::string& mes, const std::string& uri); // Helpers const BehaviorController& behaviorController; @@ -323,72 +333,75 @@ ContactModel::getContactProfileId(const std::string& contactUri) const void ContactModel::searchContact(const std::string& query) { - auto& temporaryContact = pimpl_->contacts[""]; - temporaryContact = {}; // reset in any case + // always reset temporary contact + pimpl_->contacts[""] = {}; auto uri = URI(QString(query.c_str())); - if (owner.profileInfo.type == profile::Type::SIP) { - // We don't need to search anything for SIP contacts. - // NOTE: there is no registeredName for SIP contacts + std::string uriID = uri.format(URI::Section::USER_INFO | URI::Section::HOSTNAME | URI::Section::PORT).toStdString(); - // Reset temporary if contact exists, else save the query inside it - { - std::lock_guard<std::mutex> lk(pimpl_->contactsMtx_); - auto iter = pimpl_->contacts.find(query); - if (iter == pimpl_->contacts.end()) { - profile::Info profileInfo; - profileInfo.uri = query; - profileInfo.alias = query; - profileInfo.type = profile::Type::TEMPORARY; - temporaryContact.profileInfo = profileInfo; - } - } - emit modelUpdated(query); - } else if (uri.full().startsWith("ring:")) { - auto updated = false; - // Reset temporary if contact exists, else save the query inside it - { - std::lock_guard<std::mutex> lk(pimpl_->contactsMtx_); - auto shortUri = uri.full().mid(5).toStdString(); - auto iter = pimpl_->contacts.begin(); - while (iter != pimpl_->contacts.end()) { - if (iter->first == shortUri || iter->second.registeredName == shortUri) { - break; - } - ++iter; - } - if (iter == pimpl_->contacts.end()) { - // query is a valid RingID? - profile::Info profileInfo; - profileInfo.uri = shortUri; - profileInfo.alias = shortUri; - profileInfo.type = profile::Type::TEMPORARY; - temporaryContact.profileInfo = profileInfo; - updated = true; - } + auto uriScheme = uri.schemeType(); + if (uri.schemeType() == URI::SchemeType::NONE) { + // uri has no scheme, default to current account scheme + if (owner.profileInfo.type == profile::Type::SIP) { + uriScheme = URI::SchemeType::SIP; + } else if (owner.profileInfo.type == profile::Type::RING) { + uriScheme = URI::SchemeType::RING; } - if (updated) - emit modelUpdated(query); + } + + if (uriScheme == URI::SchemeType::SIP && owner.profileInfo.type == profile::Type::SIP) { + pimpl_->searchSipContact(uriID); + } else if (uriScheme == URI::SchemeType::RING && owner.profileInfo.type == profile::Type::RING) { + pimpl_->searchRingContact(uriID); } else { - // Default searching - profile::Info profileInfo; - profileInfo.alias = "Searching…"; - profileInfo.type = profile::Type::TEMPORARY; - temporaryContact.profileInfo = profileInfo; - temporaryContact.registeredName = query; - emit modelUpdated(query); - - - // Query Name Server - if (auto* account = AccountModel::instance().getById(owner.id.c_str())) { - if (not account->lookupName(QString(query.c_str()))) { - profileInfo.alias = "No reference of " + query + " found"; - } - emit modelUpdated(query); - } + pimpl_->updateTemporaryMessage(tr("Bad URI scheme").toStdString(), uri.full().toStdString()); + } +} + +void +ContactModelPimpl::updateTemporaryMessage(const std::string& mes, const std::string& uri) +{ + std::lock_guard<std::mutex> lk(contactsMtx_); + auto& temporaryContact = contacts[""]; + temporaryContact.profileInfo.alias = mes; + temporaryContact.profileInfo.type = profile::Type::TEMPORARY; + temporaryContact.registeredName = uri; +} + +void +ContactModelPimpl::searchRingContact(const std::string& query) +{ + if (query.empty()) { + return; } + updateTemporaryMessage(tr("Searching…").toStdString(), query); + + // Default searching + if (auto* account = AccountModel::instance().getById(linked.owner.id.c_str())) { + account->lookupName(QString(query.c_str())); + } +} + +void +ContactModelPimpl::searchSipContact(const std::string& query) +{ + if (query.empty()) { + return; + } + + auto& temporaryContact = contacts[""]; + + { + std::lock_guard<std::mutex> lk(contactsMtx_); + if (contacts.find(query) == contacts.end()) { + temporaryContact.profileInfo.uri = query; + temporaryContact.profileInfo.alias = query; + temporaryContact.profileInfo.type = profile::Type::TEMPORARY; + } + } + emit linked.modelUpdated(query); } uint64_t @@ -697,36 +710,42 @@ ContactModelPimpl::slotRegisteredNameFound(const std::string& accountId, auto& temporaryContact = contacts[""]; if (status == 0 /* SUCCESS */) { - { - std::lock_guard<std::mutex> lk(contactsMtx_); - if (contacts.find(uri) == contacts.end()) { - // contact not present, update the temporaryContact - lrc::api::profile::Info profileInfo = {uri, "", "", profile::Type::TEMPORARY}; - temporaryContact = {profileInfo, registeredName, false, false}; - } else { - // Update contact - contacts[uri].registeredName = registeredName; - if (temporaryContact.registeredName == uri || temporaryContact.registeredName == registeredName) { - // contact already present, remove the temporaryContact - lrc::api::profile::Info profileInfo = {"", "", "", profile::Type::TEMPORARY}; - temporaryContact = {profileInfo, "", false, false}; - } + std::lock_guard<std::mutex> lk(contactsMtx_); + + if (contacts.find(uri) != contacts.end()) { + // update contact and remove temporary item + contacts[uri].registeredName = registeredName; + temporaryContact = {}; + } else { + if (temporaryContact.registeredName != uri && temporaryContact.registeredName != registeredName) { + // we are notified that a previous lookup ended + return; } + + // update temporary item + lrc::api::profile::Info profileInfo = {uri, "", "", profile::Type::TEMPORARY}; + temporaryContact = {profileInfo, registeredName, false, false}; } - emit linked.modelUpdated(uri); - } else if (!uri.empty() || !registeredName.empty()) { + } else { if (temporaryContact.registeredName != uri && temporaryContact.registeredName != registeredName) { + // we are notified that a previous lookup ended return; } - { - std::lock_guard<std::mutex> lk(contactsMtx_); - temporaryContact.registeredName = registeredName; - temporaryContact.profileInfo.alias = "Not found"; + + switch (status) { + case 1 /* INVALID */: + updateTemporaryMessage(tr("Invalid ringID").toStdString(), registeredName); + break; + case 2 /* NOT FOUND */: + updateTemporaryMessage(tr("Not found").toStdString(), registeredName); + break; + case 3 /* ERROR */: + updateTemporaryMessage(tr("Couldn't lookup…").toStdString(), registeredName); + break; } - emit linked.modelUpdated(uri); - } else { - qDebug() << "ContactModelPimpl::slotRegisteredNameFound, status = " << status << " with empty uri and registeredName"; } + + emit linked.modelUpdated(uri); } void diff --git a/src/conversationmodel.cpp b/src/conversationmodel.cpp index 2dee502b..81487395 100644 --- a/src/conversationmodel.cpp +++ b/src/conversationmodel.cpp @@ -295,10 +295,14 @@ ConversationModel::allFilteredConversations() const auto filter = pimpl_->filter; auto uri = URI(QString(filter.c_str())); - if (uri.full().startsWith("ring:")) { - filter = uri.full().mid(5).toStdString();; + bool stripScheme = (uri.schemeType() == URI::SchemeType::NONE) || (uri.schemeType() == URI::SchemeType::RING); + FlagPack<URI::Section> flags = URI::Section::USER_INFO | URI::Section::HOSTNAME | URI::Section::PORT; + if (!stripScheme) { + flags |= URI::Section::SCHEME; } + filter = uri.format(flags).toStdString(); + /* Check contact */ // If contact is banned, only match if filter is a perfect match if (contactInfo.isBanned) { diff --git a/src/uri.cpp b/src/uri.cpp index 40d4f3ee..6d217c60 100644 --- a/src/uri.cpp +++ b/src/uri.cpp @@ -1,6 +1,7 @@ /**************************************************************************** - * Copyright (C) 2014-2018 Savoir-faire Linux * + * Copyright (C) 2014-2018 Savoir-faire Linux * * Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> * + * Author : Hugo Lefeuvre <hugo.lefeuvre@savoirfairelinux.com> * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * @@ -15,216 +16,184 @@ * 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 "uri.h" - -#include "private/matrixutils.h" -#include <QRegularExpression> +#include "uri.h" +#include <regex> -class URIPrivate +class URIPimpl { public: - ///Strings associated with SchemeType - static const Matrix1D<URI::SchemeType, const char*> schemeNames; - - ///String associated with the transport name - static const Matrix1D<URI::Transport, const char*> transportNames; - - static const QString whitespaceCharClass; - - static const QRegularExpression startWhitespaceMatcher; - static const QRegularExpression endWhitespaceMatcher; - - ///Attributes names - struct Constants { - constexpr static const char TRANSPORT[] = "transport"; - constexpr static const char TAG [] = "tag" ; - }; - - //Constructor - URIPrivate(URI* uri); - void commonCopyConstructor(const URI& o); - - //Attributes - QString m_ExtHostname ; - QString m_Userinfo ; - QStringList m_lAttributes ; - QString m_Stripped ; - QString m_Hostname2 ; - QByteArray m_Tag ; - URI::SchemeType m_HeaderType ; - URI::Transport m_Transport ; - bool m_hasChevrons ; - bool m_Parsed ; - bool m_HasAt ; - URI::ProtocolHint m_ProtocolHint; - bool m_HintParsed ; - bool m_IsHNParsed ; - int m_Port ; - - //Helper - static QString strip(const QString& uri, URI::SchemeType& scheme); - void parse(); - void parseHostname(); - static bool checkIp(const QString& str, bool &isHash, const URI::SchemeType& scheme); - URI::Transport nameToTransport(const QByteArray& name); - void parseAttribute(const QByteArray& extHn, const int start, const int pos); -private: - URI* q_ptr; + // Strings associated with SchemeType + static const std::map<URI::SchemeType, const char*> schemeNames; + + // String associated with the transport name + static const std::map<URI::Transport, const char*> transportNames; + + struct Constants { + constexpr static const char TRANSPORT[] = "transport"; + constexpr static const char TAG[] = "tag"; + }; + + URIPimpl(const URI& uri); + URIPimpl& operator=(const URIPimpl&); + + // Attributes + const URI& linked; + + QString m_ExtHostname; + QString m_Scheme; + QString m_Userinfo; + QStringList m_lAttributes; + QString m_Stripped; + QString m_Hostname2; + QByteArray m_Tag; + int m_Port = -1; + + URI::SchemeType m_HeaderType = URI::SchemeType::NONE; + URI::Transport m_Transport = URI::Transport::NOT_SET; + URI::ProtocolHint m_ProtocolHint = URI::ProtocolHint::UNRECOGNIZED; + + bool m_hasChevrons {false}; + bool m_Parsed {false}; + bool m_HasAt {false}; + bool m_HintParsed {false}; + bool m_IsHNParsed {false}; + + // Helpers + URI::Transport nameToTransport(const QByteArray& name); + static QString strip(const QString& uri, URI::SchemeType& schemeType, QString& scheme); + static bool checkIp(const QString& str, bool &isHash, const URI::SchemeType& scheme); + void parseAttribute(const QByteArray& extHn, const int start, const int pos); + void parseHostname(); + void parse(); +}; + +constexpr const char URIPimpl::Constants::TRANSPORT[]; +constexpr const char URIPimpl::Constants::TAG[]; + +const std::map<URI::Transport, const char*> URIPimpl::transportNames = { + { URI::Transport::NOT_SET, "NOT_SET" }, + { URI::Transport::TLS, "TLS" }, + { URI::Transport::tls, "tls" }, + { URI::Transport::TCP, "TCP" }, + { URI::Transport::tcp, "tcp" }, + { URI::Transport::UDP, "UDP" }, + { URI::Transport::udp, "udp" }, + { URI::Transport::SCTP, "SCTP" }, + { URI::Transport::sctp, "sctp" }, + { URI::Transport::DTLS, "DTLS" }, + { URI::Transport::dtls, "dtls" } +}; + +const std::map<URI::SchemeType, const char*> URIPimpl::schemeNames = { + { URI::SchemeType::SIP, "sip:" }, + { URI::SchemeType::SIPS, "sips:" }, + { URI::SchemeType::RING, "ring:" } }; -constexpr const char URIPrivate::Constants::TRANSPORT[]; -constexpr const char URIPrivate::Constants::TAG []; - -const Matrix1D<URI::Transport, const char*> URIPrivate::transportNames = {{ - /*NOT_SET*/ "NOT_SET", - /*TLS */ "TLS" , - /*tls */ "tls" , - /*TCP */ "TCP" , - /*tcp */ "tcp" , - /*UDP */ "UDP" , - /*udp */ "udp" , - /*SCTP */ "SCTP" , - /*sctp */ "sctp" , - /*DTLS */ "DTLS" , - /*dtls */ "dtls" , -}}; - -const Matrix1D<URI::SchemeType, const char*> URIPrivate::schemeNames = {{ - /*NONE = */ "" , - /*SIP = */ "sip:" , - /*SIPS = */ "sips:", - /*RING = */ "ring:", -}}; - -const QString URIPrivate::whitespaceCharClass = QStringLiteral("[\\h\\x{200B}\\x{200C}\\x{200D}\\x{FEFF}]+"); - -const QRegularExpression URIPrivate::startWhitespaceMatcher = QRegularExpression( - "^" + URIPrivate::whitespaceCharClass, - QRegularExpression::UseUnicodePropertiesOption); -const QRegularExpression URIPrivate::endWhitespaceMatcher = QRegularExpression( - URIPrivate::whitespaceCharClass + "$", - QRegularExpression::UseUnicodePropertiesOption); - -URIPrivate::URIPrivate(URI* uri) : m_Parsed(false),m_HeaderType(URI::SchemeType::NONE),q_ptr(uri), -m_hasChevrons(false),m_HasAt(false),m_ProtocolHint(URI::ProtocolHint::SIP_OTHER),m_HintParsed(false), -m_IsHNParsed(false),m_Port(-1),m_Transport(URI::Transport::NOT_SET) +URIPimpl::URIPimpl(const URI& uri) +: linked(uri) { } -///Default constructor -URI::URI() : QString(), d_ptr(new URIPrivate(this)) +URIPimpl& URIPimpl::operator=(const URIPimpl& other) { - + m_Parsed = other.m_Parsed; + m_HintParsed = other.m_HintParsed; + m_ExtHostname = other.m_ExtHostname; + m_HasAt = other.m_HasAt; + m_Scheme = other.m_Scheme; + m_ProtocolHint = other.m_ProtocolHint; + m_HeaderType = other.m_HeaderType; + m_Userinfo = other.m_Userinfo; + m_Stripped = other.m_Stripped; + m_IsHNParsed = other.m_IsHNParsed; + m_Port = other.m_Port; + m_Transport = other.m_Transport; + m_Tag = other.m_Tag; + m_hasChevrons = other.m_hasChevrons; + m_Hostname2 = other.m_Hostname2; + m_lAttributes = other.m_lAttributes; + return *this; } -///Constructor -URI::URI(const QString& other) : URI() +URI::URI() +: QString() +, pimpl_(std::make_unique<URIPimpl>(*this)) { - d_ptr->m_Stripped = URIPrivate::strip(other,d_ptr->m_HeaderType); - (*static_cast<QString*>(this)) = d_ptr->m_Stripped ; } -///Copy constructor -URI::URI(const URI& o) : URI() +URI::URI(const QString& uri) +: URI() { - d_ptr->commonCopyConstructor(o); + QString simplified = uri.simplified().remove(' ').remove('<').remove('>'); + pimpl_->m_Stripped = URIPimpl::strip(simplified, pimpl_->m_HeaderType, pimpl_->m_Scheme); + (*static_cast<QString*>(this)) = pimpl_->m_Stripped; } -///Destructor -URI::~URI() +URI::URI(const URI& other) +: URI() { - (*static_cast<QString*>(this)) = QString(); - d_ptr->m_Stripped = QString(); -// delete d_ptr; + *pimpl_ = *other.pimpl_; + (*static_cast<QString*>(this)) = pimpl_->m_Stripped; } -void URIPrivate::commonCopyConstructor(const URI& o) +URI& URI::operator=(const URI& other) { - //TODO see if a copy on write kind of algo could be used for this - m_Parsed = o.d_ptr->m_Parsed ; - m_HintParsed = o.d_ptr->m_HintParsed ; - m_ExtHostname = o.d_ptr->m_ExtHostname ; - m_HasAt = o.d_ptr->m_HasAt ; - m_ProtocolHint = o.d_ptr->m_ProtocolHint; - m_HeaderType = o.d_ptr->m_HeaderType ; - m_Userinfo = o.d_ptr->m_Userinfo ; - m_Stripped = o.d_ptr->m_Stripped ; - m_IsHNParsed = o.d_ptr->m_IsHNParsed ; - m_Port = o.d_ptr->m_Port ; - m_Transport = o.d_ptr->m_Transport ; - m_Tag = o.d_ptr->m_Tag ; - - (*static_cast<QString*>(q_ptr)) = o.d_ptr->m_Stripped; + if (this != &other) { + *pimpl_ = *other.pimpl_; + (*static_cast<QString*>(this)) = pimpl_->m_Stripped; + } + return *this; } -/// Copy operator, make sure the cache is also copied -URI& URI::operator=(const URI& o) +URI::~URI() { - d_ptr->commonCopyConstructor(o); - return (*this); } -///Strip out <sip:****> from the URI -QString URIPrivate::strip(const QString& uri, URI::SchemeType& scheme) +/** + * Strip out scheme from the URI + */ +QString URIPimpl::strip(const QString& uri, URI::SchemeType& schemeType, QString& scheme) { - if (uri.isEmpty()) - return {}; - - /* remove whitespace at the start and end */ - auto uriTrimmed = uri; - uriTrimmed.replace(startWhitespaceMatcher, ""); - uriTrimmed.replace(endWhitespaceMatcher, ""); - - int start(uriTrimmed[0] == '<'?1:0),end(uriTrimmed.size()-1); //Other type of comparisons were too slow + if (uri.isEmpty()) + return {}; - if (start == end+1) - return {}; + std::regex uri_regex = std::regex("[a-zA-Z][a-zA-Z0-9+.-]*:"); + std::string uri_to_match = uri.toStdString(); + std::smatch match; - const char c = uriTrimmed[start].toLatin1(); - - //Assume the scheme is either sip or ring using the first letter and length, this - //is dangerous and can cause undefined behaviour that will cause the call to fail - //later on, but this is not really a problem for now - if (end > start+3 && uriTrimmed[start+3] == ':') { - switch (c) { - case 's': - scheme = URI::SchemeType::SIP; - break; - } - start = start +4; - } - else if (end > start+4 && uriTrimmed[start+4] == ':') { - switch (c) { - case 'r': - scheme = URI::SchemeType::RING; - break; - case 's': - scheme = URI::SchemeType::SIPS; - break; - } - start = start +5; - } + if (std::regex_search(uri_to_match, match, uri_regex)) { + if (match.ready()) { + scheme = match.str(0).c_str(); + } + } - if (end && uriTrimmed[end] == '>') - end--; - else if (start) { - //TODO there may be a ';' section with arguments, check - } + if (scheme == URIPimpl::schemeNames.at(URI::SchemeType::SIP)) { + schemeType = URI::SchemeType::SIP; + } else if (scheme == URIPimpl::schemeNames.at(URI::SchemeType::RING)) { + schemeType = URI::SchemeType::RING; + } else if (scheme == URIPimpl::schemeNames.at(URI::SchemeType::SIPS)) { + schemeType = URI::SchemeType::SIPS; + } else if (!scheme.isEmpty()) { + schemeType = URI::SchemeType::UNRECOGNIZED; + } else { + schemeType = URI::SchemeType::NONE; + } - return uriTrimmed.mid(start,end-start+1); + return uri.mid(scheme.size(), uri.size()); } /** - * Return the domaine of an URI + * Return the domain of the URI * * For example, example.com in <sip:12345@example.com> */ QString URI::hostname() const { - if (!d_ptr->m_Parsed) - const_cast<URI*>(this)->d_ptr->parse(); - return d_ptr->m_ExtHostname; + if (!pimpl_->m_Parsed) + pimpl_->parse(); + return pimpl_->m_ExtHostname; } /** @@ -234,9 +203,7 @@ QString URI::hostname() const */ bool URI::hasHostname() const { - if (!d_ptr->m_Parsed) - const_cast<URI*>(this)->d_ptr->parse(); - return !d_ptr->m_ExtHostname.isEmpty(); + return hostname().isEmpty(); } /** @@ -245,21 +212,18 @@ bool URI::hasHostname() const */ bool URI::hasPort() const { - if (!d_ptr->m_IsHNParsed) { - d_ptr->parseHostname(); - } - return d_ptr->m_Port != -1; + return port() != -1; } /** * Return the port, -1 is none is set */ -int URI::port() const +int URI::port() const { - if (!d_ptr->m_IsHNParsed) { - d_ptr->parseHostname(); - } - return d_ptr->m_Port; + if (!pimpl_->m_IsHNParsed) { + pimpl_->parseHostname(); + } + return pimpl_->m_Port; } /** @@ -267,9 +231,9 @@ int URI::port() const */ URI::SchemeType URI::schemeType() const { - if (!d_ptr->m_Parsed) - const_cast<URI*>(this)->d_ptr->parse(); - return d_ptr->m_HeaderType; + if (!pimpl_->m_Parsed) + const_cast<URI*>(this)->pimpl_->parse(); + return pimpl_->m_HeaderType; } /** @@ -279,48 +243,48 @@ URI::SchemeType URI::schemeType() const * @param str an uservalue (faster the scheme and before the "at" sign) * @param [out] isHash if the content is pure hexadecimal ASCII */ -bool URIPrivate::checkIp(const QString& str, bool &isHash, const URI::SchemeType& scheme) +bool URIPimpl::checkIp(const QString& str, bool &isHash, const URI::SchemeType& scheme) { - const QByteArray raw = str.toLatin1(); - int max = str.size(); + const QByteArray raw = str.toLatin1(); + int max = str.size(); - if (max < 3 || max > 45 || (!isHash && scheme == URI::SchemeType::RING)) - return false; + if (max < 3 || max > 45 || (!isHash && scheme == URI::SchemeType::RING)) + return false; - uchar dc(0),sc(0),i(0),d(0),hx(1); + uchar dc(0), sc(0), i(0), d(0), hx(1); - while (i < max) { - switch(raw[i]) { - case '.': + while (i < max) { + switch(raw[i]) { + case '.': isHash = false; d = 0; dc++; break; - case '0': case '1': case '2': - case '3': case '4': case '5': - case '6': case '7': case '8': - case '9': + case '0': case '1': case '2': + case '3': case '4': case '5': + case '6': case '7': case '8': + case '9': if (++d > 3 && dc) - return false; + return false; break; - case ':': + case ':': isHash = false; sc++; //No break [[clang::fallthrough]]; - case 'A': case 'B': case 'C': - case 'D': case 'E': case 'F': - case 'a': case 'b': case 'c': - case 'd': case 'e': case 'f': + case 'A': case 'B': case 'C': + case 'D': case 'E': case 'F': + case 'a': case 'b': case 'c': + case 'd': case 'e': case 'f': hx = 0; break; - default: + default: isHash = false; return false; - }; - i++; - } - return (hx && dc == 3 && d < 4) ^ (sc > 1 && dc==0); + } + i++; + } + return (hx && dc == 3 && d < 4) ^ (sc > 1 && dc==0); } /** @@ -329,180 +293,145 @@ bool URIPrivate::checkIp(const QString& str, bool &isHash, const URI::SchemeType * * @warning, this method is O(N) when called for the first time on an URI */ - URI::ProtocolHint URI::protocolHint() const - { - if (!d_ptr->m_Parsed) - const_cast<URI*>(this)->d_ptr->parse(); - - if (!d_ptr->m_HintParsed) { - bool isHash = d_ptr->m_Userinfo.size() == 40; - - URI::ProtocolHint hint; - - //Step 1: Check IP - if (URIPrivate::checkIp(d_ptr->m_Userinfo, isHash, d_ptr->m_HeaderType)) { - hint = URI::ProtocolHint::IP; - } - //Step 2: Check RING hash - else if (isHash) - { - hint = URI::ProtocolHint::RING; - } - //Step 3: Not a hash but it begins with ring:. This is a username. - else if (d_ptr->m_HeaderType == URI::SchemeType::RING){ - hint = URI::ProtocolHint::RING_USERNAME; - } - //Step 4: Check for SIP URIs - else if (d_ptr->m_HeaderType == URI::SchemeType::SIP) - { - //Step 4.1: Check for SIP URI with hostname - if (d_ptr->m_HasAt) { - hint = URI::ProtocolHint::SIP_HOST; - } - //Step 4.2: Assume SIP URI without hostname - else { - hint = URI::ProtocolHint::SIP_OTHER; - } - } - //Step 5: Assume SIP - else { - hint = URI::ProtocolHint::SIP_OTHER; - } - - d_ptr->m_ProtocolHint = hint; - d_ptr->m_HintParsed = true; +URI::ProtocolHint URI::protocolHint() const +{ + if (!pimpl_->m_Parsed) + pimpl_->parse(); + + if (!pimpl_->m_HintParsed) { + bool isHash = pimpl_->m_Userinfo.size() == 40; + + URI::ProtocolHint hint; + + //Step 1: Check IP + if (URIPimpl::checkIp(pimpl_->m_Userinfo, isHash, pimpl_->m_HeaderType)) { + hint = URI::ProtocolHint::IP; + } + //Step 2: Check RING hash + else if (isHash) + { + hint = URI::ProtocolHint::RING; + } + //Step 3: Not a hash but it begins with ring:. This is a username. + else if (pimpl_->m_HeaderType == URI::SchemeType::RING){ + hint = URI::ProtocolHint::RING_USERNAME; + } + //Step 4: Check for SIP URIs + else if (pimpl_->m_HeaderType == URI::SchemeType::SIP) + { + //Step 4.1: Check for SIP URI with hostname + if (pimpl_->m_HasAt) { + hint = URI::ProtocolHint::SIP_HOST; + } + //Step 4.2: Assume SIP URI without hostname + else { + hint = URI::ProtocolHint::SIP_OTHER; + } + } + //Step 5: UNRECOGNIZED + else { + hint = URI::ProtocolHint::UNRECOGNIZED; + } + + pimpl_->m_ProtocolHint = hint; + pimpl_->m_HintParsed = true; } - return d_ptr->m_ProtocolHint; - } + return pimpl_->m_ProtocolHint; +} -///Convert the transport name to a string -URI::Transport URIPrivate::nameToTransport(const QByteArray& name) +// Convert the transport name to a string +URI::Transport URIPimpl::nameToTransport(const QByteArray& name) { - if (name == transportNames[URI::Transport::NOT_SET ]) - return URI::Transport::NOT_SET; - else if (name == transportNames[URI::Transport::TLS ]) - return URI::Transport::TLS ; - else if (name == transportNames[URI::Transport::tls ]) - return URI::Transport::tls ; - else if (name == transportNames[URI::Transport::TCP ]) - return URI::Transport::TCP ; - else if (name == transportNames[URI::Transport::tcp ]) - return URI::Transport::tcp ; - else if (name == transportNames[URI::Transport::UDP ]) - return URI::Transport::UDP ; - else if (name == transportNames[URI::Transport::udp ]) - return URI::Transport::udp ; - else if (name == transportNames[URI::Transport::SCTP]) - return URI::Transport::SCTP ; - else if (name == transportNames[URI::Transport::sctp]) - return URI::Transport::sctp ; - else if (name == transportNames[URI::Transport::DTLS]) - return URI::Transport::DTLS ; - else if (name == transportNames[URI::Transport::dtls]) - return URI::Transport::dtls ; - return URI::Transport::NOT_SET ; + auto it = std::find_if(transportNames.begin(), transportNames.end(), [&name](auto& entry){ + return entry.second == name; + }); + + if (it != transportNames.end()) { + return it->first; + } else { + return URI::Transport::NOT_SET; + } } -///Keep a cache of the values to avoid re-parsing them -void URIPrivate::parse() +// Keep a cache of the values to avoid re-parsing them +void URIPimpl::parse() { - //FIXME the indexOf is done twice, the second time could be avoided - if (q_ptr->indexOf('@') != -1) { - const QStringList split = q_ptr->split('@'); - m_HasAt = true; - m_ExtHostname = split[1]; - m_Userinfo = split[0]; - m_Parsed = true; - } - else - m_Userinfo = (*q_ptr); + //FIXME the indexOf is done twice, the second time could be avoided + if (linked.indexOf('@') != -1) { + const QStringList split = linked.split('@'); + m_HasAt = true; + m_ExtHostname = split[1]; + m_Userinfo = split[0]; + m_Parsed = true; + } else { + m_Userinfo = (linked); + } } -void URIPrivate::parseAttribute(const QByteArray& extHn, const int start, const int pos) +void URIPimpl::parseAttribute(const QByteArray& extHn, const int start, const int pos) { - const QList<QByteArray> parts = extHn.mid(start+1,pos-start).split('='); - - if (parts.size() == 2) { - if (parts[0].toLower() == Constants::TRANSPORT) { - m_Transport = nameToTransport(parts[1]); - } - else if (parts[0].toLower() == Constants::TAG) { - m_Tag = parts[1]; - } - } + const QList<QByteArray> parts = extHn.mid(start+1,pos-start).split('='); + + if (parts.size() == 2) { + if (parts[0].toLower() == Constants::TRANSPORT) { + m_Transport = nameToTransport(parts[1]); + } + else if (parts[0].toLower() == Constants::TAG) { + m_Tag = parts[1]; + } + } } -///Extract the hostname, port and attributes -void URIPrivate::parseHostname() +// Extract the hostname, port and attributes +void URIPimpl::parseHostname() { - if (!m_Parsed) - parse(); - - const QByteArray extHn = q_ptr->hostname().toLatin1(); - int length(extHn.size()), start(0); - bool inAttributes = false; - - URI::Section section = URI::Section::HOSTNAME; - - // in case no port, attributes, etc are provided - m_Hostname2 = q_ptr->hostname(); - - for (int i = 0; i < length; i++) { - const char c = extHn[i]; - switch (c) { - case ':': //Begin port - switch(section) { - case URI::Section::HOSTNAME: - m_Hostname2 = extHn.mid(start,i); - start = i; - section = URI::Section::PORT; - break; - case URI::Section::USER_INFO: - case URI::Section::CHEVRONS : - case URI::Section::SCHEME : - case URI::Section::TRANSPORT: - case URI::Section::TAG : - case URI::Section::PORT : - break; + if (!m_Parsed) + parse(); + + const QByteArray extHn = linked.hostname().toLatin1(); + int length(extHn.size()), start(0); + bool inAttributes = false; + + URI::Section section = URI::Section::HOSTNAME; + + // in case no port, attributes, etc are provided + m_Hostname2 = linked.hostname(); + + for (int i = 0; i < length; i++) { + const char c = extHn[i]; + switch (c) { + case ':': //Begin port + if (section == URI::Section::HOSTNAME) { + m_Hostname2 = extHn.mid(start,i); + start = i; + section = URI::Section::PORT; } break; - case ';': //Begin attributes - + case ';': // Begin attributes if (inAttributes) { - parseAttribute(extHn, start, i); - } - else { - switch(section) { - case URI::Section::HOSTNAME: - m_Hostname2 = extHn.mid(start+1,i-start); - break; - case URI::Section::PORT: - m_Port = extHn.mid(start+1,i-start-1).toInt(); - break; - case URI::Section::USER_INFO: - case URI::Section::CHEVRONS : - case URI::Section::SCHEME : - case URI::Section::TRANSPORT: - case URI::Section::TAG : - break; - } - inAttributes = true; + parseAttribute(extHn, start, i); + } else { + if (section == URI::Section::HOSTNAME) { + m_Hostname2 = extHn.mid(start+1,i-start); + } else if (section == URI::Section::HOSTNAME) { + m_Port = extHn.mid(start+1,i-start-1).toInt(); + } + inAttributes = true; } start = i; break; - case '#': //Begin fragments + case '#': // Begin fragments //TODO handle fragments to comply to the RFC break; - default: + default: break; - } - } - - ///Get the remaining attribute - parseAttribute(extHn, start, length-1); + } + } - m_IsHNParsed = true; + // Get the remaining attribute + parseAttribute(extHn, start, length-1); + m_IsHNParsed = true; } /** @@ -512,18 +441,19 @@ void URIPrivate::parseHostname() */ QString URI::userinfo() const { - if (!d_ptr->m_Parsed) - const_cast<URI*>(this)->d_ptr->parse(); - return d_ptr->m_Userinfo; + if (!pimpl_->m_Parsed) + pimpl_->parse(); + return pimpl_->m_Userinfo; } /** * Sometime, some metadata can be used to deduce the scheme even if it wasn't - * originally known. This will improve the result of ::format. + * originally known. */ void URI::setSchemeType(SchemeType t) { - d_ptr->m_HeaderType = t; + pimpl_->m_HeaderType = t; + pimpl_->m_Scheme = URIPimpl::schemeNames.at(t); } /** @@ -534,57 +464,37 @@ void URI::setSchemeType(SchemeType t) */ QString URI::format(FlagPack<URI::Section> sections) const { - if (!d_ptr->m_IsHNParsed) { - d_ptr->parseHostname(); - } - - QString ret; - - if (sections & URI::Section::CHEVRONS) - ret += '<'; + if (!pimpl_->m_IsHNParsed) { + pimpl_->parseHostname(); + } - if (sections & URI::Section::SCHEME) { - auto header_type = d_ptr->m_HeaderType; + QString ret; - // Try to use the protocol hint on undeterminated header type. - // Use SIP scheme type on last resort - if (header_type == SchemeType::NONE) { - switch (protocolHint()) { - case ProtocolHint::RING: - case ProtocolHint::RING_USERNAME: - header_type = SchemeType::RING; - break; - case ProtocolHint::SIP_HOST: - case ProtocolHint::SIP_OTHER: - case ProtocolHint::IP: - default: - header_type = SchemeType::SIP; - break; - } - } + if (sections & URI::Section::CHEVRONS) + ret += '<'; - ret += URIPrivate::schemeNames[header_type]; - } + if (sections & URI::Section::SCHEME) + ret += pimpl_->m_Scheme; - if (sections & URI::Section::USER_INFO) - ret += d_ptr->m_Userinfo; + if (sections & URI::Section::USER_INFO) + ret += pimpl_->m_Userinfo; - if (sections & URI::Section::HOSTNAME && !d_ptr->m_Hostname2.isEmpty()) - ret += '@' + d_ptr->m_Hostname2; + if (sections & URI::Section::HOSTNAME && !pimpl_->m_Hostname2.isEmpty()) + ret += '@' + pimpl_->m_Hostname2; - if (sections & URI::Section::PORT && d_ptr->m_Port != -1) - ret += ':' + QString::number(d_ptr->m_Port); + if (sections & URI::Section::PORT && pimpl_->m_Port != -1) + ret += ':' + QString::number(pimpl_->m_Port); - if (sections & URI::Section::CHEVRONS) - ret += '>'; + if (sections & URI::Section::CHEVRONS) + ret += '>'; - if (sections & URI::Section::TRANSPORT && d_ptr->m_Transport != URI::Transport::NOT_SET) - ret += ";transport=" + QString(URIPrivate::transportNames[d_ptr->m_Transport]); + if (sections & URI::Section::TRANSPORT && pimpl_->m_Transport != URI::Transport::NOT_SET) + ret += ";transport=" + QString(URIPimpl::transportNames.at(pimpl_->m_Transport)); - if (sections & URI::Section::TAG && !d_ptr->m_Tag.isEmpty()) - ret += ";tag=" + d_ptr->m_Tag; + if (sections & URI::Section::TAG && !pimpl_->m_Tag.isEmpty()) + ret += ";tag=" + pimpl_->m_Tag; - return ret; + return ret; } /** @@ -598,20 +508,24 @@ QString URI::full() const QDataStream& operator<<( QDataStream& stream, const URI::ProtocolHint& ph ) { - switch(ph) { - case URI::ProtocolHint::SIP_OTHER: - stream << QStringLiteral("SIP_OTHER"); - break; - case URI::ProtocolHint::RING: - case URI::ProtocolHint::RING_USERNAME: - stream << QStringLiteral("RING"); - break; - case URI::ProtocolHint::IP : - stream << QStringLiteral("IP"); - break; - case URI::ProtocolHint::SIP_HOST : - stream << QStringLiteral("SIP_HOST"); - break; - } - return stream; + switch(ph) { + case URI::ProtocolHint::SIP_OTHER: + stream << QStringLiteral("SIP_OTHER"); + break; + case URI::ProtocolHint::RING: + case URI::ProtocolHint::RING_USERNAME: + stream << QStringLiteral("RING"); + break; + case URI::ProtocolHint::IP: + stream << QStringLiteral("IP"); + break; + case URI::ProtocolHint::SIP_HOST: + stream << QStringLiteral("SIP_HOST"); + break; + case URI::ProtocolHint::UNRECOGNIZED: + stream << QStringLiteral("UNRECOGNIZED"); + break; + } + + return stream; } diff --git a/src/uri.h b/src/uri.h index 8d928554..6c1dd153 100644 --- a/src/uri.h +++ b/src/uri.h @@ -1,6 +1,7 @@ /**************************************************************************** - * Copyright (C) 2014-2018 Savoir-faire Linux * + * Copyright (C) 2014-2018 Savoir-faire Linux * * Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> * + * Author : Hugo Lefeuvre <hugo.lefeuvre@savoirfairelinux.com> * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * @@ -19,182 +20,177 @@ #include "typedefs.h" +#include <memory> #include <QStringList> -class URIPrivate; +class URIPimpl; class QDataStream; /** - * @class URI A specialized string with multiple attributes - * - * Most of LibRingClient handle uri as strings, but more - * advanced algorithms need to access the various sections. - * This class implement a centralized and progressive URI - * parser to avoid having custom implementation peppered - * everywhere. This class doesn't attempt to produce perfect - * output. It has multiple tradeoff to be faster when - * accuracy has little value in the context of LibRingClient. - * - * Here is some example of common numbers/URIs: - * * 123 - * * 123@192.168.123.123 - * * 123@asterisk-server - * * <sip:123@192.168.123.123> - * * <sips:123@192.168.123.123> - * * <sips:888@192.168.48.213;transport=TLS> - * * <sip:c8oqz84zk7z@privacy.org>;tag=hyh8 - * * 1 800 123-4567 - * * 18001234567 - * - * @ref http://tools.ietf.org/html/rfc5456#page-8 - * @ref http://tools.ietf.org/html/rfc3986 - * @ref http://tools.ietf.org/html/rfc3261 - * @ref http://tools.ietf.org/html/rfc5630 - * - * <code> - * From the RFC: - * foo://example.com:8042/over/there?name=ferret#nose - * \_/ \______________/\_________/ \_________/ \__/ - * | | | | | - * scheme authority path query fragment - * | _____________________|__ - * / \ / \ - * urn:example:animal:ferret:nose - * - * authority = [ userinfo "@" ] host [ ":" port ] - * </code> - * - * "For example, the semicolon (";") and equals ("=") reserved characters are - * often used to delimit parameters and parameter values applicable to - * that segment. The comma (",") reserved character is often used for - * similar purposes. For example, one URI producer might use a segment - * such as "name;v=1.1" to indicate a reference to version 1.1 of - * "name", whereas another might use a segment such as "name,1.1" to - * indicate the same. " - */ + * @class URI A specialized string with multiple attributes + * + * Most of LibRingClient handle uri as strings, but more + * advanced algorithms need to access the various sections. + * This class implement a centralized and progressive URI + * parser to avoid having custom implementation peppered + * everywhere. This class doesn't attempt to produce perfect + * output. It has multiple tradeoff to be faster when + * accuracy has little value in the context of LibRingClient. + * + * Here is some example of common numbers/URIs: + * * 123 + * * 123@192.168.123.123 + * * 123@asterisk-server + * * <sip:123@192.168.123.123> + * * <sips:123@192.168.123.123> + * * <sips:888@192.168.48.213;transport=TLS> + * * <sip:c8oqz84zk7z@privacy.org>;tag=hyh8 + * * 1 800 123-4567 + * * 18001234567 + * + * @ref http://tools.ietf.org/html/rfc5456#page-8 + * @ref http://tools.ietf.org/html/rfc3986 + * @ref http://tools.ietf.org/html/rfc3261 + * @ref http://tools.ietf.org/html/rfc5630 + * + * <code> + * From the RFC: + * foo://example.com:8042/over/there?name=ferret#nose + * \_/ \______________/\_________/ \_________/ \__/ + * | | | | | + * scheme authority path query fragment + * | _____________________|__ + * / \ / \ + * urn:example:animal:ferret:nose + * + * authority = [ userinfo "@" ] host [ ":" port ] + * </code> + * + * "For example, the semicolon (";") and equals ("=") reserved characters are + * often used to delimit parameters and parameter values applicable to + * that segment. The comma (",") reserved character is often used for + * similar purposes. For example, one URI producer might use a segment + * such as "name;v=1.1" to indicate a reference to version 1.1 of + * "name", whereas another might use a segment such as "name,1.1" to + * indicate the same. " + */ class LIB_EXPORT URI : public QString { - friend class URIPrivate; public: - - ///Default constructor - URI(); - - /** - * Default copy constructor - * @param other an URI string - */ - URI(const URI& other); - - URI(const QString& other); - virtual ~URI(); - - ///@enum SchemeType The very first part of the URI followed by a ':' - enum class SchemeType { - NONE , //Implicit SIP, use account type as reference - SIP , - SIPS , - RING , - COUNT__ - }; - Q_ENUMS(URI::SchemeType) - - /** - * @enum Transport each known valid transport types - * Defined at http://tools.ietf.org/html/rfc3261#page-222 - */ - enum class Transport { - NOT_SET, /*!< The transport have not been set directly in the URI */ - TLS , /*!< Encrypted calls (capital) */ - tls , /*!< Encrypted calls */ - TCP , /*!< TCP (the default) (capital) */ - tcp , /*!< TCP (the default) */ - UDP , /*!< Without a connection (capital) */ - udp , /*!< Without a connection */ - SCTP , /*!< */ - sctp , /*!< */ - DTLS , /*!< */ - dtls , /*!< */ - COUNT__ - }; - Q_ENUMS(URI::Transport) - - /** - * @enum Section flags associated with each logical sections of the URI - * - * Those sections can be packed into a block to be used to define the - * expected URI syntax - * - */ - enum class Section { - CHEVRONS = 0x1 << 0, /*!< <code><sips:888@192.168.48.213:5060;transport=TLS> + URI(); + URI(const URI& other); + URI(const QString& other); + virtual ~URI(); + + // @enum SchemeType The very first part of the URI followed by a ':' + enum class SchemeType { + SIP , + SIPS , + RING , + NONE , + COUNT__, + UNRECOGNIZED + }; + Q_ENUMS(URI::SchemeType) + + /** + * @enum Transport each known valid transport types + * Defined at http://tools.ietf.org/html/rfc3261#page-222 + */ + enum class Transport { + NOT_SET, /*!< The transport have not been set directly in the URI */ + TLS , /*!< Encrypted calls (capital) */ + tls , /*!< Encrypted calls */ + TCP , /*!< TCP (the default) (capital) */ + tcp , /*!< TCP (the default) */ + UDP , /*!< Without a connection (capital) */ + udp , /*!< Without a connection */ + SCTP , /*!< */ + sctp , /*!< */ + DTLS , /*!< */ + dtls , /*!< */ + COUNT__ + }; + Q_ENUMS(URI::Transport) + + /** + * @enum Section flags associated with each logical sections of the URI + * + * Those sections can be packed into a block to be used to define the + * expected URI syntax + * + */ + enum class Section { + CHEVRONS = 0x1 << 0, /*!< <code><sips:888@192.168.48.213:5060;transport=TLS> \_/ \_/ - |_________________Chevrons_______________________| - </code>*/ - SCHEME = 0x1 << 1, /*!< <code><sips:888@192.168.48.213:5060;transport=TLS> - \___/ - |______Scheme|</code> */ - USER_INFO = 0x1 << 2, /*!< <code><sips:888@192.168.48.213:5060;transport=TLS> - \___/ - |_________Userinfo</code> */ - HOSTNAME = 0x1 << 3, /*!< <code><sips:888@192.168.48.213:5060;transport=TLS> - \______________/ - |_________Hostname</code> */ - PORT = 0x1 << 4, /*!< <code><sips:888@192.168.48.213:5060;transport=TLS> - \____/ - |_____Port</code> */ - TRANSPORT = 0x1 << 5, /*!< <code><sips:888@192.168.48.213:5060;transport=TLS> - \_____________/ - Transport________|</code> */ - TAG = 0x1 << 6, /*!< <code><sips:888@192.168.48.213:5060;tag=b5c73d9ef> - \_____________/ - Tag_________|</code> */ - }; - - /** - * @enum ProtocolHint Expanded version of Account::Protocol - * - * This is used to make better choice when it come to choose an account or - * guess if the URI can be used with the current set et configured accounts. - * - * @warning This is an approximation. Those values are guessed using partial - * parsing (for performance) and are not definitive. - */ - enum class ProtocolHint { - SIP_OTHER = 0, /*!< Anything non empty that doesn't fit in other categories */ - RING = 1, /*!< Start with "ring:" and 45 ASCII chars OR 40 ASCII chars */ - IP = 2, /*!< Match an IPv4 address */ - SIP_HOST = 3, /*!< Has an @ and no "ring:" prefix */ - RING_USERNAME = 4, /*!< Anything that starts with "ring:" and isn't followed by 40 ASCII chars */ - }; - Q_ENUMS(URI::ProtocolHint) - - //Getter - QString hostname () const; - QString userinfo () const; - bool hasHostname () const; - bool hasPort () const; - int port () const; - SchemeType schemeType () const; - ProtocolHint protocolHint() const; - - //Setter - void setSchemeType(SchemeType t); - - //Converter - QString format(FlagPack<URI::Section> sections) const; - - /** - * Helper function which returns a QString containing a uri formatted to include at minimum the - * SCHEME and USER_INFO, and also the HOSTNAME and PORT, if available. - */ - QString full() const; - - URI& operator=(const URI&); + |_________________Chevrons_______________________| + </code>*/ + SCHEME = 0x1 << 1, /*!< <code><sips:888@192.168.48.213:5060;transport=TLS> + \___/ + |______Scheme|</code> */ + USER_INFO = 0x1 << 2, /*!< <code><sips:888@192.168.48.213:5060;transport=TLS> + \___/ + |_________Userinfo</code> */ + HOSTNAME = 0x1 << 3, /*!< <code><sips:888@192.168.48.213:5060;transport=TLS> + \______________/ + |_________Hostname</code> */ + PORT = 0x1 << 4, /*!< <code><sips:888@192.168.48.213:5060;transport=TLS> + \____/ + |_____Port</code> */ + TRANSPORT = 0x1 << 5, /*!< <code><sips:888@192.168.48.213:5060;transport=TLS> + \_____________/ + Transport________|</code> */ + TAG = 0x1 << 6, /*!< <code><sips:888@192.168.48.213:5060;tag=b5c73d9ef> + \_____________/ + Tag_________|</code> */ + }; + + /** + * @enum ProtocolHint Expanded version of Account::Protocol + * + * This is used to make better choice when it come to choose an account or + * guess if the URI can be used with the current set et configured accounts. + * + * @warning This is an approximation. Those values are guessed using partial + * parsing (for performance) and are not definitive. + */ + enum class ProtocolHint { + RING, /* Start with "ring:" and 45 ASCII chars OR 40 ASCII chars */ + IP, /* Match an IPv4 address */ + SIP_HOST, /* Start with "sip:", has an @ and no "ring:" prefix */ + SIP_OTHER, /* Start with "sip:" and doesn't fit in other categories */ + RING_USERNAME, /* Anything that starts with "ring:" and isn't followed by 40 ASCII chars */ + UNRECOGNIZED /* Anything that doesn't fit in other categories */ + }; + Q_ENUMS(URI::ProtocolHint) + + // Getter + QString hostname() const; + QString userinfo() const; + bool hasHostname() const; + bool hasPort() const; + int port() const; + SchemeType schemeType() const; + ProtocolHint protocolHint() const; + + // Setter + void setSchemeType(SchemeType t); + + // Converter + QString format(FlagPack<URI::Section> sections) const; + + /** + * Helper function which returns a QString containing a uri formatted to include at minimum the + * SCHEME and USER_INFO, and also the HOSTNAME and PORT, if available. + */ + QString full() const; + + URI& operator=(const URI&); private: - const QScopedPointer<URIPrivate> d_ptr; + std::unique_ptr<URIPimpl> pimpl_; }; + Q_DECLARE_METATYPE(URI) Q_DECLARE_METATYPE(URI::ProtocolHint) -- GitLab