Implement RFC3987 for parsing IRI (NonASCII characters in URL) to allow chat redirections on URL.

Move QML code into C++ in order to have an updated API that take account of unicode formats.
Fix images display that comes from URL in chats.


(cherry picked from commit 8df33fc546)
This commit is contained in:
Julien Wadel 2022-12-08 17:40:49 +01:00
parent e3add33455
commit cad5ed7635
11 changed files with 404 additions and 247 deletions

View file

@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## 5.0.1 - 2022-12-09
### Fixed
- RF3987 to allow IRI parsing in chats.
- Image display in chats from an URL.
## 5.0.0 - 2022-12-07

View file

@ -262,6 +262,7 @@ set(SOURCES
src/utils/LinphoneEnums.cpp
src/utils/MediastreamerUtils.cpp
src/utils/QExifImageHeader.cpp
src/utils/UriTools.cpp
src/utils/Utils.cpp
src/utils/plugins/PluginsManager.cpp
)
@ -398,6 +399,7 @@ set(HEADERS
src/utils/LinphoneEnums.hpp
src/utils/MediastreamerUtils.hpp
src/utils/QExifImageHeader.hpp
src/utils/UriTools.hpp
src/utils/Utils.hpp
src/utils/plugins/PluginsManager.hpp
)

View file

@ -426,7 +426,6 @@
<file>ui/modules/Linphone/View/SipAddressesView.qml</file>
<file>ui/scripts/Utils/port-tools.js</file>
<file>ui/scripts/Utils/qmldir</file>
<file>ui/scripts/Utils/uri-tools.js</file>
<file>ui/scripts/Utils/utils.js</file>
<file>ui/views/App/qmldir</file>
<file>ui/views/App/Calls/AbstractStartingCall.qml</file>

View file

