diff --git a/qml/harbour-fernschreiber.qml b/qml/harbour-fernschreiber.qml index db0608c..969189c 100644 --- a/qml/harbour-fernschreiber.qml +++ b/qml/harbour-fernschreiber.qml @@ -45,6 +45,9 @@ ApplicationWindow onOpenFileExternally: { Qt.openUrlExternally(filePath); } + onTgUrlFound: { + Functions.handleLink(tgUrl); + } } AppNotification { diff --git a/qml/js/functions.js b/qml/js/functions.js index 24f7592..dd6093d 100644 --- a/qml/js/functions.js +++ b/qml/js/functions.js @@ -387,8 +387,8 @@ function handleTMeLink(link, usedPrefix) { // Do the necessary stuff to open the chat if successful // Fail with nice error message if it doesn't work } else if (link.indexOf("/+") !== -1) { - // Can't handle t.me/+... links, let the user choose the browser instead - Qt.openUrlExternally(link); + // Can't handle t.me/+... links directly, try to parse the Telegram page... + tdLibWrapper.getPageSource(link); } else { Debug.log("Search public chat: ", link.substring(usedPrefix.length)); tdLibWrapper.searchPublicChat(link.substring(usedPrefix.length), true); diff --git a/src/tdlibwrapper.cpp b/src/tdlibwrapper.cpp index b36599c..a00ed7b 100644 --- a/src/tdlibwrapper.cpp +++ b/src/tdlibwrapper.cpp @@ -53,7 +53,7 @@ namespace { const QString CHAT_LIST_MAIN("chatListMain"); } -TDLibWrapper::TDLibWrapper(AppSettings *appSettings, MceInterface *mceInterface, QObject *parent) : QObject(parent), joinChatRequested(false) +TDLibWrapper::TDLibWrapper(AppSettings *appSettings, MceInterface *mceInterface, QObject *parent) : QObject(parent), manager(new QNetworkAccessManager(this)), joinChatRequested(false) { LOG("Initializing TD Lib..."); this->appSettings = appSettings; @@ -1421,6 +1421,21 @@ void TDLibWrapper::getMessageAvailableReactions(qlonglong chatId, qlonglong mess this->sendRequest(requestObject); } +void TDLibWrapper::getPageSource(const QString &address) +{ + QUrl url = QUrl(address); + QNetworkRequest request(url); + request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + request.setHeader(QNetworkRequest::UserAgentHeader, "Fernschreiber Bot (Sailfish OS)"); + request.setRawHeader(QByteArray("Accept"), QByteArray("text/html,application/xhtml+xml")); + request.setRawHeader(QByteArray("Accept-Charset"), QByteArray("utf-8")); + request.setRawHeader(QByteArray("Connection"), QByteArray("close")); + request.setRawHeader(QByteArray("Cache-Control"), QByteArray("max-age=0")); + QNetworkReply *reply = manager->get(request); + + connect(reply, SIGNAL(finished()), this, SLOT(handleGetPageSourceFinished())); +} + void TDLibWrapper::searchEmoji(const QString &queryString) { LOG("Searching emoji" << queryString); @@ -1910,6 +1925,56 @@ void TDLibWrapper::handleSponsoredMessage(qlonglong chatId, const QVariantMap &m } } +void TDLibWrapper::handleGetPageSourceFinished() +{ + LOG("TDLibWrapper::handleGetPageSourceFinished"); + QNetworkReply *reply = qobject_cast(sender()); + reply->deleteLater(); + if (reply->error() != QNetworkReply::NoError) { + return; + } + + QString requestAddress = reply->request().url().toString(); + + QVariant contentTypeHeader = reply->header(QNetworkRequest::ContentTypeHeader); + if (!contentTypeHeader.isValid()) { + return; + } + LOG("Page source content type header: " + contentTypeHeader.toString()); + if (contentTypeHeader.toString().indexOf("text/html", 0, Qt::CaseInsensitive) == -1) { + LOG(requestAddress + " is not HTML, not searching for TG URL..."); + return; + } + + QString charset = "UTF-8"; + QRegularExpression charsetRegularExpression("charset\\s*\\=[\\s\\\"\\\']*([^\\s\\\"\\\'\\,>]*)"); + QRegularExpressionMatchIterator matchIterator = charsetRegularExpression.globalMatch(contentTypeHeader.toString()); + QStringList availableCharsets; + while (matchIterator.hasNext()) { + QRegularExpressionMatch nextMatch = matchIterator.next(); + QString currentCharset = nextMatch.captured(1).toUpper(); + LOG("Available page source charset: " << currentCharset); + availableCharsets.append(currentCharset); + } + if (availableCharsets.size() > 0 && !availableCharsets.contains("UTF-8")) { + // If we haven't received the requested UTF-8, we simply use the last one which we received in the header + charset = availableCharsets.last(); + } + LOG("Charset for " << requestAddress << ": " << charset); + + QByteArray rawDocument = reply->readAll(); + QTextCodec *codec = QTextCodec::codecForName(charset.toUtf8()); + if (codec == nullptr){ + return; + } + QString resultDocument = codec->toUnicode(rawDocument); + QRegExp urlRegex("href\\=\"(tg\\:[^\"]+)\\\""); + if (urlRegex.indexIn(resultDocument) != -1) { + LOG("TG URL found: " + urlRegex.cap(1)); + emit tgUrlFound(urlRegex.cap(1)); + } +} + void TDLibWrapper::setInitialParameters() { LOG("Sending initial parameters to TD Lib"); diff --git a/src/tdlibwrapper.h b/src/tdlibwrapper.h index 1df9c60..76924e8 100644 --- a/src/tdlibwrapper.h +++ b/src/tdlibwrapper.h @@ -20,6 +20,10 @@ #define TDLIBWRAPPER_H #include +#include +#include +#include +#include #include #include "tdlibreceiver.h" #include "dbusadaptor.h" @@ -232,6 +236,7 @@ public: Q_INVOKABLE void getActiveSessions(); Q_INVOKABLE void terminateSession(const QString &sessionId); Q_INVOKABLE void getMessageAvailableReactions(qlonglong chatId, qlonglong messageId); + Q_INVOKABLE void getPageSource(const QString &address); // Others (candidates for extraction ;)) Q_INVOKABLE void searchEmoji(const QString &queryString); @@ -315,6 +320,7 @@ signals: void sessionsReceived(const QVariantList &sessions); void openFileExternally(const QString &filePath); void availableReactionsReceived(qlonglong messageId, const QStringList &reactions); + void tgUrlFound(const QString &tgUrl); public slots: void handleVersionDetected(const QString &version); @@ -343,6 +349,8 @@ public slots: void handleUpdatedUserPrivacySettingRules(const QVariantMap &updatedRules); void handleSponsoredMessage(qlonglong chatId, const QVariantMap &message); + void handleGetPageSourceFinished(); + private: void setOption(const QString &name, const QString &type, const QVariant &value); void setInitialParameters(); @@ -353,6 +361,7 @@ private: private: void *tdLibClient; + QNetworkAccessManager *manager; AppSettings *appSettings; MceInterface *mceInterface; TDLibReceiver *tdLibReceiver;