File indexing completed on 2024-05-12 16:25:04

0001 // SPDX-FileCopyrightText: Tobias Fella <tobias.fella@kde.org>
0002 // SPDX-License-Identifier: GPL-2.0-or-later
0003 
0004 #include "devicesmodel.h"
0005 
0006 #include "controller.h"
0007 
0008 #include <QDateTime>
0009 #include <QLocale>
0010 
0011 #include <KLocalizedString>
0012 
0013 #include <Quotient/csapi/device_management.h>
0014 #include <Quotient/connection.h>
0015 #include <Quotient/user.h>
0016 
0017 using namespace Quotient;
0018 
0019 DevicesModel::DevicesModel(QObject *parent)
0020     : QAbstractListModel(parent)
0021 {
0022 }
0023 
0024 void DevicesModel::fetchDevices()
0025 {
0026     if (Controller::instance().activeConnection()) {
0027         auto job = Controller::instance().activeConnection()->callApi<GetDevicesJob>();
0028         connect(job, &BaseJob::success, this, [this, job]() {
0029             beginResetModel();
0030             m_devices = job->devices();
0031             endResetModel();
0032             Q_EMIT countChanged();
0033         });
0034     }
0035 }
0036 
0037 QVariant DevicesModel::data(const QModelIndex &index, int role) const
0038 {
0039     if (index.row() < 0 || index.row() >= rowCount(QModelIndex())) {
0040         return {};
0041     }
0042 
0043     const auto &device = m_devices[index.row()];
0044 
0045     switch (role) {
0046     case Id:
0047         return device.deviceId;
0048     case DisplayName:
0049         return device.displayName;
0050     case LastIp:
0051         return device.lastSeenIp;
0052     case LastTimestamp:
0053         if (device.lastSeenTs) {
0054             return *device.lastSeenTs;
0055         } else {
0056             return false;
0057         }
0058     case TimestampString:
0059         if (device.lastSeenTs) {
0060             return QDateTime::fromMSecsSinceEpoch(*device.lastSeenTs).toString(QLocale().dateTimeFormat(QLocale::ShortFormat));
0061         } else {
0062             return false;
0063         }
0064     case Type:
0065         if (device.deviceId == m_connection->deviceId()) {
0066             return This;
0067         }
0068 #ifdef Quotient_E2EE_ENABLED
0069         if (!m_connection->isKnownE2eeCapableDevice(m_connection->userId(), device.deviceId)) {
0070             return Unencrypted;
0071         }
0072         if (m_connection->isVerifiedDevice(m_connection->userId(), device.deviceId)) {
0073             return Verified;
0074         } else {
0075             return Unverified;
0076         }
0077 #else
0078         return Unverified;
0079 #endif
0080     }
0081     return {};
0082 }
0083 
0084 int DevicesModel::rowCount(const QModelIndex &parent) const
0085 {
0086     Q_UNUSED(parent);
0087     return m_devices.size();
0088 }
0089 
0090 QHash<int, QByteArray> DevicesModel::roleNames() const
0091 {
0092     return {
0093         {Id, "id"},
0094         {DisplayName, "displayName"},
0095         {LastIp, "lastIp"},
0096         {LastTimestamp, "lastTimestamp"},
0097         {TimestampString, "timestamp"},
0098         {Type, "type"},
0099     };
0100 }
0101 
0102 void DevicesModel::logout(const QString &deviceId, const QString &password)
0103 {
0104     int index;
0105     for (index = 0; m_devices[index].deviceId != deviceId; index++)
0106         ;
0107 
0108     auto job = Controller::instance().activeConnection()->callApi<NeochatDeleteDeviceJob>(m_devices[index].deviceId);
0109 
0110     connect(job, &BaseJob::result, this, [this, job, password, index] {
0111         auto onSuccess = [this, index]() {
0112             beginRemoveRows(QModelIndex(), index, index);
0113             m_devices.remove(index);
0114             endRemoveRows();
0115             Q_EMIT countChanged();
0116         };
0117         if (job->error() != BaseJob::Success) {
0118             QJsonObject replyData = job->jsonData();
0119             QJsonObject authData;
0120             authData["session"] = replyData["session"];
0121             authData["password"] = password;
0122             authData["type"] = "m.login.password";
0123             QJsonObject identifier = {{"type", "m.id.user"}, {"user", Controller::instance().activeConnection()->user()->id()}};
0124             authData["identifier"] = identifier;
0125             auto *innerJob = Controller::instance().activeConnection()->callApi<NeochatDeleteDeviceJob>(m_devices[index].deviceId, authData);
0126             connect(innerJob, &BaseJob::success, this, onSuccess);
0127         } else {
0128             onSuccess();
0129         }
0130     });
0131 }
0132 
0133 void DevicesModel::setName(const QString &deviceId, const QString &name)
0134 {
0135     int index;
0136     for (index = 0; m_devices[index].deviceId != deviceId; index++);
0137 
0138     auto job = Controller::instance().activeConnection()->callApi<UpdateDeviceJob>(m_devices[index].deviceId, name);
0139     QString oldName = m_devices[index].displayName;
0140     beginResetModel();
0141     m_devices[index].displayName = name;
0142     endResetModel();
0143     connect(job, &BaseJob::failure, this, [this, index, oldName]() {
0144         beginResetModel();
0145         m_devices[index].displayName = oldName;
0146         endResetModel();
0147     });
0148 }
0149 
0150 Connection *DevicesModel::connection() const
0151 {
0152     return m_connection;
0153 }
0154 
0155 void DevicesModel::setConnection(Connection *connection)
0156 {
0157     if (m_connection) {
0158         disconnect(m_connection, nullptr, this, nullptr);
0159     }
0160     m_connection = connection;
0161     Q_EMIT connectionChanged();
0162     fetchDevices();
0163 
0164 #ifdef Quotient_E2EE_ENABLED
0165     connect(m_connection, &Connection::sessionVerified, this, [this](const QString &userId, const QString &deviceId) {
0166         Q_UNUSED(deviceId);
0167         if (userId == Controller::instance().activeConnection()->userId()) {
0168             fetchDevices();
0169         }
0170     });
0171     connect(m_connection, &Connection::finishedQueryingKeys, this, [this]() {
0172         fetchDevices();
0173     });
0174 #endif
0175 }
0176 
0177 #include "moc_devicesmodel.cpp"