@ -0,0 +1,293 @@
/*
* Copyright (c) 2010-2023 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* 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/>.
*/
// =============================================================================
// Library to deal with IRI and URI.
// See:
// IRI : https://tools.ietf.org/html/rfc3987
// URI : https://tools.ietf.org/html/rfc3986
// =============================================================================
#include "UriTools.hpp"
static UriTools gUriTools;
UriTools::UriTools(){
initRegularExpressions();
}
QVector<QPair<bool, QString> > UriTools::parseIri(const QString& text){
return parse(text, gUriTools.mIriRegularExpression);
}
QVector<QPair<bool, QString> > UriTools::parseUri(const QString& text){
return parse(text, gUriTools.mUriRegularExpression);
}
// Parse a text and return all lines where regex is matched or not
QVector<QPair<bool, QString> > UriTools::parse(const QString& text, const QRegularExpression regex){
QVector<QPair<bool, QString> > results;
int currentIndex = 0;
auto match = regex.match(text);
for (int i = 0; i <= match.lastCapturedIndex(); ++i) {
int startIndex = match.capturedStart(i);
if(currentIndex != startIndex){
results.push_back({false, text.mid(currentIndex, startIndex - currentIndex)});
}
results.push_back({true, match.captured(i)});
currentIndex = startIndex;
}
if(results.size() == 0)
results.push_back({false, text});
else{
currentIndex += results.back().second.length();
if( currentIndex < text.size())
results.push_back({false, text.mid(currentIndex)});
}
return results;
}
void UriTools::initRegularExpressions() {
// Level 0. --------------------------------------------------------------------
QString URI_DEC_OCTET = QString("(?:") +
"25[0-5]" +
"|" + "2[0-4]\\d" +
"|" + "1\\d{2}" +
"|" + "[1-9]\\d" +
"|" + "\\d" +
")";
QString URI_H16 = "[0-9A-Fa-f]{1,4}";
QString URI_PCT_ENCODED = "%[A-Fa-f\\d]{2}";
QString URI_PORT = "\\d*";
QString URI_SCHEME = "[a-zA-Z][\\w+\\.\\-]*";
QString URI_SUB_DELIMS = "[!$&\"()*+,;=]";
QString URI_UNRESERVED = "[\\w\\._~\\-]";
QString IRI_UCS_CHAR = QString("(?:") +
"[\\x{00A0}-\\x{D7FF}]" + "|" + "[\\x{F900}-\\x{FDCF}]" + "|" + "[\\x{FDF0}-\\x{FFEF}]" +
"|" + "[\\x{10000}-\\x{1FFFD}]" + "|" + "[\\x{20000}-\\x{2FFFD}]" + "|" + "[\\x{30000}-\\x{3FFFD}]" +
//"|" + "[\\x{D800\\x{DC00}-\\x{D83F\\x{DFFD}]" + "|" + "[\\x{D840\\x{DC00}-\\x{D87F\\x{DFFD}]" + "|" + "[\\x{D880\\x{DC00}-\\x{D8BF\\x{DFFD}]" +
"|" + "[\\x{40000}-\\x{4FFFD}]" + "|" + "[\\x{50000}-\\x{5FFFD}]" + "|" + "[\\x{60000}-\\x{6FFFD}]" +
//"|" + "[\\x{D8C0\\x{DC00}-\\x{D8FF\\x{DFFD}]" + "|" + "[\\x{D900\\x{DC00}-\\x{D93F\\x{DFFD}]" + "|" + "[\\x{D940\\x{DC00}-\\x{D97F\\x{DFFD}]" +
"|" + "[\\x{70000}-\\x{7FFFD}]" + "|" + "[\\x{80000}-\\x{8FFFD}]" + "|" + "[\\x{90000}-\\x{9FFFD}]" +
//"|" + "[\\x{D980\\x{DC00}-\\x{D9BF\\x{DFFD}]" + "|" + "[\\x{D9C0\\x{DC00}-\\x{D9FF\\x{DFFD}]" + "|" + "[\\x{DA00\\x{DC00}-\\x{DA3F\\x{DFFD}]" +
"|" + "[\\x{A0000}-\\x{AFFFD}]" + "|" + "[\\x{B0000}-\\x{BFFFD}]" + "|" + "[\\x{C0000}-\\x{CFFFD}]" +
//"|" + "[\\x{DA40\\x{DC00}-\\x{DA7F\\x{DFFD}]" + "|" + "[\\x{DA80\\x{DC00}-\\x{DABF\\x{DFFD}]" + "|" + "[\\x{DAC0\\x{DC00}-\\x{DAFF\\x{DFFD}]" +
"|" + "[\\x{D0000}-\\x{DFFFD}]" + "|" + "[\\x{E1000}-\\x{EFFFD}]" +
//"|" + "[\\x{DB00\\x{DC00}-\\x{DB3F\\x{DFFD}]" + "|" + "[\\x{DB44\\x{DC00}-\\x{DB7F\\x{DFFD}]" +
")";
QString IRI_PRIVATE = QString("(?:") +
"[\\x{E000}-\\x{F8FF}]" +
"|" + "[\\x{F0000}-\\x{FFFFD}]" + "|" + "[\\x{100000}-\\x{10FFFD}]" +
//"|" + "[\\x{DBC0\\x{DC00}-\\x{DBFF\\x{DFFD}]" + "|" + "[\\x{DBC0\\x{DC00}-\\x{DBFF\\x{DFFD}]" +
")";
// Level 1. --------------------------------------------------------------------
QString URI_IPV_FUTURE = QString("v[0-9A-Fa-f]+\\.") + "(?:" +
URI_UNRESERVED +
URI_SUB_DELIMS +
":" +
")";
QString IRI_UNRESERVED = QString("(?:") +
"[\\w\\._~\\-]" +
"|" + IRI_UCS_CHAR +
")";
QString URI_IPV4_ADDRESS = URI_DEC_OCTET + "\\." + URI_DEC_OCTET + "\\." +
URI_DEC_OCTET + "\\." + URI_DEC_OCTET;
QString URI_PCHAR = "(?:" +
URI_UNRESERVED +
"|" + URI_PCT_ENCODED +
"|" + URI_SUB_DELIMS +
"|" + "[:@]" +
")";
QString URI_REG_NAME = "(?:" +
URI_UNRESERVED +
"|" + URI_PCT_ENCODED +
"|" + URI_SUB_DELIMS +
")*";
QString URI_USERINFO = "(?:" +
URI_UNRESERVED +
"|" + URI_PCT_ENCODED +
"|" + URI_SUB_DELIMS +
"|" + ":" +
")*";
// Level 2. --------------------------------------------------------------------
QString URI_FRAGMENT = "(?:" +
URI_PCHAR +
"|" + "[/?]" +
")*";
QString URI_LS32 = "(?:" +
URI_H16 + ":" + URI_H16 +
"|" + URI_IPV4_ADDRESS +
")";
QString URI_QUERY = "(?:" +
URI_PCHAR +
"|" + "[/?]" +
")*";
QString URI_SEGMENT = URI_PCHAR + "*";
QString URI_SEGMENT_NZ = URI_PCHAR + "+";
QString IRI_PCHAR = "(?:" +
IRI_UNRESERVED +
"|" + URI_PCT_ENCODED +
"|" + URI_SUB_DELIMS +
"|" + "[:@]" +
")";
QString IRI_REG_NAME = "(?:" +
IRI_UNRESERVED +
"|" + URI_PCT_ENCODED +
"|" + URI_SUB_DELIMS +
")*";
QString IRI_USERINFO = "(?:" +
IRI_UNRESERVED +
"|" + URI_PCT_ENCODED +
"|" + URI_SUB_DELIMS +
"|" + ":" +
")*";
// Level 3. --------------------------------------------------------------------
QString URI_IPV6_ADDRESS = QString("(?:") +
"(?:" + URI_H16 + ":){6}" + URI_LS32 +
"|" + "::(?:" + URI_H16 + ":){5}" + URI_LS32 +
"|" + "\\[" + URI_H16 + "\\]::(?:" + URI_H16 + ":){4}" + URI_LS32 +
"|" + "\\[" + "(?:" + URI_H16 + ":)?" + URI_H16 + "\\]::(?:" + URI_H16 + ":){3}" + URI_LS32 +
"|" + "\\[" + "(?:" + URI_H16 + ":){0,2}" + URI_H16 + "\\]::(?:" + URI_H16 + ":){2}" + URI_LS32 +
"|" + "\\[" + "(?:" + URI_H16 + ":){0,3}" + URI_H16 + "\\]::" + URI_H16 + ":" + URI_LS32 +
"|" + "\\[" + "(?:" + URI_H16 + ":){0,4}" + URI_H16 + "\\]::" + URI_LS32 +
"|" + "\\[" + "(?:" + URI_H16 + ":){0,5}" + URI_H16 + "\\]::" + URI_H16 +
"|" + "\\[" + "(?:" + URI_H16 + ":){0,6}" + URI_H16 + "\\]::" +
")";
QString URI_PATH_ABEMPTY = QString("(?:") + "/" + URI_SEGMENT + ")*";
QString URI_PATH_ABSOLUTE = QString("/") +
"(?:" + URI_SEGMENT_NZ + "(?:" + "/" + URI_SEGMENT + ")*" + ")?";
QString URI_PATH_ROOTLESS =
URI_SEGMENT_NZ + "(?:" + "/" + URI_SEGMENT + ")*";
QString IRI_FRAGMENT = "(?:" +
IRI_PCHAR +
"|" + "[/?]" +
")*";
QString IRI_QUERY = "(?:" +
IRI_PCHAR +
"|" + IRI_PRIVATE +
"|" + "[/?]" +
")*";
QString IRI_SEGMENT = IRI_PCHAR + "*";
QString IRI_SEGMENT_NZ = IRI_PCHAR + "+";
// Level 4. --------------------------------------------------------------------
QString URI_IP_LITERAL = QString("\\[" )+
"(?:" +
URI_IPV6_ADDRESS +
"|" + URI_IPV_FUTURE +
")" +
"\\]";
QString IRI_PATH_ABEMPTY = QString("(?:") + "/" + IRI_SEGMENT + ")*";
QString IRI_PATH_ABSOLUTE = QString("/") +
"(?:" + IRI_SEGMENT_NZ + "(?:" + "/" + IRI_SEGMENT + ")*" + ")?";
QString IRI_PATH_ROOTLESS =
IRI_SEGMENT_NZ + "(?:" + "/" + IRI_SEGMENT + ")*";
// Level 5. --------------------------------------------------------------------
QString URI_HOST = "(?:" +
URI_REG_NAME +
"|" + URI_IPV4_ADDRESS +
"|" + URI_IP_LITERAL +
")";
QString IRI_HOST = "(?:" +
IRI_REG_NAME +
"|" + URI_IPV4_ADDRESS +
"|" + URI_IP_LITERAL +
")";
// Level 6. --------------------------------------------------------------------
QString URI_AUTHORITY = "(?:" + URI_USERINFO + "@" + ")?" +
URI_HOST +
"(?:" + ":" + URI_PORT + ")?";
QString IRI_AUTHORITY = "(?:" + IRI_USERINFO + "@" + ")?" +
IRI_HOST +
"(?:" + ":" + URI_PORT + ")?";
// Level 7. --------------------------------------------------------------------
// `path-empty` not used.
QString URI_HIER_PART = QString("(?:") +
"//" + URI_AUTHORITY + URI_PATH_ABEMPTY +
"|" + URI_PATH_ABSOLUTE +
"|" + URI_PATH_ROOTLESS +
")";
QString IRI_HIER_PART = QString("(?:") +
"//" + IRI_AUTHORITY + IRI_PATH_ABEMPTY +
"|" + IRI_PATH_ABSOLUTE +
"|" + IRI_PATH_ROOTLESS +
")";
// Level 8. --------------------------------------------------------------------
// Regex to match URI. It respects the RFC 3986.
QString URI = "(?:"
+ URI_SCHEME + ":" + "|" + "www\\." + ")"
+ URI_HIER_PART + "(?:" + "\\?" + URI_QUERY + ")?" +
"(?:" + "#" + URI_FRAGMENT + ")?";
// Regex to match URI. It respects the RFC 3987.
QString IRI = "(?:" + URI_SCHEME + ":" + "|" + "www\\." + ")"
+ IRI_HIER_PART + "(?:" + "\\?" + IRI_QUERY + ")?" +
"(?:" + "#" + IRI_FRAGMENT + ")?";
mIriRegularExpression = QRegularExpression(IRI,QRegularExpression::CaseInsensitiveOption | QRegularExpression::UseUnicodePropertiesOption);
mUriRegularExpression = QRegularExpression(URI,QRegularExpression::CaseInsensitiveOption | QRegularExpression::UseUnicodePropertiesOption);
}

