Skip to content
Snippets Groups Projects
Commit 7a7b52ef authored by Emmanuel Lepage Vallee's avatar Emmanuel Lepage Vallee
Browse files

ContactMethod: Generate a protocol hint

Refs #70363
parent dd6df5de
No related branches found
No related tags found
No related merge requests found
...@@ -939,16 +939,21 @@ bool Account::supportScheme( URI::SchemeType type ) ...@@ -939,16 +939,21 @@ bool Account::supportScheme( URI::SchemeType type )
switch(type) { switch(type) {
case URI::SchemeType::NONE : case URI::SchemeType::NONE :
return true; return true;
break;
case URI::SchemeType::SIP : case URI::SchemeType::SIP :
case URI::SchemeType::SIPS : case URI::SchemeType::SIPS :
if (protocol() == Account::Protocol::SIP) if (protocol() == Account::Protocol::SIP)
return true; return true;
break;
case URI::SchemeType::IAX : case URI::SchemeType::IAX :
case URI::SchemeType::IAX2 :
if (protocol() == Account::Protocol::IAX) if (protocol() == Account::Protocol::IAX)
return true; return true;
break;
case URI::SchemeType::RING : case URI::SchemeType::RING :
if (protocol() == Account::Protocol::RING) if (protocol() == Account::Protocol::RING)
return true; return true;
break;
} }
return false; return false;
} }
......
...@@ -612,7 +612,7 @@ bool AccountModel::isSipSupported() const ...@@ -612,7 +612,7 @@ bool AccountModel::isSipSupported() const
return d_ptr->m_lSupportedProtocols[Account::Protocol::SIP]; return d_ptr->m_lSupportedProtocols[Account::Protocol::SIP];
} }
bool AccountModel::isAixSupported() const bool AccountModel::isIAXSupported() const
{ {
return d_ptr->m_lSupportedProtocols[Account::Protocol::IAX]; return d_ptr->m_lSupportedProtocols[Account::Protocol::IAX];
} }
......
...@@ -43,7 +43,7 @@ public: ...@@ -43,7 +43,7 @@ public:
Q_PROPERTY(bool presenceSubscribeSupported READ isPresenceSubscribeSupported ) Q_PROPERTY(bool presenceSubscribeSupported READ isPresenceSubscribeSupported )
Q_PROPERTY(ProtocolModel* protocolModel READ protocolModel ) Q_PROPERTY(ProtocolModel* protocolModel READ protocolModel )
Q_PROPERTY(bool isSipSupported READ isSipSupported NOTIFY supportedProtocolsChanged) Q_PROPERTY(bool isSipSupported READ isSipSupported NOTIFY supportedProtocolsChanged)
Q_PROPERTY(bool isAixSupported READ isAixSupported NOTIFY supportedProtocolsChanged) Q_PROPERTY(bool isIAXSupported READ isIAXSupported NOTIFY supportedProtocolsChanged)
Q_PROPERTY(bool isIP2IPSupported READ isIP2IPSupported NOTIFY supportedProtocolsChanged) Q_PROPERTY(bool isIP2IPSupported READ isIP2IPSupported NOTIFY supportedProtocolsChanged)
Q_PROPERTY(bool isRingSupported READ isRingSupported NOTIFY supportedProtocolsChanged) Q_PROPERTY(bool isRingSupported READ isRingSupported NOTIFY supportedProtocolsChanged)
...@@ -66,7 +66,7 @@ public: ...@@ -66,7 +66,7 @@ public:
bool isPresenceSubscribeSupported( ) const; bool isPresenceSubscribeSupported( ) const;
ProtocolModel* protocolModel ( ) const; ProtocolModel* protocolModel ( ) const;
bool isSipSupported ( ) const; bool isSipSupported ( ) const;
bool isAixSupported ( ) const; bool isIAXSupported ( ) const;
bool isIP2IPSupported ( ) const; bool isIP2IPSupported ( ) const;
bool isRingSupported ( ) const; bool isRingSupported ( ) const;
......
...@@ -414,6 +414,12 @@ QString ContactMethod::uid() const ...@@ -414,6 +414,12 @@ QString ContactMethod::uid() const
return d_ptr->m_Uid.isEmpty()?toHash():d_ptr->m_Uid; return d_ptr->m_Uid.isEmpty()?toHash():d_ptr->m_Uid;
} }
///Return the URI protocol hint
URI::ProtocolHint ContactMethod::protocolHint() const
{
return d_ptr->m_Uri.protocolHint();
}
///Return all calls from this number ///Return all calls from this number
QList<Call*> ContactMethod::calls() const QList<Call*> ContactMethod::calls() const
{ {
...@@ -597,3 +603,5 @@ TemporaryContactMethod::TemporaryContactMethod(const ContactMethod* number) : ...@@ -597,3 +603,5 @@ TemporaryContactMethod::TemporaryContactMethod(const ContactMethod* number) :
setAccount(number->account()); setAccount(number->account());
} }
} }
Q_DECLARE_METATYPE(QList<Call*>)
...@@ -54,26 +54,27 @@ public: ...@@ -54,26 +54,27 @@ public:
}; };
//Properties //Properties
Q_PROPERTY(Account* account READ account WRITE setAccount ) Q_PROPERTY(Account* account READ account WRITE setAccount )
Q_PROPERTY(Person* person READ contact WRITE setPerson ) Q_PROPERTY(Person* person READ contact WRITE setPerson )
Q_PROPERTY(int lastUsed READ lastUsed ) Q_PROPERTY(int lastUsed READ lastUsed )
Q_PROPERTY(QString uri READ uri ) Q_PROPERTY(QString uri READ uri )
Q_PROPERTY(int callCount READ callCount ) Q_PROPERTY(int callCount READ callCount )
Q_PROPERTY(QList<Call*> calls READ calls ) Q_PROPERTY(QList<Call*> calls READ calls )
Q_PROPERTY(int popularityIndex READ popularityIndex ) Q_PROPERTY(int popularityIndex READ popularityIndex )
Q_PROPERTY(bool bookmarked READ isBookmarked ) Q_PROPERTY(bool bookmarked READ isBookmarked )
Q_PROPERTY(QString uid READ uid WRITE setUid ) Q_PROPERTY(QString uid READ uid WRITE setUid )
Q_PROPERTY(bool isTracked READ isTracked NOTIFY trackedChanged ) Q_PROPERTY(bool isTracked READ isTracked NOTIFY trackedChanged )
Q_PROPERTY(bool isPresent READ isPresent NOTIFY presentChanged ) Q_PROPERTY(bool isPresent READ isPresent NOTIFY presentChanged )
Q_PROPERTY(bool supportPresence READ supportPresence ) Q_PROPERTY(bool supportPresence READ supportPresence )
Q_PROPERTY(QString presenceMessage READ presenceMessage NOTIFY presenceMessageChanged ) Q_PROPERTY(QString presenceMessage READ presenceMessage NOTIFY presenceMessageChanged )
Q_PROPERTY(uint weekCount READ weekCount ) Q_PROPERTY(uint weekCount READ weekCount )
Q_PROPERTY(uint trimCount READ trimCount ) Q_PROPERTY(uint trimCount READ trimCount )
Q_PROPERTY(bool haveCalled READ haveCalled ) Q_PROPERTY(bool haveCalled READ haveCalled )
Q_PROPERTY(QString primaryName READ primaryName ) Q_PROPERTY(QString primaryName READ primaryName )
Q_PROPERTY(bool isBookmarked READ isBookmarked ) Q_PROPERTY(bool isBookmarked READ isBookmarked )
Q_PROPERTY(QVariant icon READ icon ) Q_PROPERTY(QVariant icon READ icon )
Q_PROPERTY(int totalSpentTime READ totalSpentTime ) Q_PROPERTY(int totalSpentTime READ totalSpentTime )
Q_PROPERTY(URI::ProtocolHint protocolHint READ protocolHint )
// Q_PROPERTY(QHash<QString,int> alternativeNames READ alternativeNames ) // Q_PROPERTY(QHash<QString,int> alternativeNames READ alternativeNames )
...@@ -110,6 +111,7 @@ public: ...@@ -110,6 +111,7 @@ public:
QVariant icon () const; QVariant icon () const;
int totalSpentTime () const; int totalSpentTime () const;
QString uid () const; QString uid () const;
URI::ProtocolHint protocolHint () const;
QVariant roleData(int role) const; QVariant roleData(int role) const;
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
//Ring library //Ring library
#include "contactmethod.h" #include "contactmethod.h"
#include "accountmodel.h"
#include "collectioninterface.h" #include "collectioninterface.h"
#include "transitionalpersonbackend.h" #include "transitionalpersonbackend.h"
#include "account.h" #include "account.h"
...@@ -450,6 +451,43 @@ bool Person::supportPresence() const ...@@ -450,6 +451,43 @@ bool Person::supportPresence() const
return false; return false;
} }
///Return true if there is a change one if the account can be used to reach that person
bool Person::isReachable() const
{
if (!d_ptr->m_Numbers.size())
return false;
AccountModel* m = AccountModel::instance();
const bool hasSip = m->isSipSupported ();
const bool hasIAX = m->isIAXSupported ();
const bool hasIP2IP = m->isIP2IPSupported();
const bool hasRing = m->isRingSupported ();
for (const ContactMethod* n : d_ptr->m_Numbers) {
switch (n->protocolHint()) {
case URI::ProtocolHint::SIP_HOST :
case URI::ProtocolHint::IP :
if (hasIP2IP)
return true;
//no break
case URI::ProtocolHint::SIP_OTHER:
if (hasSip)
return true;
break;
case URI::ProtocolHint::IAX :
if (hasIAX)
return true;
break;
case URI::ProtocolHint::RING :
if (hasRing)
return true;
break;
}
}
return false;
}
///Recomputing the filter string is heavy, cache it ///Recomputing the filter string is heavy, cache it
QString Person::filterString() const QString Person::filterString() const
{ {
......
...@@ -144,6 +144,7 @@ public: ...@@ -144,6 +144,7 @@ public:
bool isPresent () const; bool isPresent () const;
bool isTracked () const; bool isTracked () const;
bool supportPresence () const; bool supportPresence () const;
bool isReachable () const;
//Setters //Setters
void setContactMethods ( ContactMethods ); void setContactMethods ( ContactMethods );
......
...@@ -17,30 +17,45 @@ ...@@ -17,30 +17,45 @@
***************************************************************************/ ***************************************************************************/
#include "uri.h" #include "uri.h"
constexpr const char* URI::schemeNames[];
class URIPrivate class URIPrivate
{ {
public: public:
///Strings associated with SchemeType
constexpr static const char* schemeNames[] = {
/*NONE = */ "" ,
/*SIP = */ "sip:" ,
/*SIPS = */ "sips:",
/*IAX = */ "iax:" ,
/*IAX2 = */ "iax2:",
/*RING = */ "ring:",
};
URIPrivate(QString* uri); URIPrivate(QString* uri);
//Attributes //Attributes
QString m_Hostname ; QString m_Hostname ;
QString m_Userinfo ; QString m_Userinfo ;
QStringList m_lAttributes ; QStringList m_lAttributes ;
QString m_Stripped ; QString m_Stripped ;
URI::SchemeType m_HeaderType ; URI::SchemeType m_HeaderType ;
bool m_hasChevrons ; bool m_hasChevrons ;
bool m_Parsed ; bool m_Parsed ;
bool m_HasAt ;
URI::ProtocolHint m_ProtocolHint;
bool m_HintParsed ;
//Helper //Helper
static QString strip(const QString& uri, URI::SchemeType& scheme); static QString strip(const QString& uri, URI::SchemeType& scheme);
void parse(); void parse();
static bool checkIp(const QString& str, bool &isHash);
private: private:
QString* q_ptr; QString* q_ptr;
}; };
constexpr const char* URIPrivate::schemeNames[];
URIPrivate::URIPrivate(QString* uri) : m_Parsed(false),m_HeaderType(URI::SchemeType::NONE),q_ptr(uri), URIPrivate::URIPrivate(QString* uri) : m_Parsed(false),m_HeaderType(URI::SchemeType::NONE),q_ptr(uri),
m_hasChevrons(false) m_hasChevrons(false),m_HasAt(false),m_ProtocolHint(URI::ProtocolHint::SIP_OTHER),m_HintParsed(false)
{ {
} }
...@@ -89,26 +104,50 @@ QString URIPrivate::strip(const QString& uri, URI::SchemeType& scheme) ...@@ -89,26 +104,50 @@ QString URIPrivate::strip(const QString& uri, URI::SchemeType& scheme)
{ {
if (uri.isEmpty()) if (uri.isEmpty())
return QString(); return QString();
int start(0),end(uri.size()-1); //Other type of comparisons were too slow
if (end > 5 && uri[0] == '<' ) { int start(uri[0] == '<'?1:0),end(uri.size()-1); //Other type of comparisons were too slow
if (uri[4] == ':') { uchar c;
scheme = uri[1] == 's'?URI::SchemeType::SIP : URI::SchemeType::IAX;
start = 5; //Assume the scheme is either iax, 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 && (c = uri[start+3].toLatin1()) == ':') {
switch (c) {
case 'i':
scheme = URI::SchemeType::IAX;
break;
case 's':
scheme = URI::SchemeType::SIP;
break;
} }
else if (uri[5] == ':') { start = 5;
scheme = URI::SchemeType::SIPS; }
start = 6; else if (end > start+4 && (c = uri[start+4].toLatin1()) == ':') {
switch (c) {
case 'i':
scheme = URI::SchemeType::IAX2;
break;
case 'r':
scheme = URI::SchemeType::RING;
break;
case 's':
scheme = URI::SchemeType::SIPS;
break;
} }
start = 6;
} }
if (end && uri[end] == '>') if (end && uri[end] == '>')
end--; end--;
else if (start) { else if (start) {
//TODO there may be a ';' section with arguments, check //TODO there may be a ';' section with arguments, check
} }
return uri.mid(start,end-start+1); return uri.mid(start,end-start+1);
} }
/**Return the domaine of an URI /**
* Return the domaine of an URI
* *
* For example, example.com in <sip:12345@example.com> * For example, example.com in <sip:12345@example.com>
*/ */
...@@ -141,14 +180,117 @@ URI::SchemeType URI::schemeType() const ...@@ -141,14 +180,117 @@ URI::SchemeType URI::schemeType() const
return d_ptr->m_HeaderType; return d_ptr->m_HeaderType;
} }
/**
* "Fast" Ipv4 and Ipv6 check, accept 999.999.999.999, :::::::::FF and other
* atrocities, but at least perform a O(N) ish check and validate the hash
*
* @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)
{
char* raw = str.toLatin1().data();
ushort max = str.size();
if (max < 3 || max > 45)
return false;
uchar dc(0),sc(0),i(0),d(0),hx(1);
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':
if (++d > 3 && dc)
return false;
break;
case ':':
isHash = false;
sc++;
//No break
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:
return false;
};
i++;
}
return (hx && dc == 3 && d < 4) ^ (~(sc < 2 || dc));
}
/**
* This method return an hint to guess the protocol that could be used to call
* this URI. It is a quick guess, not something that should be trusted
*
* @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;
d_ptr->m_ProtocolHint = \
(
//Step one : Check IAX protocol, is has already been detected at this point
d_ptr->m_HeaderType == URI::SchemeType::IAX2 || d_ptr->m_HeaderType == URI::SchemeType::IAX
? URI::ProtocolHint::IAX
: (
//Step two : check IP
URIPrivate::checkIp(d_ptr->m_Userinfo,isHash) ? URI::ProtocolHint::IP
: (
//Step three : Check RING protocol, is has already been detected at this point
d_ptr->m_HeaderType == URI::SchemeType::RING && isHash ? URI::ProtocolHint::RING
: (
//Step four : Differentiate between ***@*** and *** type URIs
d_ptr->m_HasAt ? URI::ProtocolHint::SIP_HOST : URI::ProtocolHint::SIP_OTHER
))));
d_ptr->m_HintParsed = true;
qDebug() << "ICI" << (int)d_ptr->m_ProtocolHint;
}
return d_ptr->m_ProtocolHint;
}
///Keep a cache of the values to avoid re-parsing them ///Keep a cache of the values to avoid re-parsing them
void URIPrivate::parse() void URIPrivate::parse()
{ {
//TODO DHT hashes have 40 chars or 45 with sips:/ring: is set //FIXME the indexOf is done twice, the second time could be avoided
if (q_ptr->indexOf('@') != -1) { if (q_ptr->indexOf('@') != -1) {
const QStringList split = q_ptr->split('@'); const QStringList split = q_ptr->split('@');
m_Hostname = split[1];//split[1].left(split[1].size()) m_Hostname = split[1];//split[1].left(split[1].size())
m_Userinfo = split[0]; m_Userinfo = split[0];
m_HasAt = split.size();
m_Parsed = true; m_Parsed = true;
} }
} }
...@@ -171,6 +313,28 @@ QString URI::userinfo() const ...@@ -171,6 +313,28 @@ QString URI::userinfo() const
QString URI::fullUri() const QString URI::fullUri() const
{ {
return QString("<%1%2>") return QString("<%1%2>")
.arg(schemeNames[static_cast<int>(d_ptr->m_HeaderType == SchemeType::NONE?SchemeType::SIP:d_ptr->m_HeaderType)]) .arg(URIPrivate::schemeNames[static_cast<int>(d_ptr->m_HeaderType == SchemeType::NONE?SchemeType::SIP:d_ptr->m_HeaderType)])
.arg(*this); .arg(*this);
} }
QDataStream& operator<<( QDataStream& stream, const URI::ProtocolHint& ph )
{
switch(ph) {
case URI::ProtocolHint::SIP_OTHER:
stream << "SIP_OTHER";
break;
case URI::ProtocolHint::IAX :
stream << "IAX";
break;
case URI::ProtocolHint::RING :
stream << "RING";
break;
case URI::ProtocolHint::IP :
stream << "IP";
break;
case URI::ProtocolHint::SIP_HOST :
stream << "SIP_HOST";
break;
}
return stream;
}
...@@ -23,13 +23,14 @@ ...@@ -23,13 +23,14 @@
#include <QStringList> #include <QStringList>
class URIPrivate; class URIPrivate;
class QDataStream;
/** /**
* @class URI A specialized string with multiple attributes * @class URI A specialized string with multiple attributes
* *
* Most of Ring-KDE handle uri as strings, but more * Most of LibRingClient handle uri as strings, but more
* advanced algorithms need to access the various sections. * advanced algorithms need to access the various sections.
* *
* Here is some example of common numbers/URIs: * Here is some example of common numbers/URIs:
* * 123 * * 123
* * 123@192.168.123.123 * * 123@192.168.123.123
...@@ -43,12 +44,12 @@ class URIPrivate; ...@@ -43,12 +44,12 @@ class URIPrivate;
* * iax:example.com/alice * * iax:example.com/alice
* * iax:johnQ@example.com/12022561414 * * iax:johnQ@example.com/12022561414
* * iax:example.com:4570/alice?friends * * iax:example.com:4570/alice?friends
* *
* @ref http://tools.ietf.org/html/rfc5456#page-8 * @ref http://tools.ietf.org/html/rfc5456#page-8
* @ref http://tools.ietf.org/html/rfc3986 * @ref http://tools.ietf.org/html/rfc3986
* @ref http://tools.ietf.org/html/rfc3261 * @ref http://tools.ietf.org/html/rfc3261
* @ref http://tools.ietf.org/html/rfc5630 * @ref http://tools.ietf.org/html/rfc5630
* *
* From the RFC: * From the RFC:
* foo://example.com:8042/over/there?name=ferret#nose * foo://example.com:8042/over/there?name=ferret#nose
* \_/ \______________/\_________/ \_________/ \__/ * \_/ \______________/\_________/ \_________/ \__/
...@@ -85,17 +86,10 @@ public: ...@@ -85,17 +86,10 @@ public:
SIP , SIP ,
SIPS , SIPS ,
IAX , IAX ,
IAX2 ,
RING , RING ,
}; };
Q_ENUMS(URI::SchemeType)
///Strings associated with SchemeType
constexpr static const char* schemeNames[] = {
/*NONE = */ "" ,
/*SIP = */ "sip:" ,
/*SIPS = */ "sips:",
/*IAX = */ "iax:" ,
/*RING = */ "ring:",
};
/** /**
* @enum Transport each known valid transport types * @enum Transport each known valid transport types
...@@ -112,18 +106,41 @@ public: ...@@ -112,18 +106,41 @@ public:
SCTP , /*!< */ SCTP , /*!< */
sctp , /*!< */ sctp , /*!< */
}; };
Q_ENUMS(URI::Transport)
/**
* @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 */
IAX = 1, /*!< Start with "iax:" or "iax2:" */
RING = 2, /*!< Start with "ring:" and has 45 ASCII characters */
IP = 3, /*!< Match an IPv4 address */
SIP_HOST = 4, /*!< Has an @ and no "ring:" prefix */
};
Q_ENUMS(URI::ProtocolHint)
QString hostname () const; QString hostname () const;
QString fullUri () const; QString fullUri () const;
QString userinfo () const; QString userinfo () const;
bool hasHostname () const; bool hasHostname () const;
SchemeType schemeType () const; SchemeType schemeType () const;
ProtocolHint protocolHint() const;
URI& operator=(const URI&); URI& operator=(const URI&);
private: private:
const QScopedPointer<URIPrivate> d_ptr; const QScopedPointer<URIPrivate> d_ptr;
}; };
// Q_DECLARE_METATYPE(URI*)
Q_DECLARE_METATYPE(URI::ProtocolHint)
QDataStream& operator<< ( QDataStream& stream, const URI::ProtocolHint& ph );
#endif //URI_H #endif //URI_H
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment