File indexing completed on 2024-10-06 07:38:13

0001 // SPDX-FileCopyrightText: 2022 Gary Wang <wzc782970009@gmail.com>
0002 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0003 
0004 #include "networkcontroller.h"
0005 
0006 #include "abstractaccount.h"
0007 #include "accountmanager.h"
0008 #include "config.h"
0009 #include "tokodon_http_debug.h"
0010 
0011 using namespace Qt::Literals::StringLiterals;
0012 
0013 NetworkController::NetworkController(QObject *parent)
0014     : QObject(parent)
0015 {
0016     setApplicationProxy();
0017 
0018     connect(&AccountManager::instance(), &AccountManager::accountsReady, this, [=] {
0019         m_accountsReady = true;
0020         openLink();
0021 
0022         if (!m_storedComposedText.isEmpty()) {
0023             Q_EMIT openComposer(m_storedComposedText);
0024             m_storedComposedText.clear();
0025         }
0026     });
0027     m_accountsReady = AccountManager::instance().isReady();
0028 }
0029 
0030 NetworkController &NetworkController::instance()
0031 {
0032     static NetworkController _instance;
0033     return _instance;
0034 }
0035 
0036 void NetworkController::setApplicationProxy()
0037 {
0038     Config *cfg = Config::self();
0039     QNetworkProxy proxy;
0040 
0041     // type match to ProxyType from config.kcfg
0042     switch (cfg->proxyType()) {
0043     case 1: // HTTP
0044         proxy.setType(QNetworkProxy::HttpProxy);
0045         proxy.setHostName(cfg->proxyHost());
0046         proxy.setPort(cfg->proxyPort());
0047         proxy.setUser(cfg->proxyUser());
0048         proxy.setPassword(cfg->proxyPassword());
0049         break;
0050     case 2: // SOCKS 5
0051         proxy.setType(QNetworkProxy::Socks5Proxy);
0052         proxy.setHostName(cfg->proxyHost());
0053         proxy.setPort(cfg->proxyPort());
0054         proxy.setUser(cfg->proxyUser());
0055         proxy.setPassword(cfg->proxyPassword());
0056         break;
0057     case 0: // System Default
0058     default:
0059         // do nothing
0060         break;
0061     }
0062 
0063     QNetworkProxy::setApplicationProxy(proxy);
0064 
0065     AccountManager::instance().reloadAccounts();
0066 }
0067 
0068 void NetworkController::openWebApLink(QString input)
0069 {
0070     QUrl url(input);
0071     // only web+ap (declared in app manifest) and https (explicitly not declared, can be used from command line)
0072     if (url.scheme() != QStringLiteral("web+ap") && url.scheme() != QStringLiteral("https")) {
0073         // FIXME maybe warn about unsupported links?
0074         return;
0075     }
0076 
0077     m_requestedLink = std::move(url);
0078 
0079     if (m_accountsReady) {
0080         openLink();
0081     }
0082 }
0083 
0084 void NetworkController::setAuthCode(QUrl authCode)
0085 {
0086     QUrlQuery query(authCode);
0087 
0088     if (query.hasQueryItem(QStringLiteral("code"))) {
0089         Q_EMIT receivedAuthCode(query.queryItemValue(QStringLiteral("code")));
0090     }
0091 }
0092 
0093 void NetworkController::openLink()
0094 {
0095     if (m_requestedLink.isEmpty())
0096         return;
0097 
0098     auto account = AccountManager::instance().selectedAccount();
0099 
0100     if (m_requestedLink.scheme() == QStringLiteral("web+ap")) {
0101         if (m_requestedLink.userName() == QStringLiteral("tag")) {
0102             // TODO implement in a future MR
0103             m_requestedLink.clear();
0104             return;
0105         }
0106         m_requestedLink.setScheme(QStringLiteral("https"));
0107         m_requestedLink.setUserInfo(QString());
0108     }
0109 
0110     const QUrl instanceUrl(account->instanceUri());
0111 
0112     // TODO: this assumes the post id is in the last path segment
0113     // Is this always true? Maybe there's some way to query it.
0114     if (instanceUrl.host() == m_requestedLink.host()) {
0115         QString path = m_requestedLink.path();
0116         path.remove(0, path.lastIndexOf(QLatin1Char('/')) + 1);
0117 
0118         Q_EMIT NetworkController::instance().openPost(path);
0119         return;
0120     }
0121 
0122     requestRemoteObject(account, m_requestedLink.toString(), [=](QNetworkReply *reply) {
0123         const auto searchResult = QJsonDocument::fromJson(reply->readAll()).object();
0124 
0125         const auto statuses = searchResult[QStringLiteral("statuses")].toArray();
0126         const auto accounts = searchResult[QStringLiteral("accounts")].toArray();
0127 
0128         if (statuses.isEmpty()) {
0129             qCDebug(TOKODON_HTTP) << "Failed to find any statuses!";
0130         } else {
0131             const auto status = statuses[0].toObject();
0132 
0133             Q_EMIT NetworkController::instance().openPost(status["id"_L1].toString());
0134         }
0135 
0136         if (accounts.isEmpty()) {
0137             qCDebug(TOKODON_HTTP) << "Failed to find any accounts!";
0138         } else {
0139             const auto account = accounts[0].toObject();
0140 
0141             Q_EMIT NetworkController::instance().openAccount(account["id"_L1].toString());
0142         }
0143 
0144         m_requestedLink.clear();
0145     });
0146 }
0147 
0148 void NetworkController::startComposing(const QString &text)
0149 {
0150     if (m_accountsReady) {
0151         Q_EMIT openComposer(text);
0152     } else {
0153         m_storedComposedText = text;
0154     }
0155 }
0156 
0157 void NetworkController::requestRemoteObject(AbstractAccount *account, const QString &remoteUrl, std::function<void(QNetworkReply *)> callback)
0158 {
0159     auto url = account->apiUrl(QStringLiteral("/api/v2/search"));
0160     url.setQuery({
0161         {QStringLiteral("q"), remoteUrl},
0162         {QStringLiteral("resolve"), QStringLiteral("true")},
0163         {QStringLiteral("limit"), QStringLiteral("1")},
0164     });
0165     account->get(url, true, &AccountManager::instance(), std::move(callback));
0166 }
0167 
0168 bool NetworkController::pushNotificationsAvailable() const
0169 {
0170     return !endpoint.isEmpty();
0171 }
0172 
0173 #include "moc_networkcontroller.cpp"