View file

@ -0,0 +1,57 @@
/*
* Copyright (c) 2010-2023 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* 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/>.
*/
// =============================================================================
// Library to deal with IRI and URI.
// See:
// IRI : https://tools.ietf.org/html/rfc3987
// NOTE : Unicodes after \uFFFF are not supported by the QML RegExp (or the right syntax has not been found) : "Invalid regular expression" (even with surrogate pairs). Parts have been commented out for latter use.
// URI : https://tools.ietf.org/html/rfc3986
// =============================================================================
#ifndef URI_TOOLS_H
#define URI_TOOLS_H
#include <QPair>
#include <QString>
#include <QVector>
#include <QRegularExpression>
class UriTools{
public:
UriTools();
bool mSupportUrl = true;
static QVector<QPair<bool, QString> > parseIri(const QString& text);
static QVector<QPair<bool, QString> > parseUri(const QString& text);
static QRegularExpression getRegularExpression();
private:
void initRegularExpressions();
static QVector<QPair<bool, QString> > parse(const QString& text, const QRegularExpression regex);
QRegularExpression mIriRegularExpression;//https://tools.ietf.org/html/rfc3987
QRegularExpression mUriRegularExpression;//https://tools.ietf.org/html/rfc3986
};
#endif

View file

@ -31,6 +31,7 @@
#include "config.h"
#include "Utils.hpp"
#include "UriTools.hpp"
#include "components/core/CoreManager.hpp"
#include "components/contacts/ContactsListModel.hpp"
#include "components/contact/ContactModel.hpp"
@ -604,4 +605,46 @@ QString Utils::getFileChecksum(const QString& filePath){
}
}
return QString();
}
QString Utils::encodeTextToQmlRichFormat(const QString& text, const QVariantMap& options){
QString images;
QStringList formattedText;
QStringList imageFormat;
for(auto format : QImageReader::supportedImageFormats())
imageFormat.append(QString::fromLatin1(format).toUpper());
auto iriParsed = UriTools::parseIri(text);
for(int i = 0 ; i < iriParsed.size() ; ++i){
QString iri = iriParsed[i].second.replace('&', "&amp;")
.replace('<', "\u2063&lt;")
.replace('>', "\u2063&gt;")
.replace('"', "&quot;")
.replace('\'', "&#039;");
if(!iriParsed[i].first)
formattedText.append(iri);
else{
QString uri = iriParsed[i].second.left(3) == "www" ? "http://"+iriParsed[i].second : iriParsed[i].second ;
int extIndex = iriParsed[i].second.lastIndexOf('.');
QString ext;
if( extIndex >= 0)
ext = iriParsed[i].second.mid(extIndex+1).toUpper();
if(imageFormat.contains(ext.toLatin1())){// imagesHeight is not used because of bugs on display (blank image if set without width)
images += "<a href=\"" + uri + "\"><img" + (
options.contains("imagesWidth")
? QString(" width='") + options["imagesWidth"].toString() + "'"
: ""
) + (
options.contains("imagesWidth")
? QString(" height='auto'")
: ""
) + " src=\"" + iriParsed[i].second + "\" /></a>";
}else
formattedText.append( "<a href=\"" + uri + "\">" + iri + "</a>");
}
}
if(images != "")
images = "<div>" + images +"</div>";
return images + "<p style=\"white-space:pre-wrap;\">" + formattedText.join("") + "</p>";
}

