-
Armin Felder authoredArmin Felder authored
utils.cpp 14.24 KiB
/********************************************************************************************
* *
* Copyright (C) 2017 Armin Felder, Dennis Beier *
* This file is part of RocketChatMobileEngine <https://git.fairkom.net/chat/fairchat>. *
* *
* RocketChatMobileEngine 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. *
* *
* RocketChatMobileEngine 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 RocketChatMobileEngine. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************************/
#include <QRegularExpressionMatch>
#include <QVector>
#include <QDebug>
#include <functional>
#include <QJsonObject>
#include <QJsonDocument>
#include <sstream>
#include <string>
#include "utils.h"
/**
* C++ implementation of Ben Almans linkify
* @brief Utils::emojiFy
* @param text
* @return
*/
QString Utils::removeUtf8Emojis( const QString &pText )
{
static const QRegularExpression nonAlphaNumeric{ QStringLiteral( "[\\p{L}\\p{M}\\p{N}\\p{Z}\\p{Sm}\\p{Sc}\\p{Sk}\\p{P}\\p{Cc}\\p{Cf}*]" ), QRegularExpression::UseUnicodePropertiesOption };
QRegularExpressionMatchIterator iter = nonAlphaNumeric.globalMatch( pText );
QString retString;
retString.reserve( 150 );
for ( ; iter.hasNext(); ) {
QRegularExpressionMatch match = iter.next();
retString += match.captured();
}
return retString;
}
QString Utils::linkiFy( const QString &pText )
{
static const QString scheme{ QStringLiteral( "[a-z\\d.-]+://" ) };
static const QString ipv4{ QStringLiteral( "(?:(?:[0-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.){3}(?:[0-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])" ) };
static const QString hostname{ QStringLiteral( "(?:(?:[^\\s!@#$%^&*()_=+[\\]{}\\\\|;:'\",.<>/?]+)\\.)+" ) };
static const QString tld{ QStringLiteral( "(?:ac|ad|aero|ae|af|ag|ai|al|am|an|ao|aq|arpa|ar|asia|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|biz|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|cat|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|coop|com|co|cr|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|edu|ee|eg|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gov|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|info|int|in|io|iq|ir|is|it|je|jm|jobs|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mil|mk|ml|mm|mn|mobi|mo|mp|mq|mr|ms|mt|museum|mu|mv|mw|mx|my|mz|name|na|nc|net|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|org|pa|pe|pf|pg|ph|pk|pl|pm|pn|pro|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|su|sv|sy|sz|tc|td|tel|tf|tg|th|tj|tk|tl|tm|tn|to|tp|travel|tr|tt|tv|tw|tz|ua|ug|uk|um|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|xn--0zwm56d|xn--11b5bs3a9aj6g|xn--80akhbyknj4f|xn--9t4b11yi5a|xn--deba0ad|xn--g6w251d|xn--hgbk6aj7f53bba|xn--hlcj6aya9esc7a|xn--jxalpdlp|xn--kgbechtv|xn--zckzah|ye|yt|yu|za|zm|zw)" ) };
static const QString host_or_ip{ QStringLiteral( "(?:" ) + hostname + tld + '|' + ipv4 + ')' };
static const QString path{ QStringLiteral( "(?:[;/][^#?<>\\s]*)?" ) };
static const QString query_frag{ QStringLiteral( "(?:\\?[^#<>\\s]*)?(?:#[^<>\\s]*)?" ) };
static const QString uri1{ QStringLiteral( "\\b" ) + scheme + QStringLiteral( "[^<>\\s]+" ) };
static const QString uri2{ QStringLiteral( "\\b" ) + host_or_ip + path + query_frag + QStringLiteral( "(?!\\w)" ) };
static const QString skip {QStringLiteral( "(?!file)" ) };
static const QString mailto{ QStringLiteral( "mailto" ) };
static const QString email{ QStringLiteral( "(?:" ) + mailto + QStringLiteral( ")?[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@" ) + host_or_ip + query_frag + QStringLiteral( "(?!\\w)" ) };
static const QRegularExpression uri_re{ skip + QStringLiteral( "(?:" ) + uri1 + '|' + uri2 + '|' + email + ')', QRegularExpression::CaseInsensitiveOption };
static const QRegularExpression scheme_re{ '^' + scheme, QRegularExpression::CaseInsensitiveOption};
static const QRegularExpression punctRegexp{ QStringLiteral( "(?:[!?.,:;'\"]|(?:&|&)(?:lt|gt|quot|apos|raquo|laquo|rsaquo|lsaquo);)$" ) };
static const QRegularExpression illegalEnding{ QStringLiteral( "([:/])w+$" ) };
static QHash<QString, QString> quotes;
quotes["'"] = "`";
quotes[">"] = "<";
quotes[")"] = "(";
quotes["]"] = "[";
quotes["}"] = "{";
quotes["»"] = "«";
quotes["›"] = "‹";
std::function<QString( QString text )> parse = [&]( QString pText ) {
QString html;
html.reserve( 300 );
QVector<QString> parts;
QString link_last;
link_last.reserve( 100 );
int idx = 0;
int idx_last = 0;
int idx_prev = 0;
QRegularExpressionMatchIterator iter = uri_re.globalMatch( pText );
for ( int i = 0; iter.hasNext(); i++ ) {
QRegularExpressionMatch match = iter.next();
QString link = match.captured();
if ( illegalEnding.match( link ).hasMatch() ) {
continue;
}
idx_last = match.capturedEnd();
idx = idx_last - link.length();
do {
link_last = link;
QString quote_end = link.right( 1 );
QString quote_begin = quotes[quote_end];
if ( quote_begin.length() ) {
QRegularExpression begin( QStringLiteral( "\\" ) + quote_begin + QStringLiteral( "(?!$)" ) );
QRegularExpression end( QStringLiteral( "\\" ) + quote_end );
QRegularExpressionMatch matches_begin = begin.match( link );
QRegularExpressionMatch matches_end = end.match( link );
if ( ( matches_begin.hasMatch() ? matches_begin.lastCapturedIndex() : 0 ) < ( matches_end.hasMatch() ? matches_end.lastCapturedIndex() : 0 ) ) {
link = link.left( link.length() - 1 );
}
}
} while ( link.length() && link != link_last );
QString href = link;
if ( !scheme_re.match( href ).hasMatch() ) {
if ( href.indexOf( '@' ) != -1 ) {
if ( !href.indexOf( mailto ) ) {
} else {
href = mailto + href;
}
} else if ( !href.indexOf( QStringLiteral( "irc." ) ) ) {
href = QStringLiteral( "irc://" ) + href;
} else if ( !href.indexOf( QStringLiteral( "ftp." ) ) ) {
href = QStringLiteral( "ftp://" ) + href;
} else {
href = QStringLiteral( "http://" ) + href;
}
}
if ( idx_prev != idx ) {
parts.append( pText.mid( idx_prev, idx - idx_prev ) );
}
idx_prev = idx_last;
parts.append( QStringLiteral( "<a href='" ) + href + QStringLiteral( "'>" ) + link + QStringLiteral( "</a>" ) );
}
parts.append( pText.right( pText.length() - idx_last ) );
for ( const auto part : parts ) {
html += part;
}
return html;
};
QString html = parse( pText );
return html;
}
double Utils::getMessageTimestamp( const QJsonObject &pMessage )
{
double seconds = 0;
if ( Q_LIKELY( pMessage.contains( QStringLiteral( "ts" ) ) ) ) {
QJsonObject ts = pMessage[QStringLiteral( "ts" )].toObject();
if ( Q_LIKELY( ts.contains( QStringLiteral( "$date" ) ) ) ) {
seconds = ts[QStringLiteral( "$date" )].toDouble();
}
}
return seconds;
}
bool Utils::messageHasTimestamp( const QJsonObject &pMessage )
{
if ( Q_LIKELY( pMessage.contains( QStringLiteral( "ts" ) ) ) ) {
QJsonObject ts = pMessage[QStringLiteral( "ts" )].toObject();
return ts.contains( QStringLiteral( "$date" ) );
}
return false;
}
QString Utils::getPathPrefix()
{
#if defined(Q_OS_WINDOWS)||defined(Q_OS_WINRT)
return QStringLiteral( "file:///" );
#else
return QStringLiteral( "file://" );
#endif
}
QString Utils::escapeUnicodeEmoji( const QString &pString )
{
qDebug() << pString;
static const QRegularExpression reg{ QStringLiteral( "(\\b[A-Fa-f0-9]{2,6}\\b)" ) };
QRegularExpressionMatchIterator iter = reg.globalMatch( pString );
QString retString;
if ( pString.contains( "-" ) ) {
QStringList parts = pString.split( "-" );
for ( const auto &item : parts ) {
int part;
std::stringstream ss;
ss << std::hex << item.toStdString();
ss >> part;
if ( part >= 0x10000 && part <= 0x10FFFF ) {
int hi = ( ( part - 0x10000 ) / 0x400 ) + 0xD800;
int lo = ( ( part - 0x10000 ) % 0x400 ) + 0xDC00;
retString += QChar( hi );
retString += QChar( lo );
} else {
retString = QChar( part );
}
}
} else {
int part;
std::stringstream ss;
ss << std::hex << pString.toStdString();
ss >> part;
if ( part >= 0x10000 && part <= 0x10FFFF ) {
int hi = ( ( part - 0x10000 ) / 0x400 ) + 0xD800;
int lo = ( ( part - 0x10000 ) % 0x400 ) + 0xDC00;
retString += QChar( hi );
retString += QChar( lo );
} else {
retString = QChar( part );
}
}
return retString;
}
QString Utils::replaceUnicodeEmojis( const QString &pText, EmojiRepo *pEmojiRepo )
{
QString returnText = pText;
if ( pEmojiRepo != nullptr ) {
auto items = pEmojiRepo->getElements();
for ( const auto &emoji : items ) {
if ( Q_LIKELY( !emoji.isNull() ) ) {
QString unicode = emoji->getUnicodeChar();
if ( Q_LIKELY( unicode.size() ) ) {
returnText.replace( unicode, emoji->getIdentifier() );
}
}
}
}
return returnText;
}
bool Utils::compareMessageTimestamps( const QJsonObject &pMessage1, const QJsonObject &pMessage2 )
{
return ( getMessageTimestamp( pMessage1 ) - getMessageTimestamp( pMessage2 ) ) > 0;
}
QString Utils::emojiFy( const QString &pMessage, EmojiRepo *pEmojiRepo )
{
QString returnText = pMessage;
static const QRegularExpression reg{ QStringLiteral( ":[a-zA-Z-_0-9ßöüäÖÜÄ]+:" ) };
if ( pEmojiRepo != nullptr ) {
QRegularExpressionMatchIterator iter = reg.globalMatch( pMessage );
QVector<QString> parts;
parts.reserve( 150 );
int idx = 0;
int idx_last = 0;
int idx_prev = 0;
while ( iter.hasNext() ) {
QRegularExpressionMatch match = iter.next();
QString matchString = match.captured();
if ( pEmojiRepo->contains( matchString ) ) {
idx_last = match.capturedEnd();
idx = idx_last - matchString.length();
// QString before = pMessage.left( match.capturedStart() );
// QString after = pMessage.right( pMessage.length() - match.capturedEnd() );
if ( idx_prev != idx ) {
parts.append( pMessage.mid( idx_prev, idx - idx_prev ) );
}
idx_prev = idx_last;
parts.append( pEmojiRepo->get( matchString )->getHtml() );
}
}
if ( parts.length() ) {
parts.append( pMessage.right( pMessage.length() - idx_last ) );
returnText = "";
for ( const auto &part : parts ) {
returnText += part;
}
}
}
return returnText;
}
qint16 Utils::hash( const QStringList &pStrings )
{
QByteArray stringSum;
stringSum.reserve( 30 );
for ( const auto &string : pStrings ) {
QByteArray stringArray( string.toUtf8() );
int limit = std::max( stringArray.length(), stringSum.length() );
if ( stringSum.isEmpty() ) {
stringSum = stringArray;
} else {
for ( int i = 0; i < limit; i++ ) {
int num1 = 0;
if ( i < stringArray.length() ) {
num1 = stringArray[i];
}
if ( i < stringSum.length() ) {
stringSum[i] = stringSum[i] + num1;
} else {
stringSum.append( num1 );
}
}
}
}
return qChecksum( stringSum, stringSum.length() );
}
QString Utils::getRandomString( int pLength )
{
static const QString BASE64_CHARS{ QStringLiteral( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_" )};
QString randString;
randString.reserve( pLength );
for ( int i = 0; i < pLength; i++ ) {
int index = std::rand() % BASE64_CHARS.size();
randString += BASE64_CHARS.at( index );
}
return randString;
}