From 82912f4a0b892988c83755bdd417e25025cb5647 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 27 Jun 2017 23:11:38 +0300 Subject: [PATCH] Handle socks internal links. --- Telegram/Resources/langs/lang.strings | 2 + Telegram/SourceFiles/application.cpp | 4 +- Telegram/SourceFiles/boxes/connection_box.cpp | 25 +++++ Telegram/SourceFiles/boxes/connection_box.h | 2 + Telegram/SourceFiles/boxes/share_box.cpp | 10 +- Telegram/SourceFiles/boxes/share_box.h | 4 +- .../SourceFiles/core/click_handler_types.cpp | 9 +- Telegram/SourceFiles/facades.cpp | 4 - Telegram/SourceFiles/facades.h | 1 - Telegram/SourceFiles/historywidget.cpp | 2 +- Telegram/SourceFiles/mainwidget.cpp | 72 +------------- Telegram/SourceFiles/mainwidget.h | 2 - Telegram/SourceFiles/mainwindow.cpp | 6 +- Telegram/SourceFiles/messenger.cpp | 97 +++++++++++++++++++ Telegram/SourceFiles/messenger.h | 3 + 15 files changed, 147 insertions(+), 96 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 401a884d42..3f5d6591fc 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -151,6 +151,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org "lng_sure_add_admin_unban" = "This user is currently restricted or banned in this group. Are you sure you want to unban and promote them?"; "lng_sure_ban_admin" = "This user is an admin in this group. Are you sure you want to go ahead and restrict them?"; "lng_sure_ban_user_group" = "Ban {user} in the group?"; +"lng_sure_enable_socks" = "Are you sure you want to enable this proxy?\n\nServer: {server}\nPort: {port}\n\nYou can change your proxy server later in the Settings (Connection Type)."; +"lng_sure_enable" = "Enable"; "lng_edit_deleted" = "This message was deleted"; "lng_edit_too_long" = "Your message text is too long"; diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp index 0f221becce..c728298638 100644 --- a/Telegram/SourceFiles/application.cpp +++ b/Telegram/SourceFiles/application.cpp @@ -286,9 +286,7 @@ void Application::readClients() { if (!startUrl.isEmpty()) { cSetStartUrl(startUrl); } - if (auto main = App::main()) { - main->checkStartUrl(); - } + Messenger::Instance().checkStartUrl(); } void Application::removeClients() { diff --git a/Telegram/SourceFiles/boxes/connection_box.cpp b/Telegram/SourceFiles/boxes/connection_box.cpp index fac3d96dce..3360fe3733 100644 --- a/Telegram/SourceFiles/boxes/connection_box.cpp +++ b/Telegram/SourceFiles/boxes/connection_box.cpp @@ -20,6 +20,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org */ #include "boxes/connection_box.h" +#include "boxes/confirm_box.h" #include "lang/lang_keys.h" #include "storage/localstorage.h" #include "mainwidget.h" @@ -30,6 +31,30 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "history/history_location_manager.h" #include "styles/style_boxes.h" +void ConnectionBox::ShowApplyProxyConfirmation(const QMap &fields) { + auto server = fields.value(qsl("server")); + auto port = fields.value(qsl("port")).toInt(); + if (!server.isEmpty() && port != 0) { + auto weakBox = std::make_shared>(nullptr); + auto box = Ui::show(Box(lng_sure_enable_socks(lt_server, server, lt_port, QString::number(port)), lang(lng_sure_enable), [fields, weakBox] { + auto p = ProxyData(); + p.host = fields.value(qsl("server")); + p.user = fields.value(qsl("user")); + p.password = fields.value(qsl("pass")); + p.port = fields.value(qsl("port")).toInt(); + Global::SetConnectionType(dbictTcpProxy); + Global::SetConnectionProxy(p); + Local::writeSettings(); + Global::RefConnectionTypeChanged().notify(); + MTP::restart(); + reinitLocationManager(); + reinitWebLoadManager(); + if (*weakBox) (*weakBox)->closeBox(); + }), KeepOtherLayers); + *weakBox = box; + } +} + ConnectionBox::ConnectionBox(QWidget *parent) : _hostInput(this, st::connectionHostInputField, langFactory(lng_connection_host_ph), Global::ConnectionProxy().host) , _portInput(this, st::connectionPortInputField, langFactory(lng_connection_port_ph), QString::number(Global::ConnectionProxy().port)) diff --git a/Telegram/SourceFiles/boxes/connection_box.h b/Telegram/SourceFiles/boxes/connection_box.h index d93fe460a9..db4d0a2211 100644 --- a/Telegram/SourceFiles/boxes/connection_box.h +++ b/Telegram/SourceFiles/boxes/connection_box.h @@ -39,6 +39,8 @@ class ConnectionBox : public BoxContent { public: ConnectionBox(QWidget *parent); + static void ShowApplyProxyConfirmation(const QMap &fields); + protected: void prepare() override; void setInnerFocus() override; diff --git a/Telegram/SourceFiles/boxes/share_box.cpp b/Telegram/SourceFiles/boxes/share_box.cpp index d41fcaec49..6de786b7fe 100644 --- a/Telegram/SourceFiles/boxes/share_box.cpp +++ b/Telegram/SourceFiles/boxes/share_box.cpp @@ -809,7 +809,7 @@ QVector ShareBox::Inner::selected() const { return result; } -QString appendShareGameScoreUrl(const QString &url, const FullMsgId &fullId) { +QString AppendShareGameScoreUrl(const QString &url, const FullMsgId &fullId) { auto shareHashData = QByteArray(0x10, Qt::Uninitialized); auto shareHashDataInts = reinterpret_cast(shareHashData.data()); auto channel = fullId.channel ? App::channelLoaded(fullId.channel) : static_cast(nullptr); @@ -854,7 +854,7 @@ QString appendShareGameScoreUrl(const QString &url, const FullMsgId &fullId) { namespace { -void shareGameScoreFromItem(HistoryItem *item) { +void ShareGameScoreFromItem(HistoryItem *item) { struct ShareGameScoreData { ShareGameScoreData(const FullMsgId &msgId) : msgId(msgId) { } @@ -949,7 +949,7 @@ void shareGameScoreFromItem(HistoryItem *item) { } // namespace -void shareGameScoreByHash(const QString &hash) { +void ShareGameScoreByHash(const QString &hash) { auto key128Size = 0x10; auto hashEncrypted = QByteArray::fromBase64(hash.toLatin1(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); @@ -1000,12 +1000,12 @@ void shareGameScoreByHash(const QString &hash) { } if (auto item = App::histItemById(channelId, msgId)) { - shareGameScoreFromItem(item); + ShareGameScoreFromItem(item); } else if (App::api()) { auto resolveMessageAndShareScore = [msgId](ChannelData *channel) { App::api()->requestMessageData(channel, msgId, [](ChannelData *channel, MsgId msgId) { if (auto item = App::histItemById(channel, msgId)) { - shareGameScoreFromItem(item); + ShareGameScoreFromItem(item); } else { Ui::show(Box(lang(lng_edit_deleted))); } diff --git a/Telegram/SourceFiles/boxes/share_box.h b/Telegram/SourceFiles/boxes/share_box.h index 00b3d586c1..b4861b279f 100644 --- a/Telegram/SourceFiles/boxes/share_box.h +++ b/Telegram/SourceFiles/boxes/share_box.h @@ -37,8 +37,8 @@ namespace Ui { class MultiSelect; } // namespace Ui -QString appendShareGameScoreUrl(const QString &url, const FullMsgId &fullId); -void shareGameScoreByHash(const QString &hash); +QString AppendShareGameScoreUrl(const QString &url, const FullMsgId &fullId); +void ShareGameScoreByHash(const QString &hash); class ShareBox : public BoxContent, public RPCSender { Q_OBJECT diff --git a/Telegram/SourceFiles/core/click_handler_types.cpp b/Telegram/SourceFiles/core/click_handler_types.cpp index d75389c9cf..5f599644ed 100644 --- a/Telegram/SourceFiles/core/click_handler_types.cpp +++ b/Telegram/SourceFiles/core/click_handler_types.cpp @@ -21,6 +21,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "core/click_handler_types.h" #include "lang/lang_keys.h" +#include "messenger.h" #include "platform/platform_specific.h" #include "boxes/confirm_box.h" #include "base/qthelp_regex.h" @@ -58,6 +59,8 @@ QString tryConvertUrlToLocal(QString url) { || previewedUrl.startsWith(qstr("https://"), Qt::CaseInsensitive)) { return previewedUrl; } + } else if (auto socksMatch = regex_match(qsl("socks/?\\?(.+)(#|$)"), query, matchOptions)) { + return qsl("tg://socks?") + socksMatch->captured(1); } else if (auto usernameMatch = regex_match(qsl("^([a-zA-Z0-9\\.\\_]+)(/?\\?|/?$|/(\\d+)/?(?:\\?|$))"), query, matchOptions)) { auto params = query.mid(usernameMatch->captured(0).size()).toString(); auto postParam = QString(); @@ -83,7 +86,7 @@ void UrlClickHandler::doOpen(QString url) { url = tryConvertUrlToLocal(url); if (url.startsWith(qstr("tg://"), Qt::CaseInsensitive)) { - App::openLocalUrl(url); + Messenger::Instance().openLocalUrl(url); } else { QDesktopServices::openUrl(url); } @@ -113,7 +116,7 @@ void HiddenUrlClickHandler::doOpen(QString url) { auto urlText = tryConvertUrlToLocal(url); if (urlText.startsWith(qstr("tg://"), Qt::CaseInsensitive)) { - App::openLocalUrl(urlText); + Messenger::Instance().openLocalUrl(urlText); } else { auto parsedUrl = QUrl::fromUserInput(urlText); auto displayUrl = parsedUrl.isValid() ? parsedUrl.toDisplayString() : urlText; @@ -128,7 +131,7 @@ void BotGameUrlClickHandler::onClick(Qt::MouseButton button) const { auto urlText = tryConvertUrlToLocal(url()); if (urlText.startsWith(qstr("tg://"), Qt::CaseInsensitive)) { - App::openLocalUrl(urlText); + Messenger::Instance().openLocalUrl(urlText); } else if (!_bot || _bot->isVerified() || Local::isBotTrusted(_bot)) { doOpen(urlText); } else { diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index ad5754db63..19122e3c64 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -167,10 +167,6 @@ void stickersBox(const QString &name) { if (MainWidget *m = main()) m->stickersBox(MTP_inputStickerSetShortName(MTP_string(name))); } -void openLocalUrl(const QString &url) { - if (MainWidget *m = main()) m->openLocalUrl(url); -} - bool forward(const PeerId &peer, ForwardWhatMessages what) { if (MainWidget *m = main()) return m->onForward(peer, what); return false; diff --git a/Telegram/SourceFiles/facades.h b/Telegram/SourceFiles/facades.h index 60d9be41e1..9002cfd044 100644 --- a/Telegram/SourceFiles/facades.h +++ b/Telegram/SourceFiles/facades.h @@ -73,7 +73,6 @@ void searchByHashtag(const QString &tag, PeerData *inPeer); void openPeerByName(const QString &username, MsgId msgId = ShowAtUnreadMsgId, const QString &startToken = QString()); void joinGroupByHash(const QString &hash); void stickersBox(const QString &name); -void openLocalUrl(const QString &url); bool forward(const PeerId &peer, ForwardWhatMessages what); void removeDialog(History *history); void showSettings(); diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 8868401f27..00d8a0c23e 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -3510,7 +3510,7 @@ void HistoryWidget::botCallbackDone(BotCallbackInfo info, const MTPmessages_BotC } else if (answerData.has_url()) { auto url = qs(answerData.vurl); if (info.game) { - url = appendShareGameScoreUrl(url, info.msgId); + url = AppendShareGameScoreUrl(url, info.msgId); BotGameUrlClickHandler(info.bot, url).onClick(Qt::LeftButton); if (item && (!item->history()->peer->isChannel() || item->history()->peer->isMegagroup())) { updateSendAction(item->history(), SendAction::Type::PlayGame); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 823686d4bd..d6b9b06013 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -51,8 +51,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "boxes/sticker_set_box.h" #include "boxes/contacts_box.h" #include "boxes/download_path_box.h" -#include "boxes/confirm_phone_box.h" -#include "boxes/share_box.h" #include "storage/localstorage.h" #include "shortcuts.h" #include "media/media_audio.h" @@ -4088,81 +4086,13 @@ void MainWidget::start(const MTPUser *self) { Local::readSavedGifs(); _history->start(); - checkStartUrl(); + Messenger::Instance().checkStartUrl(); } bool MainWidget::started() { return _started; } -void MainWidget::checkStartUrl() { - if (!cStartUrl().isEmpty() && App::self() && !App::passcoded()) { - auto url = cStartUrl(); - cSetStartUrl(QString()); - - openLocalUrl(url); - } -} - -void MainWidget::openLocalUrl(const QString &url) { - auto urlTrimmed = url.trimmed(); - if (urlTrimmed.size() > 8192) urlTrimmed = urlTrimmed.mid(0, 8192); - - if (!urlTrimmed.startsWith(qstr("tg://"), Qt::CaseInsensitive)) { - return; - } - auto command = urlTrimmed.midRef(qstr("tg://").size()); - - using namespace qthelp; - auto matchOptions = RegExOption::CaseInsensitive; - if (auto joinChatMatch = regex_match(qsl("^join/?\\?invite=([a-zA-Z0-9\\.\\_\\-]+)(&|$)"), command, matchOptions)) { - joinGroupByHash(joinChatMatch->captured(1)); - } else if (auto stickerSetMatch = regex_match(qsl("^addstickers/?\\?set=([a-zA-Z0-9\\.\\_]+)(&|$)"), command, matchOptions)) { - stickersBox(MTP_inputStickerSetShortName(MTP_string(stickerSetMatch->captured(1)))); - } else if (auto shareUrlMatch = regex_match(qsl("^msg_url/?\\?(.+)(#|$)"), command, matchOptions)) { - auto params = url_parse_params(shareUrlMatch->captured(1), UrlParamNameTransform::ToLower); - auto url = params.value(qsl("url")); - if (!url.isEmpty()) { - shareUrlLayer(url, params.value("text")); - } - } else if (auto confirmPhoneMatch = regex_match(qsl("^confirmphone/?\\?(.+)(#|$)"), command, matchOptions)) { - auto params = url_parse_params(confirmPhoneMatch->captured(1), UrlParamNameTransform::ToLower); - auto phone = params.value(qsl("phone")); - auto hash = params.value(qsl("hash")); - if (!phone.isEmpty() && !hash.isEmpty()) { - ConfirmPhoneBox::start(phone, hash); - } - } else if (auto usernameMatch = regex_match(qsl("^resolve/?\\?(.+)(#|$)"), command, matchOptions)) { - auto params = url_parse_params(usernameMatch->captured(1), UrlParamNameTransform::ToLower); - auto domain = params.value(qsl("domain")); - if (regex_match(qsl("^[a-zA-Z0-9\\.\\_]+$"), domain, matchOptions)) { - auto start = qsl("start"); - auto startToken = params.value(start); - if (startToken.isEmpty()) { - start = qsl("startgroup"); - startToken = params.value(start); - if (startToken.isEmpty()) { - start = QString(); - } - } - auto post = (start == qsl("startgroup")) ? ShowAtProfileMsgId : ShowAtUnreadMsgId; - auto postParam = params.value(qsl("post")); - if (auto postId = postParam.toInt()) { - post = postId; - } - auto gameParam = params.value(qsl("game")); - if (!gameParam.isEmpty() && regex_match(qsl("^[a-zA-Z0-9\\.\\_]+$"), gameParam, matchOptions)) { - startToken = gameParam; - post = ShowAtGameShareMsgId; - } - openPeerByName(domain, post, startToken); - } - } else if (auto shareGameScoreMatch = regex_match(qsl("^share_game_score/?\\?(.+)(#|$)"), command, matchOptions)) { - auto params = url_parse_params(shareGameScoreMatch->captured(1), UrlParamNameTransform::ToLower); - shareGameScoreByHash(params.value(qsl("hash"))); - } -} - void MainWidget::openPeerByName(const QString &username, MsgId msgId, const QString &startToken) { App::wnd()->hideMediaview(); diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index b5cfcbb927..bef68aa615 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -168,8 +168,6 @@ public: void start(const MTPUser *self = nullptr); - void checkStartUrl(); - void openLocalUrl(const QString &str); void openPeerByName(const QString &name, MsgId msgId = ShowAtUnreadMsgId, const QString &startToken = QString()); void joinGroupByHash(const QString &hash); void stickersBox(const MTPInputStickerSet &set); diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index 3f279e141e..1b4cd18f06 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -218,7 +218,7 @@ void MainWindow::clearPasscode() { } else { t_assert(_main != nullptr); _main->showAnimated(bg, true); - _main->checkStartUrl(); + Messenger::Instance().checkStartUrl(); } } @@ -609,9 +609,7 @@ bool MainWindow::eventFilter(QObject *object, QEvent *e) { QString url = static_cast(e)->url().toEncoded().trimmed(); if (url.startsWith(qstr("tg://"), Qt::CaseInsensitive)) { cSetStartUrl(url.mid(0, 8192)); - if (_main) { - _main->checkStartUrl(); - } + Messenger::Instance().checkStartUrl(); } activate(); } diff --git a/Telegram/SourceFiles/messenger.cpp b/Telegram/SourceFiles/messenger.cpp index 4a6e53abe7..173bdb3de5 100644 --- a/Telegram/SourceFiles/messenger.cpp +++ b/Telegram/SourceFiles/messenger.cpp @@ -45,6 +45,11 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "ui/widgets/tooltip.h" #include "storage/serialize_common.h" #include "window/window_controller.h" +#include "base/qthelp_regex.h" +#include "base/qthelp_url.h" +#include "boxes/connection_box.h" +#include "boxes/confirm_phone_box.h" +#include "boxes/share_box.h" namespace { @@ -651,6 +656,98 @@ QString Messenger::createInternalLinkFull(const QString &query) const { return Global::InternalLinksDomain() + query; } +void Messenger::checkStartUrl() { + if (!cStartUrl().isEmpty() && !App::passcoded()) { + auto url = cStartUrl(); + cSetStartUrl(QString()); + if (!openLocalUrl(url)) { + cSetStartUrl(url); + } + } +} + +bool Messenger::openLocalUrl(const QString &url) { + auto urlTrimmed = url.trimmed(); + if (urlTrimmed.size() > 8192) urlTrimmed = urlTrimmed.mid(0, 8192); + + if (!urlTrimmed.startsWith(qstr("tg://"), Qt::CaseInsensitive) || App::passcoded()) { + return false; + } + auto command = urlTrimmed.midRef(qstr("tg://").size()); + + using namespace qthelp; + auto matchOptions = RegExOption::CaseInsensitive; + if (auto joinChatMatch = regex_match(qsl("^join/?\\?invite=([a-zA-Z0-9\\.\\_\\-]+)(&|$)"), command, matchOptions)) { + if (auto main = App::main()) { + main->joinGroupByHash(joinChatMatch->captured(1)); + return true; + } + } else if (auto stickerSetMatch = regex_match(qsl("^addstickers/?\\?set=([a-zA-Z0-9\\.\\_]+)(&|$)"), command, matchOptions)) { + if (auto main = App::main()) { + main->stickersBox(MTP_inputStickerSetShortName(MTP_string(stickerSetMatch->captured(1)))); + return true; + } + } else if (auto shareUrlMatch = regex_match(qsl("^msg_url/?\\?(.+)(#|$)"), command, matchOptions)) { + if (auto main = App::main()) { + auto params = url_parse_params(shareUrlMatch->captured(1), UrlParamNameTransform::ToLower); + auto url = params.value(qsl("url")); + if (!url.isEmpty()) { + main->shareUrlLayer(url, params.value("text")); + return true; + } + } + } else if (auto confirmPhoneMatch = regex_match(qsl("^confirmphone/?\\?(.+)(#|$)"), command, matchOptions)) { + if (auto main = App::main()) { + auto params = url_parse_params(confirmPhoneMatch->captured(1), UrlParamNameTransform::ToLower); + auto phone = params.value(qsl("phone")); + auto hash = params.value(qsl("hash")); + if (!phone.isEmpty() && !hash.isEmpty()) { + ConfirmPhoneBox::start(phone, hash); + return true; + } + } + } else if (auto usernameMatch = regex_match(qsl("^resolve/?\\?(.+)(#|$)"), command, matchOptions)) { + if (auto main = App::main()) { + auto params = url_parse_params(usernameMatch->captured(1), UrlParamNameTransform::ToLower); + auto domain = params.value(qsl("domain")); + if (regex_match(qsl("^[a-zA-Z0-9\\.\\_]+$"), domain, matchOptions)) { + auto start = qsl("start"); + auto startToken = params.value(start); + if (startToken.isEmpty()) { + start = qsl("startgroup"); + startToken = params.value(start); + if (startToken.isEmpty()) { + start = QString(); + } + } + auto post = (start == qsl("startgroup")) ? ShowAtProfileMsgId : ShowAtUnreadMsgId; + auto postParam = params.value(qsl("post")); + if (auto postId = postParam.toInt()) { + post = postId; + } + auto gameParam = params.value(qsl("game")); + if (!gameParam.isEmpty() && regex_match(qsl("^[a-zA-Z0-9\\.\\_]+$"), gameParam, matchOptions)) { + startToken = gameParam; + post = ShowAtGameShareMsgId; + } + main->openPeerByName(domain, post, startToken); + return true; + } + } + } else if (auto shareGameScoreMatch = regex_match(qsl("^share_game_score/?\\?(.+)(#|$)"), command, matchOptions)) { + if (auto main = App::main()) { + auto params = url_parse_params(shareGameScoreMatch->captured(1), UrlParamNameTransform::ToLower); + ShareGameScoreByHash(params.value(qsl("hash"))); + return true; + } + } else if (auto socksMatch = regex_match(qsl("^socks/?\\?(.+)(#|$)"), command, matchOptions)) { + auto params = url_parse_params(socksMatch->captured(1), UrlParamNameTransform::ToLower); + ConnectionBox::ShowApplyProxyConfirmation(params); + return true; + } + return false; +} + FileUploader *Messenger::uploader() { if (!_uploader && !App::quitting()) _uploader = new FileUploader(); return _uploader; diff --git a/Telegram/SourceFiles/messenger.h b/Telegram/SourceFiles/messenger.h index f89cf105a5..501c512313 100644 --- a/Telegram/SourceFiles/messenger.h +++ b/Telegram/SourceFiles/messenger.h @@ -128,9 +128,12 @@ public: return *_audio; } + // Internal links. void setInternalLinkDomain(const QString &domain) const; QString createInternalLink(const QString &query) const; QString createInternalLinkFull(const QString &query) const; + void checkStartUrl(); + bool openLocalUrl(const QString &url); FileUploader *uploader(); void uploadProfilePhoto(const QImage &tosend, const PeerId &peerId);