View file

@ -66,6 +66,7 @@ public:
Q_INVOKABLE QSize getImageSize(const QString& url);
Q_INVOKABLE static QPoint getCursorPosition();
Q_INVOKABLE static QString getFileChecksum(const QString& filePath);
Q_INVOKABLE static QString encodeTextToQmlRichFormat(const QString& text, const QVariantMap& options);
//----------------------------------------------------------------------------------

View file

@ -42,7 +42,7 @@ TextEdit {
font.family: customFont.family
font.pointSize: Units.dp * customFont.pointSize
text: visible ? Utils.encodeTextToQmlRichFormat(contentModel.text, {
text: visible ? UtilsCpp.encodeTextToQmlRichFormat(contentModel.text, {
imagesHeight: ChatStyle.entry.message.images.height,
imagesWidth: ChatStyle.entry.message.images.width
})

View file

@ -243,7 +243,8 @@ QtObject {
}
property QtObject images: QtObject {
property int height: 48
property int height: 240
property int width: 240
}
property QtObject incoming: QtObject {

View file

@ -1,164 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* 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/>.
*/
// =============================================================================
// Library to deal with URI.
// See: https://tools.ietf.org/html/rfc3986#section-1.3
// =============================================================================
.pragma library
// Options.
// If true, strings starting with `www.` can be detected.
// Not standard but helpful.
var SUPPORTS_URL = true
// Level 0. --------------------------------------------------------------------
var URI_DEC_OCTET = '(?:' +
'25[0-5]' +
'|' + '2[0-4]\\d' +
'|' + '1\\d{2}' +
'|' + '[1-9]\\d' +
'|' + '\\d' +
')'
var URI_H16 = '[0-9A-Fa-f]{1,4}'
var URI_PCT_ENCODED = '%[A-Fa-f\\d]{2}'
var URI_PORT = '\\d*'
var URI_SCHEME = '[a-zA-Z][\\w+\-\.]*'
var URI_SUB_DELIMS = '[!$&\'()*+,;=]'
var URI_UNRESERVED = '[\\w\-\._~]'
// Level 1. --------------------------------------------------------------------
var URI_IPV_FUTURE = 'v[0-9A-Fa-f]+\\.' + '(?:' +
URI_UNRESERVED +
URI_SUB_DELIMS +
':' +
')'
var URI_IPV4_ADDRESS = URI_DEC_OCTET + '\\.' + URI_DEC_OCTET + '\\.' +
URI_DEC_OCTET + '\\.' + URI_DEC_OCTET
var URI_PCHAR = '(?:' +
URI_UNRESERVED +
'|' + URI_PCT_ENCODED +
'|' + URI_SUB_DELIMS +
'|' + '[:@]' +
')'
var URI_REG_NAME = '(?:' +
URI_UNRESERVED +
'|' + URI_PCT_ENCODED +
'|' + URI_SUB_DELIMS +
')*'
var URI_USERINFO = '(?:' +
URI_UNRESERVED +
'|' + URI_PCT_ENCODED +
'|' + URI_SUB_DELIMS +
'|' + ':' +
')*'
// Level 2. --------------------------------------------------------------------
var URI_FRAGMENT = '(?:' +
URI_PCHAR +
'|' + '[/?]' +
')*'
var URI_LS32 = '(?:' +
URI_H16 + ':' + URI_H16 +
'|' + URI_IPV4_ADDRESS +
')'
var URI_QUERY = '(?:' +
URI_PCHAR +
'|' + '[/?]' +
')*'
var URI_SEGMENT = URI_PCHAR + '*'
var URI_SEGMENT_NZ = URI_PCHAR + '+'
// Level 3. --------------------------------------------------------------------
var URI_IPV6_ADDRESS = '(?:' +
'(?:' + URI_H16 + ':){6}' + URI_LS32 +
'|' + '::(?:' + URI_H16 + ':){5}' + URI_LS32 +
'|' + '\\[' + URI_H16 + '\\]::(?:' + URI_H16 + ':){4}' + URI_LS32 +
'|' + '\\[' + '(?:' + URI_H16 + ':)?' + URI_H16 + '\\]::(?:' + URI_H16 + ':){3}' + URI_LS32 +
'|' + '\\[' + '(?:' + URI_H16 + ':){0,2}' + URI_H16 + '\\]::(?:' + URI_H16 + ':){2}' + URI_LS32 +
'|' + '\\[' + '(?:' + URI_H16 + ':){0,3}' + URI_H16 + '\\]::' + URI_H16 + ':' + URI_LS32 +
'|' + '\\[' + '(?:' + URI_H16 + ':){0,4}' + URI_H16 + '\\]::' + URI_LS32 +
'|' + '\\[' + '(?:' + URI_H16 + ':){0,5}' + URI_H16 + '\\]::' + URI_H16 +
'|' + '\\[' + '(?:' + URI_H16 + ':){0,6}' + URI_H16 + '\\]::' +
')'
var URI_PATH_ABEMPTY = '(?:' + '/' + URI_SEGMENT + ')*'
var URI_PATH_ABSOLUTE = '/' +
'(?:' + URI_SEGMENT_NZ + '(?:' + '/' + URI_SEGMENT + ')*' + ')?'
var URI_PATH_ROOTLESS =
URI_SEGMENT_NZ + '(?:' + '/' + URI_SEGMENT + ')*'
// Level 4. --------------------------------------------------------------------
var URI_IP_LITERAL = '\\[' +
'(?:' +
URI_IPV6_ADDRESS +
'|' + URI_IPV_FUTURE +
')' +
'\\]'
// Level 5. --------------------------------------------------------------------
var URI_HOST = '(?:' +
URI_REG_NAME +
'|' + URI_IPV4_ADDRESS +
'|' + URI_IP_LITERAL +
')'
// Level 6. --------------------------------------------------------------------
var URI_AUTHORITY = '(?:' + URI_USERINFO + '@' + ')?' +
URI_HOST +
'(?:' + ':' + URI_PORT + ')?'
// Level 7. --------------------------------------------------------------------
// `path-empty` not used.
var URI_HIER_PART = '(?:' +
'//' + URI_AUTHORITY + URI_PATH_ABEMPTY +
'|' + URI_PATH_ABSOLUTE +
'|' + URI_PATH_ROOTLESS +
')'
// Level 8. --------------------------------------------------------------------
// Regex to match URI. It respects the RFC 3986.
var URI = (SUPPORTS_URL
? '(?:' + URI_SCHEME + ':' + '|' + 'www\\.' + ')'
: URI_SCHEME + ':'
) + URI_HIER_PART + '(?:' + '\\?' + URI_QUERY + ')?' +
'(?:' + '#' + URI_FRAGMENT + ')?'
var URI_REGEX = new RegExp('(' + URI + ')', 'g')

View file

@ -27,7 +27,6 @@
.import Linphone 1.0 as Linphone
.import 'port-tools.js' as PortTools
.import 'uri-tools.js' as UriTools
// =============================================================================
// Constants.
@ -97,51 +96,6 @@ function createObject (source, parent, options) {
// -----------------------------------------------------------------------------
function encodeTextToQmlRichFormat (text, options) {
var images = ''
if (!options) {
options = {}
}
var formattedText = execAll(UriTools.URI_REGEX, text, function (str, valid) {
if (!valid) {
return unscapeHtml(str)
}
var uri = startsWith(str, 'www.') ? 'http://' + str : str
var ext = getExtension(str)
if (includes([ 'jpg', 'jpeg', 'gif', 'png', 'svg' ], ext)) {
images += '<a href="' + uri + '"><img' + (
options.imagesWidth != null
? ' width="' + options.imagesWidth + '"'
: ''
) + (
options.imagesHeight != null
? ' height="' + options.imagesHeight + '"'
: ''
) + ' src="' + str + '" /></a>'
}
return '<a href="' + uri + '">' + unscapeHtml(str) + '</a>'
}).join('')
if (images.length > 0) {
images = '<div>' + images + '</div>'
}
return images.concat('<p style="white-space:pre-wrap;">' + formattedText + '</p>')
}
function extractFirstUri (str) {
var res = str.match(UriTools.URI_REGEX)
return res == null || startsWith(res[0], 'www')
? undefined
: res[0]
}
// -----------------------------------------------------------------------------
function getSystemPathFromUri (uri) {
var str = uri.toString()
if (startsWith(str, 'file://')) {
@ -385,40 +339,6 @@ function dirname (str) {
// -----------------------------------------------------------------------------
function execAll (regex, text, cb) {
var index = 0
var arr = []
var match
if (!cb) {
cb = function (text) {
return text
}
}
while ((match = regex.exec(text))) {
var curIndex = match.index
var matchStr = match[0]
if (curIndex > index) {
arr.push(cb(text.substring(index, curIndex), false))
}
arr.push(cb(matchStr, true))
index = curIndex + matchStr.length
}
var length = text.length
if (index < length) {
arr.push(cb(text.substring(index, length)))
}
return arr
}
// -----------------------------------------------------------------------------
function extractProperties (obj, pattern) {
if (!pattern) {
return {}