mirror of
https://gitlab.linphone.org/BC/public/linphone-desktop.git
synced 2026-01-28 17:29:19 +00:00
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.
This commit is contained in:
parent
b19af5d882
commit
8df33fc546
11 changed files with 404 additions and 247 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
293
linphone-app/src/utils/UriTools.cpp
Normal file
293
linphone-app/src/utils/UriTools.cpp
Normal 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);
|
||||
}
|
||||
57
linphone-app/src/utils/UriTools.hpp
Normal file
57
linphone-app/src/utils/UriTools.hpp
Normal 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
|
||||
|
|
@ -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('&', "&")
|
||||
.replace('<', "\u2063<")
|
||||
.replace('>', "\u2063>")
|
||||
.replace('"', """)
|
||||
.replace('\'', "'");
|
||||
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>";
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
@ -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 {}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue