diff --git a/Telegram/SourceFiles/core/application.cpp b/Telegram/SourceFiles/core/application.cpp index 76900ac133..66d5d9f979 100644 --- a/Telegram/SourceFiles/core/application.cpp +++ b/Telegram/SourceFiles/core/application.cpp @@ -693,33 +693,26 @@ bool Application::eventFilter(QObject *object, QEvent *e) { case QEvent::FileOpen: { if (object == QCoreApplication::instance()) { - const auto event = static_cast(e); - const auto flushQueued = [=] { - if (_filesToOpen.isEmpty()) { - InvokeQueued(this, [=] { - cSetSendPaths(_filesToOpen); - _filesToOpen.clear(); - checkSendPaths(); - }); - } - }; - if (const auto file = event->file(); !file.isEmpty()) { - flushQueued(); - _filesToOpen.append(file); - } else if (event->url().scheme() == u"tg"_q - || event->url().scheme() == u"tonsite"_q) { - const auto url = QString::fromUtf8( - event->url().toEncoded().trimmed()); - cSetStartUrl(url.mid(0, 8192)); - checkStartUrl(); - if (_lastActivePrimaryWindow - && StartUrlRequiresActivate(url)) { - _lastActivePrimaryWindow->activate(); - } - } else if (event->url().scheme() == u"interpret"_q) { - flushQueued(); - _filesToOpen.append(event->url().toString()); + if (_urlsToOpen.isEmpty()) { + InvokeQueued(this, [=] { + const auto activateRequired = ranges::any_of( + ranges::views::all( + _urlsToOpen + ) | ranges::views::transform([](const QUrl &url) { + return url.toString(); + }), + StartUrlRequiresActivate); + cRefStartUrls() << base::take(_urlsToOpen); + checkStartUrls(); + if (_lastActivePrimaryWindow && activateRequired) { + _lastActivePrimaryWindow->activate(); + } + }); } + const auto event = static_cast(e); + _urlsToOpen << event->url().toString(QUrl::FullyEncoded).mid( + 0, + 8192); } } break; @@ -1091,31 +1084,27 @@ bool Application::canApplyLangPackWithoutRestart() const { return true; } -void Application::checkSendPaths() { - if (!cSendPaths().isEmpty() +void Application::checkStartUrls() { + if (!Core::App().passcodeLocked()) { + cRefStartUrls() = ranges::views::all( + cRefStartUrls() + ) | ranges::views::filter([&](const QUrl &url) { + if (url.scheme() == u"tonsite"_q) { + iv().showTonSite(url.toString(), {}); + return false; + } else if (_lastActivePrimaryWindow) { + return !openLocalUrl(url.toString(), {}); + } + return true; + }) | ranges::to>; + } + if (!cRefStartUrls().isEmpty() && _lastActivePrimaryWindow && !_lastActivePrimaryWindow->locked()) { _lastActivePrimaryWindow->widget()->sendPaths(); } } -void Application::checkStartUrl() { - if (!cStartUrl().isEmpty()) { - const auto url = cStartUrl(); - if (!Core::App().passcodeLocked()) { - if (url.startsWith("tonsite://", Qt::CaseInsensitive)) { - cSetStartUrl(QString()); - iv().showTonSite(url, {}); - } else if (_lastActivePrimaryWindow) { - cSetStartUrl(QString()); - if (!openLocalUrl(url, {})) { - cSetStartUrl(url); - } - } - } - } -} - bool Application::openLocalUrl(const QString &url, QVariant context) { return openCustomUrl("tg://", LocalUrlHandlers(), url, context); } diff --git a/Telegram/SourceFiles/core/application.h b/Telegram/SourceFiles/core/application.h index 4cfa3ef176..fd415f07c8 100644 --- a/Telegram/SourceFiles/core/application.h +++ b/Telegram/SourceFiles/core/application.h @@ -270,8 +270,7 @@ public: } // Internal links. - void checkStartUrl(); - void checkSendPaths(); + void checkStartUrls(); bool openLocalUrl(const QString &url, QVariant context); bool openInternalUrl(const QString &url, QVariant context); [[nodiscard]] QString changelogLink() const; @@ -450,7 +449,7 @@ private: crl::time _shouldLockAt = 0; base::Timer _autoLockTimer; - QStringList _filesToOpen; + QList _urlsToOpen; std::optional _saveSettingsTimer; diff --git a/Telegram/SourceFiles/core/launcher.cpp b/Telegram/SourceFiles/core/launcher.cpp index c0627ed24a..a5e98dc11e 100644 --- a/Telegram/SourceFiles/core/launcher.cpp +++ b/Telegram/SourceFiles/core/launcher.cpp @@ -536,9 +536,8 @@ void Launcher::processArguments() { { "-tosettings" , KeyFormat::NoValues }, { "-startintray" , KeyFormat::NoValues }, { "-quit" , KeyFormat::NoValues }, - { "-sendpath" , KeyFormat::AllLeftValues }, { "-workdir" , KeyFormat::OneValue }, - { "--" , KeyFormat::OneValue }, + { "--" , KeyFormat::AllLeftValues }, { "-scale" , KeyFormat::OneValue }, }; auto parseResult = QMap(); @@ -585,19 +584,15 @@ void Launcher::processArguments() { gStartToSettings = parseResult.contains("-tosettings"); gStartInTray = parseResult.contains("-startintray"); gQuit = parseResult.contains("-quit"); - gSendPaths = parseResult.value("-sendpath", {}); _customWorkingDir = parseResult.value("-workdir", {}).join(QString()); if (!_customWorkingDir.isEmpty()) { _customWorkingDir = QDir(_customWorkingDir).absolutePath() + '/'; } - auto startUrls = parseResult.value("--", {}); - startUrls = ranges::views::filter(startUrls, [](const QString &url) { - return QUrl::fromUserInput(url).isValid(); - }) | ranges::to; - if (!startUrls.isEmpty()) { - gStartUrl = startUrls[0]; - } + const auto startUrls = parseResult.value("--", {}); + gStartUrls = startUrls | ranges::views::transform([&](const QString &url) { + return QUrl::fromUserInput(url, _initialWorkingDir); + }) | ranges::views::filter(&QUrl::isValid) | ranges::to>; const auto scaleKey = parseResult.value("-scale", {}); if (scaleKey.size() > 0) { diff --git a/Telegram/SourceFiles/core/sandbox.cpp b/Telegram/SourceFiles/core/sandbox.cpp index 41888829f7..b7ac2a5d10 100644 --- a/Telegram/SourceFiles/core/sandbox.cpp +++ b/Telegram/SourceFiles/core/sandbox.cpp @@ -35,47 +35,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include namespace Core { -namespace { - -QChar _toHex(ushort v) { - v = v & 0x000F; - return QChar::fromLatin1((v >= 10) ? ('a' + (v - 10)) : ('0' + v)); -} -ushort _fromHex(QChar c) { - return ((c.unicode() >= uchar('a')) ? (c.unicode() - uchar('a') + 10) : (c.unicode() - uchar('0'))) & 0x000F; -} - -QString _escapeTo7bit(const QString &str) { - QString result; - result.reserve(str.size() * 2); - for (int i = 0, l = str.size(); i != l; ++i) { - QChar ch(str.at(i)); - ushort uch(ch.unicode()); - if (uch < 32 || uch > 127 || uch == ushort(uchar('%'))) { - result.append('%').append(_toHex(uch >> 12)).append(_toHex(uch >> 8)).append(_toHex(uch >> 4)).append(_toHex(uch)); - } else { - result.append(ch); - } - } - return result; -} - -QString _escapeFrom7bit(const QString &str) { - QString result; - result.reserve(str.size()); - for (int i = 0, l = str.size(); i != l; ++i) { - QChar ch(str.at(i)); - if (ch == QChar::fromLatin1('%') && i + 4 < l) { - result.append(QChar(ushort((_fromHex(str.at(i + 1)) << 12) | (_fromHex(str.at(i + 2)) << 8) | (_fromHex(str.at(i + 3)) << 4) | _fromHex(str.at(i + 4))))); - i += 4; - } else { - result.append(ch); - } - } - return result; -} - -} // namespace bool Sandbox::QuitOnStartRequested = false; @@ -277,18 +236,15 @@ void Sandbox::socketConnected() { _secondInstance = true; QString commands; - const QStringList &lst(cSendPaths()); - for (QStringList::const_iterator i = lst.cbegin(), e = lst.cend(); i != e; ++i) { - commands += u"SEND:"_q + _escapeTo7bit(*i) + ';'; - } if (qEnvironmentVariableIsSet("XDG_ACTIVATION_TOKEN")) { - commands += u"XDG_ACTIVATION_TOKEN:"_q + _escapeTo7bit(qEnvironmentVariable("XDG_ACTIVATION_TOKEN")) + ';'; + commands += u"XDG_ACTIVATION_TOKEN:"_q + qgetenv("XDG_ACTIVATION_TOKEN").toBase64() + ';'; } - if (!cStartUrl().isEmpty()) { - commands += u"OPEN:"_q + _escapeTo7bit(cStartUrl()) + ';'; - } else if (cQuit()) { + for (const auto &url : cRefStartUrls()) { + commands += u"OPEN:"_q + url.toString(QUrl::FullyEncoded) + ';'; + } + if (cQuit()) { commands += u"CMD:quit;"_q; - } else { + } else if (cRefStartUrls().isEmpty()) { commands += u"CMD:show;"_q; } @@ -434,11 +390,11 @@ void Sandbox::newInstanceConnected() { void Sandbox::readClients() { // This method can be called before Application is constructed. - QString startUrl; - QStringList toSend; + QList startUrls; for (LocalClients::iterator i = _localClients.begin(), e = _localClients.end(); i != e; ++i) { i->second.append(i->first->readAll()); if (i->second.size()) { + bool activationRequired = false; QString cmds(QString::fromLatin1(i->second)); int32 from = 0, l = cmds.length(); for (int32 to = cmds.indexOf(QChar(';'), from); to >= from; to = (from < l) ? cmds.indexOf(QChar(';'), from) : -1) { @@ -448,21 +404,13 @@ void Sandbox::readClients() { const auto windowId = execExternal(cmds.mid(from + 4, to - from - 4)); const auto response = u"RES:%1_%2;"_q.arg(processId).arg(windowId).toLatin1(); i->first->write(response.data(), response.size()); - } else if (cmd.startsWith(u"SEND:"_q)) { - if (cSendPaths().isEmpty()) { - toSend.append(_escapeFrom7bit(cmds.mid(from + 5, to - from - 5))); - } } else if (cmd.startsWith(u"XDG_ACTIVATION_TOKEN:"_q)) { - qputenv("XDG_ACTIVATION_TOKEN", _escapeFrom7bit(cmds.mid(from + 21, to - from - 21)).toUtf8()); + qputenv("XDG_ACTIVATION_TOKEN", QByteArray::fromBase64(cmds.mid(from + 21, to - from - 21).toLatin1())); } else if (cmd.startsWith(u"OPEN:"_q)) { - startUrl = _escapeFrom7bit(cmds.mid(from + 5, to - from - 5)).mid(0, 8192); - const auto activationRequired = StartUrlRequiresActivate(startUrl); - const auto processId = QApplication::applicationPid(); - const auto windowId = activationRequired - ? execExternal("show") - : 0; - const auto response = u"RES:%1_%2;"_q.arg(processId).arg(windowId).toLatin1(); - i->first->write(response.data(), response.size()); + startUrls.append(cmds.mid(from + 5, to - from - 5).mid(0, 8192)); + if (!activationRequired) { + activationRequired = StartUrlRequiresActivate(startUrls.back().toString()); + } } else { LOG(("Sandbox Error: unknown command %1 passed in local socket").arg(cmd.toString())); } @@ -471,21 +419,17 @@ void Sandbox::readClients() { if (from > 0) { i->second = i->second.mid(from); } + const auto processId = QApplication::applicationPid(); + const auto windowId = activationRequired + ? execExternal("show") + : 0; + const auto response = u"RES:%1_%2;"_q.arg(processId).arg(windowId).toLatin1(); + i->first->write(response.data(), response.size()); } } - if (!toSend.isEmpty()) { - QStringList paths(cSendPaths()); - paths.append(toSend); - cSetSendPaths(paths); - } + cRefStartUrls() << base::take(startUrls); if (_application) { - _application->checkSendPaths(); - } - if (!startUrl.isEmpty()) { - cSetStartUrl(startUrl); - } - if (_application) { - _application->checkStartUrl(); + _application->checkStartUrls(); } } diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 42f3494180..f9ce2d25b9 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -2898,17 +2898,30 @@ bool MainWidget::contentOverlapped(const QRect &globalRect) { void MainWidget::activate() { if (_showAnimation) { return; - } else if (const auto paths = cSendPaths(); !paths.isEmpty()) { - const auto interpret = u"interpret://"_q; - cSetSendPaths(QStringList()); - if (paths[0].startsWith(interpret)) { - const auto error = Support::InterpretSendPath( - _controller, - paths[0].mid(interpret.size())); - if (!error.isEmpty()) { - _controller->show(Ui::MakeInformBox(error)); + } + const auto urls = base::take(cRefStartUrls()); + const auto interprets = urls | ranges::views::filter([](const QUrl &url) { + return url.scheme() == u"interpret"_q; + }) | ranges::views::transform([](const QUrl &url) { + return url.path(); + }) | ranges::to; + const auto paths = urls | ranges::views::filter( + &QUrl::isLocalFile + ) | ranges::views::transform( + &QUrl::toLocalFile + ) | ranges::to; + if (!interprets.isEmpty() || !paths.isEmpty()) { + if (!interprets.isEmpty()) { + for (const auto &interpret : interprets) { + const auto error = Support::InterpretSendPath( + _controller, + interpret); + if (!error.isEmpty()) { + _controller->show(Ui::MakeInformBox(error)); + } } - } else { + } + if (!paths.isEmpty()) { const auto chosen = [=](not_null thread) { return sendPaths(thread, paths); }; diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index 773c4fb3a6..597618cddd 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -215,7 +215,7 @@ void MainWindow::clearPasscodeLock() { _main->show(); updateControlsGeometry(); _main->showAnimated(std::move(oldContentCache), true); - Core::App().checkStartUrl(); + Core::App().checkStartUrls(); } } @@ -285,7 +285,7 @@ void MainWindow::setupMain( } else { _main->activate(); } - Core::App().checkStartUrl(); + Core::App().checkStartUrls(); } fixOrder(); if (const auto strong = weakAnimatedLayer.get()) { diff --git a/Telegram/SourceFiles/platform/win/specific_win.cpp b/Telegram/SourceFiles/platform/win/specific_win.cpp index 7ca0a68de9..c35389cf22 100644 --- a/Telegram/SourceFiles/platform/win/specific_win.cpp +++ b/Telegram/SourceFiles/platform/win/specific_win.cpp @@ -707,7 +707,7 @@ void psSendToMenu(bool send, bool silent) { send, silent, FOLDERID_SendTo, - L"-sendpath", + L"--", L"Telegram send to link.\n" "You can disable send to menu item in Telegram settings."); } diff --git a/Telegram/SourceFiles/settings.cpp b/Telegram/SourceFiles/settings.cpp index 4e2f6e878e..7b5e9ba868 100644 --- a/Telegram/SourceFiles/settings.cpp +++ b/Telegram/SourceFiles/settings.cpp @@ -20,8 +20,7 @@ bool gManyInstance = false; QString gKeyFile; QString gWorkingDir; -QStringList gSendPaths; -QString gStartUrl; +QList gStartUrls; QString gDialogLastPath, gDialogHelperPath; // optimize QFileDialog diff --git a/Telegram/SourceFiles/settings.h b/Telegram/SourceFiles/settings.h index c0d6f1cbbb..f39138e3a9 100644 --- a/Telegram/SourceFiles/settings.h +++ b/Telegram/SourceFiles/settings.h @@ -108,8 +108,7 @@ DeclareSetting(bool, PasswordRecovered); DeclareSetting(int32, PasscodeBadTries); DeclareSetting(crl::time, PasscodeLastTry); -DeclareSetting(QStringList, SendPaths); -DeclareSetting(QString, StartUrl); +DeclareRefSetting(QList, StartUrls); DeclareSetting(int, OtherOnline);