File indexing completed on 2023-11-26 08:17:52

0001 /*
0002    SPDX-FileCopyrightText: 2017-2023 Laurent Montel <montel@kde.org>
0003 
0004    SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "rocketchataccount.h"
0008 #include "attachments/fileattachments.h"
0009 #include "authenticationmanager.h"
0010 #include "config-ruqola.h"
0011 #include "customemojiiconmanager.h"
0012 #include "downloadappslanguages/downloadappslanguagesmanager.h"
0013 #include "emoticons/emojimanager.h"
0014 #include "managerdatapaths.h"
0015 #include "messagequeue.h"
0016 #include "model/autotranslatelanguagesmodel.h"
0017 #include "model/commandsmodel.h"
0018 #include "model/commonmessagefilterproxymodel.h"
0019 #include "model/commonmessagesmodel.h"
0020 #include "model/discussionsfilterproxymodel.h"
0021 #include "model/discussionsmodel.h"
0022 #include "model/emoticonmodel.h"
0023 #include "model/filesforroomfilterproxymodel.h"
0024 #include "model/filesforroommodel.h"
0025 #include "model/listmessagesmodelfilterproxymodel.h"
0026 #include "model/loginmethodmodel.h"
0027 #include "model/messagesmodel.h"
0028 #include "model/searchchannelfilterproxymodel.h"
0029 #include "model/searchchannelmodel.h"
0030 #include "model/statusmodel.h"
0031 #include "model/threadmessagemodel.h"
0032 #include "model/usercompleterfilterproxymodel.h"
0033 #include "model/usercompletermodel.h"
0034 #include "model/usersforroommodel.h"
0035 #include "model/usersmodel.h"
0036 #include "otr/otrmanager.h"
0037 #include "rocketchatbackend.h"
0038 #include "rocketchatcache.h"
0039 #include "ruqola.h"
0040 #include "ruqola_debug.h"
0041 #include "ruqola_notification_debug.h"
0042 #include "ruqola_reconnect_core_debug.h"
0043 #include "ruqola_typing_notification_debug.h"
0044 #include "ruqolaglobalconfig.h"
0045 #include "ruqolalogger.h"
0046 #include "typingnotification.h"
0047 #include <TextEmoticonsCore/UnicodeEmoticonManager>
0048 
0049 #include "channelcounterinfo.h"
0050 #include "connection.h"
0051 #include "ddpapi/ddpclient.h"
0052 #include "directmessage/opendmjob.h"
0053 #include "discussions/discussions.h"
0054 #include "license/licensesisenterprisejob.h"
0055 #include "listmessages.h"
0056 #include "localdatabase/localdatabasemanager.h"
0057 #include "localdatabase/localdatabaseutils.h"
0058 #include "managechannels.h"
0059 #include "managelocaldatabase.h"
0060 #include "messagecache.h"
0061 #include "misc/roleslistjob.h"
0062 #include "receivetypingnotificationmanager.h"
0063 #include "ruqola_thread_message_debug.h"
0064 #include "uploadfilemanager.h"
0065 #include "videoconference/videoconferencemanager.h"
0066 #include "videoconference/videoconferencemessageinfomanager.h"
0067 
0068 #include <KLocalizedString>
0069 #include <KNotification>
0070 #include <QDesktopServices>
0071 #include <QJsonArray>
0072 #include <QTimer>
0073 #include <TextEmoticonsCore/EmojiModel>
0074 #include <TextEmoticonsCore/EmojiModelManager>
0075 #include <channelgroupbasejob.h>
0076 
0077 #if HAVE_NETWORKMANAGER
0078 #include <NetworkManagerQt/Manager>
0079 #endif
0080 
0081 #if HAVE_SOLID
0082 #include <Solid/Power>
0083 #endif
0084 
0085 #include <plugins/pluginauthentication.h>
0086 #include <plugins/pluginauthenticationinterface.h>
0087 
0088 #include "away/awaymanager.h"
0089 #include "customsound/customsoundsmanager.h"
0090 #include "model/switchchannelhistorymodel.h"
0091 #include "users/setstatusjob.h"
0092 #include "users/usersautocompletejob.h"
0093 
0094 RocketChatAccount::RocketChatAccount(const QString &accountFileName, QObject *parent)
0095     : QObject(parent)
0096     , mAccountRoomSettings(new AccountRoomSettings)
0097     , mUserModel(new UsersModel(this))
0098     , mRoomModel(new RoomModel(this, this))
0099     , mRuqolaServerConfig(new RuqolaServerConfig)
0100     , mUserCompleterModel(new UserCompleterModel(this))
0101     , mStatusModel(new StatusModel(this))
0102     , mOtrManager(new OtrManager(this, this))
0103     , mSearchChannelModel(new SearchChannelModel(this))
0104     , mLoginMethodModel(new LoginMethodModel(this))
0105     , mInputTextManager(new InputTextManager(this, this))
0106     , mInputThreadMessageTextManager(new InputTextManager(this, this))
0107     , mReceiveTypingNotificationManager(new ReceiveTypingNotificationManager(this))
0108     , mDiscussionsModel(new DiscussionsModel(this))
0109     , mCommandsModel(new CommandsModel(this))
0110     , mAutoTranslateLanguagesModel(new AutotranslateLanguagesModel(this))
0111     , mDownloadAppsLanguagesManager(new DownloadAppsLanguagesManager(this))
0112     , mMessageCache(new MessageCache(this, this))
0113     , mManageChannels(new ManageChannels(this, this))
0114     , mCustomSoundManager(new CustomSoundsManager(this))
0115     , mAwayManager(new AwayManager(this, this))
0116     , mSwitchChannelHistoryModel(new SwitchChannelHistoryModel(this))
0117     , mUploadFileManager(new UploadFileManager(this))
0118     , mVideoConferenceManager(new VideoConferenceManager(this))
0119     , mVideoConferenceMessageInfoManager(new VideoConferenceMessageInfoManager(this))
0120     , mLocalDatabaseManager(std::make_unique<LocalDatabaseManager>())
0121     , mManageLoadHistory(new ManageLocalDatabase(this, this))
0122 {
0123     qCDebug(RUQOLA_LOG) << " RocketChatAccount::RocketChatAccount(const QString &accountFileName, QObject *parent)" << accountFileName;
0124     // create an unique file for each account
0125     loadSettings(accountFileName);
0126 #if 0 // Disable it  otherwise autotests failed
0127     if (!mSettings->isValid()) {
0128         return;
0129     }
0130 #endif
0131     if (!qEnvironmentVariableIsEmpty("RUQOLA_LOGFILE")) {
0132         mRuqolaLogger = new RuqolaLogger(mSettings->accountName());
0133     }
0134 
0135     mServerConfigInfo = new ServerConfigInfo(this, this);
0136     // Create it before loading settings
0137 
0138     mInputTextManager->setObjectName(QStringLiteral("mInputTextManager"));
0139     connect(mInputTextManager,
0140             &InputTextManager::completionRequested,
0141             this,
0142             [this](const QString &pattern, const QString &exceptions, InputTextManager::CompletionForType type) {
0143                 inputAutocomplete(pattern, exceptions, type, false);
0144             });
0145 
0146     mInputThreadMessageTextManager->setObjectName(QStringLiteral("mInputThreadMessageTextManager"));
0147     connect(mInputThreadMessageTextManager,
0148             &InputTextManager::completionRequested,
0149             this,
0150             [this](const QString &pattern, const QString &exceptions, InputTextManager::CompletionForType type) {
0151                 inputAutocomplete(pattern, exceptions, type, true);
0152             });
0153 
0154     initializeAuthenticationPlugins();
0155 
0156     mRocketChatBackend = new RocketChatBackend(this, this);
0157 
0158     // Load list of unicode emoticon
0159     TextEmoticonsCore::UnicodeEmoticonManager::self();
0160 
0161     // After loadSettings
0162     mEmojiManager = new EmojiManager(this, this);
0163     mEmojiManager->setServerUrl(mSettings->serverUrl());
0164     connect(mEmojiManager, &EmojiManager::customEmojiChanged, this, &RocketChatAccount::updateCustomEmojiList);
0165 
0166     mEmoticonModel = new EmoticonModel(this, this);
0167     mEmoticonModel->setUnicodeEmoticons(mEmojiManager->unicodeEmojiList());
0168     mInputTextManager->setEmoticonModel(mEmoticonModel);
0169     mInputThreadMessageTextManager->setEmoticonModel(mEmoticonModel);
0170 
0171     mUserCompleterFilterModelProxy = new UserCompleterFilterProxyModel(this);
0172     mUserCompleterFilterModelProxy->setSourceModel(mUserCompleterModel);
0173 
0174     mSearchChannelFilterProxyModel = new SearchChannelFilterProxyModel(this);
0175     mSearchChannelFilterProxyModel->setSourceModel(mSearchChannelModel);
0176 
0177     mSearchMessageModel = new CommonMessagesModel(this, this);
0178     mSearchMessageFilterProxyModel = new CommonMessageFilterProxyModel(mSearchMessageModel, this);
0179 
0180     mFilesModelForRoom = new FilesForRoomModel(this, this);
0181     mFilesModelForRoom->setObjectName(QStringLiteral("filesmodelforrooms"));
0182     mFilesForRoomFilterProxyModel = new FilesForRoomFilterProxyModel(mFilesModelForRoom, this);
0183     mFilesForRoomFilterProxyModel->setObjectName(QStringLiteral("filesforroomfiltermodelproxy"));
0184 
0185     mDiscussionsModel->setObjectName(QStringLiteral("discussionsmodel"));
0186     mDiscussionsFilterProxyModel = new DiscussionsFilterProxyModel(mDiscussionsModel, this);
0187     mDiscussionsFilterProxyModel->setObjectName(QStringLiteral("discussionsfilterproxymodel"));
0188 
0189     mThreadMessageModel = new ThreadMessageModel(this, this);
0190     mThreadMessageModel->setObjectName(QStringLiteral("threadmessagemodel"));
0191 
0192     mListMessageModel = new ListMessagesModel(QString(), this, nullptr, this);
0193     mListMessageModel->setObjectName(QStringLiteral("listmessagemodel"));
0194 
0195     mListMessagesFilterProxyModel = new ListMessagesModelFilterProxyModel(mListMessageModel, this);
0196     mListMessagesFilterProxyModel->setObjectName(QStringLiteral("listmessagesfiltermodelproxy"));
0197 
0198     mAutoTranslateLanguagesModel->setObjectName(QStringLiteral("autotranslatelanguagesmodel"));
0199 
0200     connect(mRoomModel, &RoomModel::needToUpdateNotification, this, &RocketChatAccount::slotNeedToUpdateNotification);
0201     connect(mRoomModel, &RoomModel::roomNeedAttention, this, &RocketChatAccount::slotRoomNeedAttention);
0202     connect(mRoomModel, &RoomModel::roomRemoved, this, &RocketChatAccount::roomRemoved);
0203 
0204     mMessageQueue = new MessageQueue(this, this);
0205     mTypingNotification = new TypingNotification(this);
0206     mCache = new RocketChatCache(this, this);
0207 
0208     connect(mDownloadAppsLanguagesManager, &DownloadAppsLanguagesManager::fileLanguagesParseSuccess, this, &RocketChatAccount::slotFileLanguagedParsed);
0209     connect(mDownloadAppsLanguagesManager, &DownloadAppsLanguagesManager::fileLanguagesParseFailed, this, &RocketChatAccount::slotFileLanguagedParsed);
0210 
0211     connect(mCache, &RocketChatCache::fileDownloaded, this, &RocketChatAccount::fileDownloaded);
0212     connect(mTypingNotification, &TypingNotification::informTypingStatus, this, &RocketChatAccount::slotInformTypingStatus);
0213     connect(this, &RocketChatAccount::customUserStatusChanged, this, &RocketChatAccount::slotUpdateCustomUserStatus);
0214     QTimer::singleShot(0, this, &RocketChatAccount::clearModels);
0215 
0216 #if HAVE_SOLID
0217     connect(Solid::Power::self(), &Solid::Power::resumeFromSuspend, this, &RocketChatAccount::slotReconnectToServer);
0218 #endif
0219 
0220 #if HAVE_NETWORKMANAGER
0221     connect(NetworkManager::notifier(), &NetworkManager::Notifier::primaryConnectionChanged, this, [=](const QString &uni) {
0222         // If there is a new network connection, log out and back. The uni is "/" when the last primary connection
0223         // was closed. Do not log out to keep the messages visible. Login only if we were logged in at this point.
0224         if (uni != QLatin1String("/") && mDdp) {
0225             qCDebug(RUQOLA_RECONNECT_LOG) << "Reconnect and logout : " << accountName();
0226             logOut();
0227             slotReconnectToServer();
0228         }
0229     });
0230 #endif
0231     connect(mManageChannels, &ManageChannels::selectRoomByRoomIdRequested, this, &RocketChatAccount::selectRoomByRoomIdRequested);
0232     connect(mManageChannels, &ManageChannels::selectRoomByRoomNameRequested, this, &RocketChatAccount::selectRoomByRoomNameRequested);
0233     connect(mManageChannels, &ManageChannels::missingChannelPassword, this, &RocketChatAccount::missingChannelPassword);
0234     connect(mManageChannels, &ManageChannels::openArchivedRoom, this, &RocketChatAccount::openArchivedRoom);
0235     connect(&mRolesManager, &RolesManager::rolesChanged, this, &RocketChatAccount::rolesUpdated);
0236     connect(mCustomSoundManager, &CustomSoundsManager::customSoundRemoved, this, &RocketChatAccount::customSoundRemoved);
0237     connect(mCustomSoundManager, &CustomSoundsManager::customSoundAdded, this, &RocketChatAccount::customSoundAdded);
0238     connect(mCustomSoundManager, &CustomSoundsManager::customSoundUpdated, this, &RocketChatAccount::customSoundUpdated);
0239     connect(mAwayManager, &AwayManager::awayChanged, this, &RocketChatAccount::slotAwayStatusChanged);
0240 }
0241 
0242 RocketChatAccount::~RocketChatAccount()
0243 {
0244     delete mCache;
0245     mCache = nullptr;
0246 
0247     delete mRuqolaServerConfig;
0248     delete mRuqolaLogger;
0249     delete mAccountRoomSettings;
0250 }
0251 
0252 void RocketChatAccount::reconnectToServer()
0253 {
0254     slotReconnectToServer();
0255 }
0256 
0257 Room::TeamRoomInfo RocketChatAccount::roomFromTeamId(const QString &teamId) const
0258 {
0259     return mRoomModel->roomFromTeamId(teamId);
0260 }
0261 
0262 void RocketChatAccount::removeSettings()
0263 {
0264     mSettings->removeSettings();
0265 }
0266 
0267 void RocketChatAccount::loadSettings(const QString &accountFileName)
0268 {
0269     delete mSettings;
0270     mSettings = new RocketChatAccountSettings(accountFileName, this);
0271     if (mSettings->isValid()) {
0272         connect(mSettings, &RocketChatAccountSettings::serverURLChanged, this, &RocketChatAccount::serverUrlChanged);
0273         connect(mSettings, &RocketChatAccountSettings::userIdChanged, this, &RocketChatAccount::userIdChanged);
0274         connect(mSettings, &RocketChatAccountSettings::userNameChanged, this, &RocketChatAccount::userNameChanged);
0275         connect(mSettings, &RocketChatAccountSettings::passwordChanged, this, &RocketChatAccount::passwordChanged);
0276     }
0277 }
0278 
0279 void RocketChatAccount::slotRoomNeedAttention()
0280 {
0281     qCDebug(RUQOLA_NOTIFICATION_LOG) << " emit alert"
0282                                      << " account name: " << accountName();
0283     Q_EMIT roomNeedAttention();
0284 }
0285 
0286 void RocketChatAccount::setOwnUserPreferences(const OwnUserPreferences &ownUserPreferences)
0287 {
0288     mOwnUser.setOwnUserPreferences(ownUserPreferences);
0289 }
0290 
0291 OwnUser RocketChatAccount::ownUser() const
0292 {
0293     return mOwnUser;
0294 }
0295 
0296 void RocketChatAccount::cleanChannelHistory(const RocketChatRestApi::RoomsCleanHistoryJob::CleanHistoryInfo &info)
0297 {
0298     restApi()->cleanChannelHistory(info);
0299 }
0300 
0301 void RocketChatAccount::slotNeedToUpdateNotification()
0302 {
0303     bool hasAlert = false;
0304     int nbUnread = 0;
0305     mRoomModel->getUnreadAlertFromAccount(hasAlert, nbUnread);
0306     Q_EMIT updateNotification(hasAlert, nbUnread, accountName());
0307 }
0308 
0309 void RocketChatAccount::clearModels()
0310 {
0311     // Clear rooms data and refill it with data in the cache, if there is
0312     mRoomModel->reset();
0313 
0314     mMessageQueue->loadCache();
0315     // Try to send queue message
0316     mMessageQueue->processQueue();
0317 }
0318 
0319 SearchChannelFilterProxyModel *RocketChatAccount::searchChannelFilterProxyModel() const
0320 {
0321     return mSearchChannelFilterProxyModel;
0322 }
0323 
0324 SearchChannelModel *RocketChatAccount::searchChannelModel() const
0325 {
0326     return mSearchChannelModel;
0327 }
0328 
0329 UserCompleterModel *RocketChatAccount::userCompleterModel() const
0330 {
0331     return mUserCompleterModel;
0332 }
0333 
0334 UserCompleterFilterProxyModel *RocketChatAccount::userCompleterFilterModelProxy() const
0335 {
0336     return mUserCompleterFilterModelProxy;
0337 }
0338 
0339 EmojiManager *RocketChatAccount::emojiManager() const
0340 {
0341     return mEmojiManager;
0342 }
0343 
0344 QString RocketChatAccount::userStatusStr(const QString &name)
0345 {
0346     return mUserModel->userStatusStr(name);
0347 }
0348 
0349 QString RocketChatAccount::userStatusIconFileName(const QString &name)
0350 {
0351     return mUserModel->userStatusIconFileName(name);
0352 }
0353 
0354 bool RocketChatAccount::userIsOffline(const QString &name)
0355 {
0356     return mUserModel->userIsOffline(name);
0357 }
0358 
0359 StatusModel *RocketChatAccount::statusModel() const
0360 {
0361     return mStatusModel;
0362 }
0363 
0364 RuqolaServerConfig *RocketChatAccount::ruqolaServerConfig() const
0365 {
0366     return mRuqolaServerConfig;
0367 }
0368 
0369 RuqolaLogger *RocketChatAccount::ruqolaLogger() const
0370 {
0371     return mRuqolaLogger;
0372 }
0373 
0374 UsersForRoomModel *RocketChatAccount::usersModelForRoom(const QString &roomId) const
0375 {
0376     return mRoomModel->usersModelForRoom(roomId);
0377 }
0378 
0379 FilesForRoomFilterProxyModel *RocketChatAccount::filesForRoomFilterProxyModel() const
0380 {
0381     return mFilesForRoomFilterProxyModel;
0382 }
0383 
0384 RocketChatBackend *RocketChatAccount::rocketChatBackend() const
0385 {
0386     return mRocketChatBackend;
0387 }
0388 
0389 MessageQueue *RocketChatAccount::messageQueue() const
0390 {
0391     return mMessageQueue;
0392 }
0393 
0394 RocketChatAccountSettings *RocketChatAccount::settings() const
0395 {
0396     return mSettings;
0397 }
0398 
0399 void RocketChatAccount::slotInformTypingStatus(const QString &room, bool typing)
0400 {
0401     qCDebug(RUQOLA_TYPING_NOTIFICATION_LOG) << " slotInformTypingStatus room " << room << " typing " << typing;
0402     ddp()->informTypingStatus(room, typing, mSettings->userName());
0403 }
0404 
0405 RoomModel *RocketChatAccount::roomModel() const
0406 {
0407     return mRoomModel;
0408 }
0409 
0410 UsersModel *RocketChatAccount::usersModel() const
0411 {
0412     return mUserModel;
0413 }
0414 
0415 Room *RocketChatAccount::room(const QString &roomId)
0416 {
0417     return mRoomModel->findRoom(roomId);
0418 }
0419 
0420 DiscussionsFilterProxyModel *RocketChatAccount::discussionsFilterProxyModel() const
0421 {
0422     return mDiscussionsFilterProxyModel;
0423 }
0424 
0425 MessagesModel *RocketChatAccount::messageModelForRoom(const QString &roomID)
0426 {
0427     return mRoomModel->messageModel(roomID);
0428 }
0429 
0430 void RocketChatAccount::changeShowOriginalMessage(const QString &roomId, const QString &messageId, bool showOriginal)
0431 {
0432     MessagesModel *model = mRoomModel->messageModel(roomId);
0433     if (model) {
0434         model->changeShowOriginalMessage(messageId, showOriginal);
0435     } else {
0436         qCWarning(RUQOLA_LOG) << "impossible to find room: " << roomId;
0437     }
0438 }
0439 
0440 QString RocketChatAccount::getUserCurrentMessage(const QString &roomId)
0441 {
0442     return mRoomModel->inputMessage(roomId);
0443 }
0444 
0445 void RocketChatAccount::setUserCurrentMessage(const QString &message, const QString &roomId)
0446 {
0447     mRoomModel->setInputMessage(roomId, message);
0448 }
0449 
0450 void RocketChatAccount::textEditing(const QString &roomId, bool clearNotification)
0451 {
0452     mTypingNotification->textNotificationChanged(roomId, clearNotification);
0453 }
0454 
0455 void RocketChatAccount::reactOnMessage(const QString &messageId, const QString &emoji, bool shouldReact)
0456 {
0457     if (emoji.startsWith(QLatin1Char(':')) && emoji.endsWith(QLatin1Char(':'))) {
0458         restApi()->reactOnMessage(messageId, emoji, shouldReact);
0459     } else {
0460         restApi()->reactOnMessage(messageId, mEmojiManager->normalizedReactionEmoji(emoji), shouldReact);
0461     }
0462 }
0463 
0464 void RocketChatAccount::sendMessage(const QString &roomID, const QString &message)
0465 {
0466     restApi()->postMessage(roomID, message);
0467     markRoomAsRead(roomID);
0468 }
0469 
0470 void RocketChatAccount::updateMessage(const QString &roomID, const QString &messageId, const QString &message)
0471 {
0472     restApi()->updateMessage(roomID, messageId, message);
0473 }
0474 
0475 void RocketChatAccount::replyOnThread(const QString &roomID, const QString &threadMessageId, const QString &message)
0476 {
0477     restApi()->sendMessage(roomID, message, QString(), threadMessageId);
0478 }
0479 
0480 void RocketChatAccount::deleteFileMessage(const QString &roomId, const QString &fileId, Room::RoomType channelType)
0481 {
0482     ddp()->deleteFileMessage(roomId, fileId, channelType);
0483 }
0484 
0485 QString RocketChatAccount::avatarUrl(const Utils::AvatarInfo &info)
0486 {
0487     return mCache->avatarUrl(info);
0488 }
0489 
0490 void RocketChatAccount::insertAvatarUrl(const QString &userId, const QUrl &url)
0491 {
0492     mCache->insertAvatarUrl(userId, url);
0493 }
0494 
0495 RocketChatRestApi::Connection *RocketChatAccount::restApi()
0496 {
0497     if (!mRestApi) {
0498         mRestApi = new RocketChatRestApi::Connection(this);
0499         connect(mRestApi, &RocketChatRestApi::Connection::loadEmojiCustomDone, this, &RocketChatAccount::loadEmoji);
0500         connect(mRestApi, &RocketChatRestApi::Connection::channelMembersDone, this, &RocketChatAccount::parseUsersForRooms);
0501         connect(mRestApi, &RocketChatRestApi::Connection::channelFilesDone, this, &RocketChatAccount::slotChannelFilesDone);
0502         connect(mRestApi, &RocketChatRestApi::Connection::channelRolesDone, this, &RocketChatAccount::slotChannelGroupRolesDone);
0503         connect(mRestApi, &RocketChatRestApi::Connection::groupRolesDone, this, &RocketChatAccount::slotChannelGroupRolesDone);
0504         connect(mRestApi, &RocketChatRestApi::Connection::searchMessageDone, this, &RocketChatAccount::slotSearchMessages);
0505         connect(mRestApi, &RocketChatRestApi::Connection::failed, this, &RocketChatAccount::slotJobFailed);
0506         connect(mRestApi, &RocketChatRestApi::Connection::spotlightDone, this, &RocketChatAccount::slotSplotLightDone);
0507         connect(mRestApi, &RocketChatRestApi::Connection::directoryDone, this, &RocketChatAccount::slotDirectoryDone);
0508         connect(mRestApi, &RocketChatRestApi::Connection::getThreadMessagesDone, this, &RocketChatAccount::slotGetThreadMessagesDone);
0509         connect(mRestApi, &RocketChatRestApi::Connection::getDiscussionsDone, this, &RocketChatAccount::slotGetDiscussionsListDone);
0510         connect(mRestApi, &RocketChatRestApi::Connection::channelListDone, this, &RocketChatAccount::slotChannelListDone);
0511         connect(mRestApi, &RocketChatRestApi::Connection::markAsReadDone, this, &RocketChatAccount::slotMarkAsReadDone);
0512         connect(mRestApi, &RocketChatRestApi::Connection::postMessageDone, this, &RocketChatAccount::slotPostMessageDone);
0513         connect(mRestApi, &RocketChatRestApi::Connection::updateMessageFailed, this, &RocketChatAccount::updateMessageFailed);
0514 
0515         connect(mRestApi, &RocketChatRestApi::Connection::getThreadsDone, this, [this](const QJsonObject &obj, const QString &roomId, bool onlyUnread) {
0516             slotGetListMessagesDone(obj,
0517                                     roomId,
0518                                     onlyUnread ? ListMessagesModel::ListMessageType::UnreadThreadsMessages
0519                                                : ListMessagesModel::ListMessageType::ThreadsMessages);
0520         });
0521         connect(mRestApi, &RocketChatRestApi::Connection::getMentionedMessagesDone, this, [this](const QJsonObject &obj, const QString &roomId) {
0522             slotGetListMessagesDone(obj, roomId, ListMessagesModel::ListMessageType::MentionsMessages);
0523         });
0524         connect(mRestApi, &RocketChatRestApi::Connection::getPinnedMessagesDone, this, [this](const QJsonObject &obj, const QString &roomId) {
0525             slotGetListMessagesDone(obj, roomId, ListMessagesModel::ListMessageType::PinnedMessages);
0526         });
0527         connect(mRestApi, &RocketChatRestApi::Connection::getStarredMessagesDone, this, [this](const QJsonObject &obj, const QString &roomId) {
0528             slotGetListMessagesDone(obj, roomId, ListMessagesModel::ListMessageType::StarredMessages);
0529         });
0530 
0531         connect(mRestApi, &RocketChatRestApi::Connection::getSupportedLanguagesDone, this, &RocketChatAccount::slotGetSupportedLanguagesDone);
0532         connect(mRestApi, &RocketChatRestApi::Connection::usersPresenceDone, this, &RocketChatAccount::slotUsersPresenceDone);
0533         connect(mRestApi, &RocketChatRestApi::Connection::usersAutocompleteDone, this, &RocketChatAccount::slotUserAutoCompleterDone);
0534         connect(mRestApi, &RocketChatRestApi::Connection::listCommandsDone, this, &RocketChatAccount::slotListCommandDone);
0535         connect(mRestApi, &RocketChatRestApi::Connection::registerUserDone, this, &RocketChatAccount::slotRegisterUserDone);
0536         connect(mRestApi, &RocketChatRestApi::Connection::channelGetCountersDone, this, &RocketChatAccount::slotChannelGetCountersDone);
0537         connect(mRestApi, &RocketChatRestApi::Connection::customUserStatusDone, this, &RocketChatAccount::slotCustomUserStatusDone);
0538         connect(mRestApi, &RocketChatRestApi::Connection::roomExportDone, this, &RocketChatAccount::slotRoomExportDone);
0539         connect(mRestApi, &RocketChatRestApi::Connection::permissionListAllDone, this, &RocketChatAccount::slotPermissionListAllDone);
0540         connect(mRestApi, &RocketChatRestApi::Connection::usersSetPreferencesDone, this, &RocketChatAccount::slotUsersSetPreferencesDone);
0541         connect(mRestApi, &RocketChatRestApi::Connection::networkSessionFailedError, this, [this]() {
0542             qCDebug(RUQOLA_RECONNECT_LOG) << "networkSessionFailedError Reconnect and logout : " << accountName();
0543             logOut();
0544             slotReconnectToServer();
0545         });
0546 
0547         mRestApi->setServerUrl(mSettings->serverUrl());
0548         mRestApi->setRestApiLogger(mRuqolaLogger);
0549         mCache->setRestApiConnection(mRestApi);
0550     }
0551     return mRestApi;
0552 }
0553 
0554 void RocketChatAccount::slotJobFailed(const QString &str)
0555 {
0556     Q_EMIT jobFailed(str, accountName());
0557 }
0558 
0559 const BannerInfos &RocketChatAccount::bannerInfos() const
0560 {
0561     return mBannerInfos;
0562 }
0563 
0564 UploadFileManager *RocketChatAccount::uploadFileManager() const
0565 {
0566     return mUploadFileManager;
0567 }
0568 
0569 int RocketChatAccount::messageMaximumAllowedSize() const
0570 {
0571     return mRuqolaServerConfig->messageMaximumAllowedSize();
0572 }
0573 
0574 bool RocketChatAccount::messageAllowConvertLongMessagesToAttachment() const
0575 {
0576     return mRuqolaServerConfig->messageAllowConvertLongMessagesToAttachment();
0577 }
0578 
0579 bool RocketChatAccount::useRealName() const
0580 {
0581     return mRuqolaServerConfig->useRealName();
0582 }
0583 
0584 SwitchChannelHistoryModel *RocketChatAccount::switchChannelHistoryModel() const
0585 {
0586     return mSwitchChannelHistoryModel;
0587 }
0588 
0589 const QStringList &RocketChatAccount::searchListCompletion() const
0590 {
0591     return mSearchListCompletion;
0592 }
0593 
0594 void RocketChatAccount::setSearchListCompletion(const QStringList &newSearchListCompletion)
0595 {
0596     mSearchListCompletion = newSearchListCompletion;
0597 }
0598 
0599 void RocketChatAccount::leaveRoom(const QString &identifier, Room::RoomType channelType)
0600 {
0601     switch (channelType) {
0602     case Room::RoomType::Private:
0603         restApi()->leaveGroups(identifier);
0604         break;
0605     case Room::RoomType::Channel:
0606         restApi()->leaveChannel(identifier);
0607         break;
0608     case Room::RoomType::Direct:
0609     case Room::RoomType::Unknown:
0610         qCWarning(RUQOLA_LOG) << " unsupported leave room for type " << channelType;
0611         break;
0612     }
0613 }
0614 
0615 void RocketChatAccount::hideRoom(const QString &roomId, Room::RoomType channelType)
0616 {
0617     restApi()->closeChannel(roomId, Room::roomFromRoomType(channelType));
0618 }
0619 
0620 DDPClient *RocketChatAccount::ddp()
0621 {
0622     if (!mDdp) {
0623         mDdp = new DDPClient(this, this);
0624         connect(mDdp->authenticationManager(), &DDPAuthenticationManager::loginStatusChanged, this, &RocketChatAccount::loginStatusChangedSlot);
0625         connect(mDdp, &DDPClient::connectedChanged, this, &RocketChatAccount::connectedChanged);
0626         connect(mDdp, &DDPClient::changed, this, &RocketChatAccount::changed);
0627         connect(mDdp, &DDPClient::added, this, &RocketChatAccount::added);
0628         connect(mDdp, &DDPClient::removed, this, &RocketChatAccount::removed);
0629         connect(mDdp, &DDPClient::socketError, this, &RocketChatAccount::socketError);
0630         connect(mDdp, &DDPClient::disconnectedByServer, this, &RocketChatAccount::slotReconnectToServer);
0631 
0632         if (mSettings) {
0633             mDdp->setServerUrl(mSettings->serverUrl());
0634         }
0635         mDdp->start();
0636     }
0637     return mDdp;
0638 }
0639 
0640 bool RocketChatAccount::editingMode() const
0641 {
0642     return mEditingMode;
0643 }
0644 
0645 DDPAuthenticationManager::LoginStatus RocketChatAccount::loginStatus()
0646 {
0647     // TODO: DDP API should exist as soon as the hostname is known
0648     if (mDdp) {
0649         return ddp()->authenticationManager()->loginStatus();
0650     } else {
0651         return DDPAuthenticationManager::LoggedOut;
0652     }
0653 }
0654 
0655 void RocketChatAccount::tryLogin()
0656 {
0657     qCDebug(RUQOLA_LOG) << "Attempting login" << mSettings->userName() << "on" << mSettings->serverUrl();
0658 
0659     // ddp() creates a new DDPClient object if it doesn't exist.
0660     ddp()->enqueueLogin();
0661 
0662     // In the meantime, load cache...
0663     mRoomModel->reset();
0664 }
0665 
0666 void RocketChatAccount::logOut()
0667 {
0668     mSettings->logout();
0669     mRoomModel->clear();
0670     if (mDdp) {
0671         mDdp->authenticationManager()->logout();
0672         delete mDdp;
0673         mDdp = nullptr;
0674     }
0675     delete mRestApi;
0676     mRestApi = nullptr;
0677 }
0678 
0679 void RocketChatAccount::clearAllUnreadMessages()
0680 {
0681     for (int roomIdx = 0, nRooms = mRoomModel->rowCount(); roomIdx < nRooms; ++roomIdx) {
0682         const auto roomModelIndex = mRoomModel->index(roomIdx);
0683         const auto roomId = roomModelIndex.data(RoomModel::RoomId).toString();
0684         const bool roomHasAlert = roomModelIndex.data(RoomModel::RoomAlert).toBool();
0685         if (roomHasAlert) {
0686             markRoomAsRead(roomId);
0687         }
0688     }
0689 }
0690 
0691 void RocketChatAccount::markRoomAsRead(const QString &roomId)
0692 {
0693     mMarkUnreadThreadsAsReadOnNextReply = true;
0694     restApi()->markRoomAsRead(roomId);
0695     if (threadsEnabled()) {
0696         getListMessages(roomId, ListMessagesModel::UnreadThreadsMessages);
0697     }
0698 }
0699 
0700 void RocketChatAccount::changeFavorite(const QString &roomId, bool checked)
0701 {
0702     restApi()->markAsFavorite(roomId, checked);
0703 }
0704 
0705 void RocketChatAccount::openPrivateGroup(const QString &roomId, ChannelTypeInfo typeInfo)
0706 {
0707     mManageChannels->openPrivateGroup(roomId, typeInfo);
0708 }
0709 
0710 void RocketChatAccount::openChannel(const QString &roomId, ChannelTypeInfo typeInfo)
0711 {
0712     mManageChannels->openChannel(roomId, typeInfo);
0713 }
0714 
0715 void RocketChatAccount::openArchivedRoom(const RocketChatRestApi::ChannelGroupBaseJob::ChannelGroupInfo &channelInfo)
0716 {
0717     // TODO
0718 }
0719 
0720 void RocketChatAccount::joinJitsiConfCall(const QString &roomId)
0721 {
0722     qCDebug(RUQOLA_LOG) << " void RocketChatAccount::joinJitsiConfCall(const QString &roomId)" << roomId;
0723     // const QString hash = QString::fromLatin1(QCryptographicHash::hash((mRuqolaServerConfig->uniqueId() + roomId).toUtf8(), QCryptographicHash::Md5).toHex());
0724     const QString hash = mRuqolaServerConfig->uniqueId() + roomId;
0725 #if defined(Q_OS_IOS)
0726     const QString scheme = QStringLiteral("org.jitsi.meet://");
0727 #else
0728     const QString scheme = QStringLiteral("https://");
0729 #endif
0730     const QString url = scheme + mRuqolaServerConfig->jitsiMeetUrl() + QLatin1Char('/') + mRuqolaServerConfig->jitsiMeetPrefix() + hash;
0731     const QUrl clickedUrl = QUrl::fromUserInput(url);
0732     QDesktopServices::openUrl(clickedUrl);
0733 }
0734 
0735 void RocketChatAccount::eraseRoom(const QString &roomId, Room::RoomType channelType)
0736 {
0737     switch (channelType) {
0738     case Room::RoomType::Private:
0739         restApi()->groupDelete(roomId);
0740         break;
0741     case Room::RoomType::Channel:
0742         restApi()->channelDelete(roomId);
0743         break;
0744     case Room::RoomType::Direct:
0745     case Room::RoomType::Unknown:
0746         qCWarning(RUQOLA_LOG) << " unsupported delete for type " << channelType;
0747         break;
0748     }
0749 }
0750 
0751 void RocketChatAccount::openDirectChannel(const QString &roomId)
0752 {
0753     if (hasPermission(QStringLiteral("create-d"))) {
0754         auto job = new RocketChatRestApi::OpenDmJob(this);
0755         job->setDirectUserId(roomId);
0756         restApi()->initializeRestApiJob(job);
0757         // TODO ????
0758         // connect(job, &RocketChatRestApi::OpenDmJob::openDmDone, this, &RolesManager::parseRoles);
0759         if (!job->start()) {
0760             qCWarning(RUQOLA_LOG) << "Impossible to start OpenDmJob job";
0761         }
0762         qDebug() << "Open direct conversation channel with" << roomId;
0763     }
0764 }
0765 
0766 void RocketChatAccount::createNewChannel(const RocketChatRestApi::CreateChannelTeamInfo &info)
0767 {
0768     if (!info.name.trimmed().isEmpty()) {
0769         if (info.privateChannel) {
0770             restApi()->createGroups(info);
0771         } else {
0772             restApi()->createChannels(info);
0773         }
0774     } else {
0775         qCDebug(RUQOLA_LOG) << "Channel name can't be empty";
0776     }
0777 }
0778 
0779 void RocketChatAccount::joinDiscussion(const QString &roomId, const QString &joinCode)
0780 {
0781     ddp()->joinRoom(roomId, joinCode);
0782 }
0783 
0784 void RocketChatAccount::joinRoom(const QString &roomId, const QString &joinCode)
0785 {
0786     RocketChatRestApi::ChannelGroupBaseJob::ChannelGroupInfo info;
0787     info.channelGroupInfoType = RocketChatRestApi::ChannelGroupBaseJob::ChannelGroupInfoType::Identifier;
0788     info.identifier = roomId;
0789     mManageChannels->channelJoin(info, joinCode);
0790 }
0791 
0792 void RocketChatAccount::channelAndPrivateAutocomplete(const QString &pattern)
0793 {
0794     if (pattern.isEmpty()) {
0795         searchChannelModel()->clear();
0796     } else {
0797         // Use restapi
0798         // Avoid to show own user
0799         // restApi()->searchRooms(pattern);
0800         restApi()->searchRoomUser(pattern);
0801     }
0802 }
0803 
0804 void RocketChatAccount::listEmojiCustom()
0805 {
0806     restApi()->listEmojiCustom();
0807 }
0808 
0809 void RocketChatAccount::setDefaultStatus(User::PresenceStatus status, const QString &messageStatus)
0810 {
0811     RocketChatRestApi::SetStatusJob::StatusType type = RocketChatRestApi::SetStatusJob::Unknown;
0812     switch (status) {
0813     case User::PresenceStatus::PresenceOnline:
0814         type = RocketChatRestApi::SetStatusJob::OnLine;
0815         break;
0816     case User::PresenceStatus::PresenceBusy:
0817         type = RocketChatRestApi::SetStatusJob::Busy;
0818         break;
0819     case User::PresenceStatus::PresenceAway:
0820         type = RocketChatRestApi::SetStatusJob::Away;
0821         break;
0822     case User::PresenceStatus::PresenceOffline:
0823         type = RocketChatRestApi::SetStatusJob::Offline;
0824         break;
0825     case User::PresenceStatus::Unknown:
0826         type = RocketChatRestApi::SetStatusJob::Unknown;
0827         break;
0828     }
0829     mPresenceStatus = status;
0830     // qDebug() << "RocketChatAccount::setDefaultStatus  " << messageStatus;
0831     restApi()->setUserStatus(userId(), type, messageStatus);
0832 }
0833 
0834 QList<TextEmoticonsCore::CustomEmoji> RocketChatAccount::customEmojies() const
0835 {
0836     QList<TextEmoticonsCore::CustomEmoji> mCustomEmojies;
0837     const auto customEmojiList = mEmojiManager->customEmojiList();
0838     for (const auto &emoji : customEmojiList) {
0839         TextEmoticonsCore::CustomEmoji custom;
0840         custom.setIdentifier(emoji.emojiIdentifier());
0841         custom.setIsAnimatedEmoji(emoji.isAnimatedImage());
0842         mCustomEmojies.append(custom);
0843     }
0844     return mCustomEmojies;
0845 }
0846 
0847 void RocketChatAccount::loadEmoji(const QJsonObject &obj)
0848 {
0849     mEmojiManager->loadCustomEmoji(obj);
0850     updateCustomEmojiList(false);
0851 }
0852 
0853 void RocketChatAccount::updateCustomEmojiList(bool fetchListCustom)
0854 {
0855     if (fetchListCustom) {
0856         listEmojiCustom();
0857     } else {
0858         const auto customEmojiList = mEmojiManager->customEmojiList();
0859         mEmoticonModel->setCustomEmojiList(customEmojiList);
0860         if (Ruqola::self()->customEmojiIconManager()->currentRocketChatAccount() == this) {
0861             TextEmoticonsCore::EmojiModelManager::self()->emojiModel()->setCustomEmojiList(customEmojies());
0862         }
0863     }
0864 }
0865 
0866 OtrManager *RocketChatAccount::otrManager() const
0867 {
0868     return mOtrManager;
0869 }
0870 
0871 void RocketChatAccount::deleteMessage(const QString &messageId, const QString &roomId)
0872 {
0873     restApi()->deleteMessage(roomId, messageId);
0874 }
0875 
0876 void RocketChatAccount::insertCompleterUsers()
0877 {
0878     userCompleterModel()->insertUsers(rocketChatBackend()->users());
0879 }
0880 
0881 void RocketChatAccount::userAutocomplete(const QString &searchText, const QString &exception)
0882 {
0883     userCompleterModel()->clear();
0884     if (!searchText.isEmpty()) {
0885         RocketChatRestApi::UsersAutocompleteJob::UsersAutocompleterInfo info;
0886         info.pattern = searchText;
0887         info.exception = exception;
0888         restApi()->usersAutocomplete(info);
0889     }
0890 }
0891 
0892 void RocketChatAccount::membersInRoom(const QString &roomId, Room::RoomType channelType)
0893 {
0894     // We call it first for initialize all member in rooms.
0895     // We need to clear it. It can be initialize by "/rooms-changed" signal
0896     UsersForRoomModel *usersModelForRoom = roomModel()->usersModelForRoom(roomId);
0897     if (usersModelForRoom) {
0898         if (usersModelForRoom->total() != 0) {
0899             usersModelForRoom->clear();
0900         }
0901         usersModelForRoom->setLoadMoreUsersInProgress(true);
0902     }
0903     restApi()->membersInRoom(roomId, Room::roomFromRoomType(channelType));
0904 }
0905 
0906 void RocketChatAccount::updateUserInRoom(const QJsonObject &roomData)
0907 {
0908     const QString roomId = roomData.value(QStringLiteral("_id")).toString();
0909     UsersForRoomModel *usersModelForRoom = roomModel()->usersModelForRoom(roomId);
0910     if (usersModelForRoom) {
0911         const int numberOfUsers = roomData.value(QStringLiteral("usersCount")).toInt();
0912         if (usersModelForRoom->total() != numberOfUsers) {
0913             if (!usersModelForRoom->loadMoreUsersInProgress()) {
0914                 usersModelForRoom->clear();
0915                 usersModelForRoom->setLoadMoreUsersInProgress(true);
0916                 const QString channelType = roomData.value(QStringLiteral("t")).toString();
0917                 restApi()->membersInRoom(roomId, channelType, 0, qMin(50, usersModelForRoom->offset()));
0918             }
0919         }
0920     }
0921 }
0922 
0923 void RocketChatAccount::parseUsersForRooms(const QJsonObject &obj, const RocketChatRestApi::ChannelGroupBaseJob::ChannelGroupInfo &channelInfo)
0924 {
0925     // FIXME channelInfo
0926     const QString channelInfoIdentifier = channelInfo.identifier;
0927     UsersForRoomModel *usersModelForRoom = roomModel()->usersModelForRoom(channelInfoIdentifier);
0928     if (usersModelForRoom) {
0929         usersModelForRoom->parseUsersForRooms(obj, mUserModel, true);
0930         usersModelForRoom->setLoadMoreUsersInProgress(false);
0931     } else {
0932         qCWarning(RUQOLA_LOG) << " Impossible to find room " << channelInfoIdentifier;
0933     }
0934 }
0935 
0936 void RocketChatAccount::roomFiles(const QString &roomId, Room::RoomType channelType)
0937 {
0938     mFilesModelForRoom->initialize();
0939     restApi()->filesInRoom(roomId, Room::roomFromRoomType(channelType));
0940 }
0941 
0942 MessagesModel *RocketChatAccount::threadMessageModel() const
0943 {
0944     return mThreadMessageModel;
0945 }
0946 
0947 DiscussionsModel *RocketChatAccount::discussionsModel() const
0948 {
0949     return mDiscussionsModel;
0950 }
0951 
0952 FilesForRoomModel *RocketChatAccount::filesModelForRoom() const
0953 {
0954     return mFilesModelForRoom;
0955 }
0956 
0957 EmoticonModel *RocketChatAccount::emoticonModel() const
0958 {
0959     return mEmoticonModel;
0960 }
0961 
0962 CommandsModel *RocketChatAccount::commandsModel() const
0963 {
0964     return mCommandsModel;
0965 }
0966 
0967 ReceiveTypingNotificationManager *RocketChatAccount::receiveTypingNotificationManager() const
0968 {
0969     return mReceiveTypingNotificationManager;
0970 }
0971 
0972 void RocketChatAccount::slotChannelGroupRolesDone(const QJsonObject &obj, const RocketChatRestApi::ChannelGroupBaseJob::ChannelGroupInfo &channelInfo)
0973 {
0974     Room *room = mRoomModel->findRoom(channelInfo.identifier);
0975     if (room) {
0976         Roles r;
0977         r.parseRole(obj);
0978         room->setRolesForRooms(r);
0979         // qDebug() << " r " << r << " room " << room->name() << " obj" << obj;
0980     } else {
0981         qCWarning(RUQOLA_LOG) << " Impossible to find room " << channelInfo.identifier;
0982     }
0983 }
0984 
0985 void RocketChatAccount::slotGetThreadMessagesDone(const QJsonObject &obj, const QString &threadMessageId)
0986 {
0987     if (mThreadMessageModel->threadMessageId() != threadMessageId) {
0988         mThreadMessageModel->setThreadMessageId(threadMessageId);
0989         mThreadMessageModel->parseThreadMessages(obj);
0990     } else {
0991         mThreadMessageModel->loadMoreThreadMessages(obj);
0992     }
0993 }
0994 
0995 void RocketChatAccount::slotGetDiscussionsListDone(const QJsonObject &obj, const QString &roomId)
0996 {
0997     if (mDiscussionsModel->roomId() != roomId) {
0998         mDiscussionsModel->parseDiscussions(obj, roomId);
0999     } else {
1000         mDiscussionsModel->addMoreDiscussions(obj);
1001     }
1002     mDiscussionsModel->setLoadMoreDiscussionsInProgress(false);
1003 }
1004 
1005 void RocketChatAccount::slotGetListMessagesDone(const QJsonObject &obj, const QString &roomId, ListMessagesModel::ListMessageType type)
1006 {
1007     if (mMarkUnreadThreadsAsReadOnNextReply && type == ListMessagesModel::UnreadThreadsMessages) {
1008         qCDebug(RUQOLA_THREAD_MESSAGE_LOG) << "Obj" << obj << "roomId:" << roomId;
1009         mMarkUnreadThreadsAsReadOnNextReply = false;
1010 
1011         ListMessages messages;
1012         messages.parseMessages(obj, QStringLiteral("threads"));
1013         const auto listMessages = messages.listMessages();
1014         for (const auto &msg : listMessages) {
1015             QJsonObject params;
1016             params.insert(QStringLiteral("tmid"), msg.messageId());
1017             mDdp->method(QStringLiteral("getThreadMessages"), QJsonDocument(params), [](const QJsonObject &reply, RocketChatAccount *account) {
1018                 // don't trigger warning about unhandled replies
1019                 Q_UNUSED(reply)
1020                 Q_UNUSED(account)
1021             });
1022         }
1023         return;
1024     }
1025 
1026     if (mListMessageModel->roomId() != roomId || mListMessageModel->listMessageType() != type) {
1027         mListMessageModel->setRoomId(roomId);
1028         mListMessageModel->setListMessageType(type);
1029         mListMessageModel->parseListMessages(obj);
1030     } else {
1031         mListMessageModel->loadMoreListMessages(obj);
1032     }
1033     mListMessageModel->setLoadMoreListMessagesInProgress(false);
1034 }
1035 
1036 void RocketChatAccount::slotUserAutoCompleterDone(const QJsonObject &obj)
1037 {
1038     const QVector<User> users = User::parseUsersList(obj, roleInfo());
1039     mUserCompleterModel->insertUsers(users);
1040 }
1041 
1042 User::PresenceStatus RocketChatAccount::presenceStatus() const
1043 {
1044     return mPresenceStatus;
1045 }
1046 
1047 AccountRoomSettings *RocketChatAccount::accountRoomSettings() const
1048 {
1049     return mAccountRoomSettings;
1050 }
1051 
1052 ListMessagesModelFilterProxyModel *RocketChatAccount::listMessagesFilterProxyModel() const
1053 {
1054     return mListMessagesFilterProxyModel;
1055 }
1056 
1057 ListMessagesModel *RocketChatAccount::listMessageModel() const
1058 {
1059     return mListMessageModel;
1060 }
1061 
1062 void RocketChatAccount::slotDirectoryDone(const QJsonObject &obj)
1063 {
1064     qDebug() << "void RocketChatAccount::slotDirectoryDone(const QJsonObject &obj)" << obj;
1065     mSearchChannelModel->parseChannels(obj);
1066 }
1067 
1068 void RocketChatAccount::slotSplotLightDone(const QJsonObject &obj)
1069 {
1070     // qDebug() << " void RocketChatAccount::slotSplotLightDone(const QJsonObject &obj)"<<obj;
1071     // If empty ! show empty list
1072     mSearchChannelModel->parseChannels(obj);
1073 }
1074 
1075 void RocketChatAccount::slotChannelListDone(const QJsonObject &obj)
1076 {
1077     // qDebug() << " void RocketChatAccount::slotChannelListDone(const QJsonObject &obj)" << obj;
1078     mSearchChannelModel->parseAllChannels(obj);
1079 }
1080 
1081 void RocketChatAccount::slotChannelFilesDone(const QJsonObject &obj, const RocketChatRestApi::ChannelGroupBaseJob::ChannelGroupInfo &channelInfo)
1082 {
1083     // TODO fixme channelinfo
1084     // qDebug() << " slotChannelFilesDone(const QJsonObject &obj, const QString &roomId)" << roomId << " obj " << obj;
1085     if (mFilesModelForRoom->roomId() != channelInfo.identifier) {
1086         mFilesModelForRoom->parseFileAttachments(obj, channelInfo.identifier);
1087     } else {
1088         mFilesModelForRoom->addMoreFileAttachments(obj);
1089     }
1090     mFilesModelForRoom->setLoadMoreFilesInProgress(false);
1091 }
1092 
1093 void RocketChatAccount::loadMoreUsersInRoom(const QString &roomId, Room::RoomType channelType)
1094 {
1095     UsersForRoomModel *usersModelForRoom = roomModel()->usersModelForRoom(roomId);
1096     const int offset = usersModelForRoom->usersCount();
1097     if (offset < usersModelForRoom->total()) {
1098         usersModelForRoom->setLoadMoreUsersInProgress(true);
1099         restApi()->membersInRoom(roomId, Room::roomFromRoomType(channelType), offset, qMin(50, usersModelForRoom->total() - offset));
1100     }
1101 }
1102 
1103 void RocketChatAccount::getMentionsMessages(const QString &roomId)
1104 {
1105     mListMessageModel->clear();
1106     mListMessageModel->setRoomId(roomId);
1107     mListMessageModel->setLoadMoreListMessagesInProgress(true);
1108     restApi()->getMentionedMessages(roomId);
1109 }
1110 
1111 void RocketChatAccount::getPinnedMessages(const QString &roomId)
1112 {
1113     mListMessageModel->clear();
1114     mListMessageModel->setLoadMoreListMessagesInProgress(true);
1115     mListMessageModel->setRoomId(roomId);
1116     restApi()->getPinnedMessages(roomId);
1117 }
1118 
1119 void RocketChatAccount::getStarredMessages(const QString &roomId)
1120 {
1121     mListMessageModel->clear();
1122     mListMessageModel->setRoomId(roomId);
1123     mListMessageModel->setLoadMoreListMessagesInProgress(true);
1124     restApi()->getStarredMessages(roomId);
1125 }
1126 
1127 void RocketChatAccount::loadMoreFileAttachments(const QString &roomId, Room::RoomType channelType)
1128 {
1129     if (!mFilesModelForRoom->loadMoreFilesInProgress()) {
1130         const int offset = mFilesModelForRoom->fileAttachments()->filesCount();
1131         if (offset < mFilesModelForRoom->fileAttachments()->total()) {
1132             mFilesModelForRoom->setLoadMoreFilesInProgress(true);
1133             restApi()->filesInRoom(roomId, Room::roomFromRoomType(channelType), offset, qMin(50, mFilesModelForRoom->fileAttachments()->total() - offset));
1134         }
1135     }
1136 }
1137 
1138 void RocketChatAccount::loadMoreDiscussions(const QString &roomId)
1139 {
1140     if (!mDiscussionsModel->loadMoreDiscussionsInProgress()) {
1141         const int offset = mDiscussionsModel->discussions()->discussionsCount();
1142         if (offset < mDiscussionsModel->discussions()->total()) {
1143             mDiscussionsModel->setLoadMoreDiscussionsInProgress(true);
1144             restApi()->getDiscussions(roomId, offset, qMin(50, mDiscussionsModel->discussions()->total() - offset));
1145         }
1146     }
1147 }
1148 
1149 void RocketChatAccount::updateThreadMessageList(const Message &m)
1150 {
1151     if (mThreadMessageModel->threadMessageId() == m.threadMessageId()) {
1152         mThreadMessageModel->addMessages({m});
1153     }
1154 }
1155 
1156 void RocketChatAccount::getListMessages(const QString &roomId, ListMessagesModel::ListMessageType type)
1157 {
1158     mListMessageModel->setListMessageType(type);
1159     mListMessageModel->setLoadMoreListMessagesInProgress(true);
1160     switch (type) {
1161     case ListMessagesModel::Unknown:
1162         qCWarning(RUQOLA_LOG) << " Error when using getListMessages";
1163         break;
1164     case ListMessagesModel::StarredMessages:
1165         getStarredMessages(roomId);
1166         break;
1167     case ListMessagesModel::PinnedMessages:
1168         getPinnedMessages(roomId);
1169         break;
1170     case ListMessagesModel::MentionsMessages:
1171         getMentionsMessages(roomId);
1172         break;
1173     case ListMessagesModel::ThreadsMessages:
1174         threadsInRoom(roomId, false);
1175         break;
1176     case ListMessagesModel::UnreadThreadsMessages:
1177         threadsInRoom(roomId, true);
1178         break;
1179     }
1180 }
1181 
1182 void RocketChatAccount::setNameChanged(const QJsonArray &array)
1183 {
1184     qCWarning(RUQOLA_LOG) << "Need to implement: Users:NameChanged :" << array << " account name " << accountName();
1185     // QJsonArray([{"_id":"Z5TPBsWrmjAWCKGBC","name":"LifeLine","username":"LifeLine-GM"}])
1186     for (int i = 0; i < array.count(); ++i) {
1187         const QJsonObject obj = array.at(i).toObject();
1188         const QString id = obj[QLatin1String("_id")].toString();
1189         const QString name = obj[QLatin1String("name")].toString();
1190         const QString username = obj[QLatin1String("username")].toString();
1191         // TODO
1192     }
1193 }
1194 
1195 void RocketChatAccount::setOwnStatus(const User &user)
1196 {
1197     userStatusChanged(user);
1198 }
1199 
1200 void RocketChatAccount::setUserStatusChanged(const QJsonArray &array)
1201 {
1202     // qDebug() << "Account Name : " << accountName() << " status changed: " << array << " array " << array.count() << " array" << array.toVariantList();
1203     const auto list = array.toVariantList();
1204     for (const auto &var : list) {
1205         const auto userListArguments = var.toJsonArray().toVariantList();
1206         User user;
1207         user.parseUser(userListArguments);
1208         if (user.isValid()) {
1209             userStatusChanged(user);
1210             // qDebug() << " user status changed " << user;
1211         }
1212     }
1213 }
1214 
1215 void RocketChatAccount::setShowRoomAvatar(bool checked)
1216 {
1217     RocketChatRestApi::UsersSetPreferencesJob::UsersSetPreferencesInfo info;
1218     info.userId = userId();
1219     info.sidebarDisplayAvatar = RocketChatRestApi::UsersSetPreferencesJob::UsersSetPreferencesInfo::convertToState(checked);
1220     setUserPreferences(info);
1221 }
1222 
1223 void RocketChatAccount::setShowFavoriteRoom(bool checked)
1224 {
1225     RocketChatRestApi::UsersSetPreferencesJob::UsersSetPreferencesInfo info;
1226     info.userId = userId();
1227     info.sidebarShowFavorites = RocketChatRestApi::UsersSetPreferencesJob::UsersSetPreferencesInfo::convertToState(checked);
1228     setUserPreferences(info);
1229 }
1230 
1231 void RocketChatAccount::loadMoreListMessages(const QString &roomId)
1232 {
1233     if (!mListMessageModel->loadMoreListMessagesInProgress()) {
1234         const int offset = mListMessageModel->rowCount();
1235         if (offset < mListMessageModel->total()) {
1236             mListMessageModel->setLoadMoreListMessagesInProgress(true);
1237             switch (mListMessageModel->listMessageType()) {
1238             case ListMessagesModel::Unknown:
1239                 qCWarning(RUQOLA_LOG) << " Error when using loadMoreListMessages";
1240                 break;
1241             case ListMessagesModel::StarredMessages:
1242                 restApi()->getStarredMessages(roomId, offset, qMin(50, mListMessageModel->total() - offset));
1243                 break;
1244             case ListMessagesModel::PinnedMessages:
1245                 restApi()->getPinnedMessages(roomId, offset, qMin(50, mListMessageModel->total() - offset));
1246                 break;
1247             case ListMessagesModel::MentionsMessages:
1248                 restApi()->getMentionedMessages(roomId, offset, qMin(50, mListMessageModel->total() - offset));
1249                 break;
1250             case ListMessagesModel::ThreadsMessages:
1251                 restApi()->getThreadsList(roomId, false, offset, qMin(50, mListMessageModel->total() - offset));
1252                 break;
1253             case ListMessagesModel::UnreadThreadsMessages:
1254                 restApi()->getThreadsList(roomId, true, offset, qMin(50, mListMessageModel->total() - offset));
1255                 break;
1256             }
1257         }
1258     }
1259 }
1260 
1261 void RocketChatAccount::loadThreadMessagesHistory(const QString &threadMessageId)
1262 {
1263     restApi()->getThreadMessages(threadMessageId);
1264 }
1265 
1266 void RocketChatAccount::createJitsiConfCall(const QString &roomId)
1267 {
1268     // TODO use restapi
1269     ddp()->createJitsiConfCall(roomId);
1270     joinJitsiConfCall(roomId);
1271 }
1272 
1273 void RocketChatAccount::addUserToRoom(const QString &userId, const QString &roomId, Room::RoomType channelType)
1274 {
1275     switch (channelType) {
1276     case Room::RoomType::Private:
1277         restApi()->addUserInGroup(roomId, userId);
1278         break;
1279     case Room::RoomType::Channel:
1280         restApi()->addUserInChannel(roomId, userId);
1281         break;
1282     case Room::RoomType::Direct:
1283     case Room::RoomType::Unknown:
1284         break;
1285     }
1286 }
1287 
1288 void RocketChatAccount::clearSearchModel()
1289 {
1290     mSearchMessageModel->clearModel();
1291 }
1292 
1293 void RocketChatAccount::messageSearch(const QString &pattern, const QString &rid, bool userRegularExpression)
1294 {
1295     if (pattern.isEmpty()) {
1296         clearSearchModel();
1297     } else {
1298         mSearchMessageModel->setLoadCommonMessagesInProgress(true);
1299         restApi()->searchMessages(rid, pattern, userRegularExpression);
1300     }
1301 }
1302 
1303 InputTextManager *RocketChatAccount::inputTextManager() const
1304 {
1305     return mInputTextManager;
1306 }
1307 
1308 InputTextManager *RocketChatAccount::inputThreadMessageTextManager() const
1309 {
1310     return mInputThreadMessageTextManager;
1311 }
1312 
1313 void RocketChatAccount::slotSearchMessages(const QJsonObject &obj)
1314 {
1315     mSearchMessageModel->setLoadCommonMessagesInProgress(false);
1316     mSearchMessageModel->parse(obj);
1317 }
1318 
1319 void RocketChatAccount::starMessage(const QString &messageId, bool starred)
1320 {
1321     restApi()->starMessage(messageId, starred);
1322 }
1323 
1324 void RocketChatAccount::pinMessage(const QString &messageId, bool pinned)
1325 {
1326     restApi()->pinMessage(messageId, pinned);
1327 }
1328 
1329 void RocketChatAccount::reportMessage(const QString &messageId, const QString &message)
1330 {
1331     restApi()->reportMessage(messageId, message);
1332 }
1333 
1334 void RocketChatAccount::getThreadMessages(const QString &threadMessageId, const Message &message)
1335 {
1336     // mListMessageModel->clear();
1337     mThreadMessageModel->setPreviewMessage(message);
1338     restApi()->getThreadMessages(threadMessageId);
1339 }
1340 
1341 void RocketChatAccount::changeNotificationsSettings(const QString &roomId,
1342                                                     RocketChatAccount::NotificationOptionsType notificationsType,
1343                                                     const QVariant &newValue)
1344 {
1345     switch (notificationsType) {
1346     case DisableNotifications:
1347         restApi()->disableNotifications(roomId, newValue.toBool());
1348         break;
1349     case HideUnreadStatus:
1350         restApi()->hideUnreadStatus(roomId, newValue.toBool());
1351         break;
1352     case DesktopNotifications:
1353         restApi()->desktopNotifications(roomId, newValue.toString());
1354         break;
1355     case EmailNotifications:
1356         restApi()->emailNotifications(roomId, newValue.toString());
1357         break;
1358     case MobilePushNotifications:
1359         restApi()->mobilePushNotifications(roomId, newValue.toString());
1360         break;
1361     case UnreadAlert:
1362         restApi()->unreadAlert(roomId, newValue.toString());
1363         break;
1364     case MuteGroupMentions:
1365         restApi()->muteGroupMentions(roomId, newValue.toBool());
1366         break;
1367     case DesktopDurationNotifications:
1368         restApi()->desktopDurationNotifications(roomId, newValue.toInt());
1369         break;
1370     case DesktopSoundNotifications:
1371         restApi()->desktopSoundNotifications(roomId, newValue.toString());
1372         break;
1373     case HideMentionStatus:
1374         restApi()->hideMentionStatus(roomId, newValue.toBool());
1375         break;
1376     }
1377 }
1378 
1379 void RocketChatAccount::parsePublicSettings(const QJsonObject &obj, bool update)
1380 {
1381     mRuqolaServerConfig->parsePublicSettings(obj, update);
1382     parsePublicSettings();
1383 }
1384 
1385 void RocketChatAccount::parsePublicSettings()
1386 {
1387     localDatabaseManager()->updateAccount(accountName(), mRuqolaServerConfig->serialize(false), LocalDatabaseUtils::currentTimeStamp());
1388 
1389     fillOauthModel();
1390     // Download logo/favicon if possible
1391     (void)faviconLogoUrlFromLocalCache(mRuqolaServerConfig->logoUrl().url);
1392     (void)faviconLogoUrlFromLocalCache(mRuqolaServerConfig->faviconUrl().url);
1393 
1394     Q_EMIT publicSettingChanged();
1395 }
1396 
1397 void RocketChatAccount::fillOauthModel()
1398 {
1399     QVector<AuthenticationInfo> fillModel;
1400     for (int i = 0, total = mLstInfos.count(); i < total; ++i) {
1401         if (mRuqolaServerConfig->canShowOauthService(mLstInfos.at(i).oauthType())) {
1402             fillModel.append(mLstInfos.at(i));
1403         }
1404     }
1405     mLoginMethodModel->setAuthenticationInfos(fillModel);
1406 }
1407 
1408 void RocketChatAccount::changeDefaultAuthentication(int index)
1409 {
1410     setDefaultAuthentication(mLoginMethodModel->loginType(index));
1411 }
1412 
1413 void RocketChatAccount::setDefaultAuthentication(AuthenticationManager::OauthType type)
1414 {
1415     PluginAuthenticationInterface *interface = mLstPluginAuthenticationInterface.value(type);
1416     if (interface) {
1417         mDefaultAuthenticationInterface = interface;
1418     } else {
1419         qCWarning(RUQOLA_LOG) << "No interface defined for  " << type;
1420     }
1421 }
1422 
1423 CommonMessageFilterProxyModel *RocketChatAccount::searchMessageFilterProxyModel() const
1424 {
1425     return mSearchMessageFilterProxyModel;
1426 }
1427 
1428 CommonMessagesModel *RocketChatAccount::searchMessageModel() const
1429 {
1430     return mSearchMessageModel;
1431 }
1432 
1433 void RocketChatAccount::initializeAuthenticationPlugins()
1434 {
1435     // TODO change it when we change server
1436     // Clean up at the end.
1437     const QVector<PluginAuthentication *> lstPlugins = AuthenticationManager::self()->pluginsList();
1438     qCDebug(RUQOLA_LOG) << " void RocketChatAccount::initializeAuthenticationPlugins()" << lstPlugins.count();
1439     if (lstPlugins.isEmpty()) {
1440         qCWarning(RUQOLA_LOG) << " No plugins loaded. Please verify your installation.";
1441         ddp()->authenticationManager()->setLoginStatus(DDPAuthenticationManager::FailedToLoginPluginProblem);
1442         return;
1443     }
1444     mLstPluginAuthenticationInterface.clear();
1445 
1446     mLstInfos.clear();
1447     for (PluginAuthentication *abstractPlugin : lstPlugins) {
1448         AuthenticationInfo info;
1449         info.setIconName(abstractPlugin->iconName());
1450         info.setName(abstractPlugin->name());
1451         info.setOauthType(abstractPlugin->type());
1452         if (info.isValid()) {
1453             mLstInfos.append(std::move(info));
1454         }
1455 
1456         PluginAuthenticationInterface *interface = abstractPlugin->createInterface(this);
1457         interface->setAccount(this);
1458         mRuqolaServerConfig->addRuqolaAuthenticationSupport(abstractPlugin->type());
1459         mLstPluginAuthenticationInterface.insert(abstractPlugin->type(), interface);
1460         // For the moment initialize default interface
1461         if (abstractPlugin->type() == AuthenticationManager::OauthType::Password) {
1462             mDefaultAuthenticationInterface = interface;
1463         }
1464         qCDebug(RUQOLA_LOG) << " plugin type " << abstractPlugin->type();
1465     }
1466     // TODO fill ??? or store QVector<AuthenticationInfo>
1467 }
1468 
1469 PluginAuthenticationInterface *RocketChatAccount::defaultAuthenticationInterface() const
1470 {
1471     return mDefaultAuthenticationInterface;
1472 }
1473 
1474 LoginMethodModel *RocketChatAccount::loginMethodModel() const
1475 {
1476     return mLoginMethodModel;
1477 }
1478 
1479 QString RocketChatAccount::authToken() const
1480 {
1481     return settings()->authToken();
1482 }
1483 
1484 QString RocketChatAccount::userName() const
1485 {
1486     return settings()->userName();
1487 }
1488 
1489 void RocketChatAccount::setAccountName(const QString &accountname)
1490 {
1491     // Initialize new account room
1492     // qDebug() << "void RocketChatAccount::setAccountName(const QString &accountname)"<<accountname;
1493     loadSettings(ManagerDataPaths::self()->accountConfigFileName(accountname));
1494     settings()->setAccountName(accountname);
1495 }
1496 
1497 QString RocketChatAccount::accountName() const
1498 {
1499     return settings()->accountName();
1500 }
1501 
1502 QString RocketChatAccount::displayName() const
1503 {
1504     return settings()->displayName();
1505 }
1506 
1507 void RocketChatAccount::addUpdateEmojiCustomList(const QJsonArray &replyArray)
1508 {
1509     mEmojiManager->addUpdateEmojiCustomList(replyArray);
1510 }
1511 
1512 void RocketChatAccount::deleteEmojiCustom(const QJsonArray &replyArray)
1513 {
1514     mEmojiManager->deleteEmojiCustom(replyArray);
1515 }
1516 
1517 void RocketChatAccount::privateSettingsUpdated(const QJsonArray &replyArray)
1518 {
1519     mRuqolaServerConfig->privateSettingsUpdated(replyArray);
1520     localDatabaseManager()->updateAccount(accountName(), mRuqolaServerConfig->serialize(false), LocalDatabaseUtils::currentTimeStamp());
1521     Q_EMIT privateSettingsChanged();
1522 }
1523 
1524 void RocketChatAccount::permissionUpdated(const QJsonArray &replyArray)
1525 {
1526     if (mPermissionManager.updatePermission(replyArray)) {
1527         Q_EMIT permissionChanged();
1528     }
1529     // TODO stockage roles! Load it before permission!
1530     // QJsonObject({"args":["updated",{"_id":"access-mailer","_updatedAt":{"$date":1634569746270},"roles":["admin","vFXCWG9trXLti6xQm"]}],"eventName":"permissions-changed"})
1531 }
1532 
1533 const QVector<RoleInfo> &RocketChatAccount::roleInfo() const
1534 {
1535     return mRolesManager.roleInfo();
1536 }
1537 
1538 void RocketChatAccount::deleteCustomSound(const QJsonArray &replyArray)
1539 {
1540     mCustomSoundManager->deleteCustomSounds(replyArray);
1541 }
1542 
1543 void RocketChatAccount::updateRoles(const QJsonArray &contents)
1544 {
1545     mRolesManager.updateRoles(contents);
1546 }
1547 
1548 void RocketChatAccount::addStdoutInfo(const QJsonArray &contents)
1549 {
1550     const auto count{contents.count()};
1551     for (auto i = 0; i < count; ++i) {
1552         const QJsonObject obj = contents.at(i).toObject();
1553         const QString infoStr = obj[QLatin1String("string")].toString();
1554         // qDebug() << " infoStr " << infoStr;
1555         Q_EMIT insertStdOutInfo(infoStr);
1556     }
1557 }
1558 
1559 void RocketChatAccount::updateCustomSound(const QJsonArray &replyArray)
1560 {
1561     mCustomSoundManager->updateCustomSounds(replyArray);
1562 }
1563 
1564 void RocketChatAccount::deleteUser(const QJsonArray &replyArray)
1565 {
1566     qDebug() << " void RocketChatAccount::deleteUser(const QJsonObject &replyObject)" << replyArray;
1567 }
1568 
1569 void RocketChatAccount::deleteCustomUserStatus(const QJsonArray &replyArray)
1570 {
1571     qDebug() << " void RocketChatAccount::deleteCustomUserStatus(const QJsonObject &replyObject)" << replyArray;
1572     mCustomUserStatuses.deleteCustomUserStatuses(replyArray);
1573     Q_EMIT customUserStatusChanged();
1574 }
1575 
1576 void RocketChatAccount::updateCustomUserStatus(const QJsonArray &replyArray)
1577 {
1578     mCustomUserStatuses.updateCustomUserStatues(replyArray);
1579     Q_EMIT customUserStatusChanged();
1580     qDebug() << " void RocketChatAccount::updateCustomUserStatus(const QJsonObject &replyObject)" << replyArray;
1581 }
1582 
1583 void RocketChatAccount::setDisplayName(const QString &displayName)
1584 {
1585     settings()->setDisplayName(displayName);
1586 }
1587 
1588 QString RocketChatAccount::userId() const
1589 {
1590     return settings()->userId();
1591 }
1592 
1593 QString RocketChatAccount::password() const
1594 {
1595     return settings()->password();
1596 }
1597 
1598 QString RocketChatAccount::twoFactorAuthenticationCode() const
1599 {
1600     return settings()->twoFactorAuthenticationCode();
1601 }
1602 
1603 void RocketChatAccount::setAuthToken(const QString &token)
1604 {
1605     settings()->setAuthToken(token);
1606 }
1607 
1608 void RocketChatAccount::setPassword(const QString &password)
1609 {
1610     settings()->setPassword(password);
1611 }
1612 
1613 void RocketChatAccount::setTwoFactorAuthenticationCode(const QString &twoFactorAuthenticationCode)
1614 {
1615     settings()->setTwoFactorAuthenticationCode(twoFactorAuthenticationCode);
1616 }
1617 
1618 void RocketChatAccount::setAccountEnabled(bool enabled)
1619 {
1620     settings()->setAccountEnabled(enabled);
1621 }
1622 
1623 void RocketChatAccount::setUserName(const QString &username)
1624 {
1625     settings()->setUserName(username);
1626 }
1627 
1628 bool RocketChatAccount::accountEnabled() const
1629 {
1630     return settings()->accountEnabled();
1631 }
1632 
1633 void RocketChatAccount::setUserId(const QString &userID)
1634 {
1635     settings()->setUserId(userID);
1636 }
1637 
1638 QString RocketChatAccount::serverUrl() const
1639 {
1640     return settings()->serverUrl();
1641 }
1642 
1643 void RocketChatAccount::setServerUrl(const QString &serverURL)
1644 {
1645     settings()->setServerUrl(serverURL);
1646     restApi()->setServerUrl(serverURL);
1647     mEmojiManager->setServerUrl(serverURL);
1648 }
1649 
1650 QUrl RocketChatAccount::urlForLink(const QString &link) const
1651 {
1652     if (link.startsWith(QLatin1String("https:")) || link.startsWith(QLatin1String("http:"))) {
1653         return QUrl(link);
1654     }
1655     QString tmpUrl = settings()->serverUrl();
1656     if (!tmpUrl.startsWith(QLatin1String("https://"))) {
1657         tmpUrl = QLatin1String("https://") + tmpUrl;
1658     }
1659     if (!link.startsWith(QLatin1Char('/'))) {
1660         tmpUrl += QLatin1Char('/');
1661     }
1662     const QUrl downloadFileUrl = QUrl::fromUserInput(tmpUrl + link);
1663     return downloadFileUrl;
1664 }
1665 
1666 QString RocketChatAccount::recordingVideoPath() const
1667 {
1668     return mCache->recordingVideoPath(accountName());
1669 }
1670 
1671 QString RocketChatAccount::recordingImagePath() const
1672 {
1673     return mCache->recordingImagePath(accountName());
1674 }
1675 
1676 void RocketChatAccount::downloadFile(const QString &downloadFileUrl, const QUrl &localFile)
1677 {
1678     mCache->downloadFile(downloadFileUrl, localFile);
1679 }
1680 
1681 QUrl RocketChatAccount::faviconLogoUrlFromLocalCache(const QString &url)
1682 {
1683     return mCache->faviconLogoUrlFromLocalCache(url);
1684 }
1685 
1686 QUrl RocketChatAccount::attachmentUrlFromLocalCache(const QString &url)
1687 {
1688     return mCache->attachmentUrlFromLocalCache(url);
1689 }
1690 
1691 bool RocketChatAccount::attachmentIsInLocalCache(const QString &url)
1692 {
1693     return mCache->attachmentIsInLocalCache(url);
1694 }
1695 
1696 void RocketChatAccount::loadHistory(const QString &roomID, bool initial, qint64 timeStamp)
1697 {
1698     MessagesModel *roomModel = messageModelForRoom(roomID);
1699     if (roomModel) {
1700         Room *room = mRoomModel->findRoom(roomID);
1701         // qDebug() << " room->numberMessages() " << room->numberMessages() << " roomModel->rowCount() " << roomModel->rowCount();
1702         if (!initial && (room->numberMessages() == roomModel->rowCount())) {
1703             return;
1704         }
1705         ManageLocalDatabase::ManageLoadHistoryInfo info;
1706         info.roomModel = roomModel;
1707         info.roomId = roomID;
1708         info.initial = initial;
1709         info.timeStamp = timeStamp;
1710         info.roomName = room->displayFName();
1711         info.lastSeenAt = room->lastSeenAt();
1712         mManageLoadHistory->loadMessagesHistory(info);
1713     } else {
1714         qCWarning(RUQOLA_LOG) << "Room is not found " << roomID;
1715     }
1716 }
1717 
1718 void RocketChatAccount::loadAccountSettings()
1719 {
1720     mManageLoadHistory->loadAccountSettings();
1721 }
1722 
1723 void RocketChatAccount::setServerVersion(const QString &version)
1724 {
1725     qCDebug(RUQOLA_LOG) << " void RocketChatAccount::setServerVersion(const QString &version)" << version;
1726     mRuqolaServerConfig->setServerVersion(version);
1727     Q_EMIT serverVersionChanged();
1728 }
1729 
1730 QString RocketChatAccount::serverVersion() const
1731 {
1732     return mRuqolaServerConfig->serverVersion();
1733 }
1734 
1735 bool RocketChatAccount::needAdaptNewSubscriptionRC60() const
1736 {
1737     return mRuqolaServerConfig->needAdaptNewSubscriptionRC60();
1738 }
1739 
1740 bool RocketChatAccount::otrEnabled() const
1741 {
1742     return mRuqolaServerConfig->serverConfigFeatureTypes() & RuqolaServerConfig::ServerConfigFeatureType::OtrEnabled;
1743 }
1744 
1745 bool RocketChatAccount::allowProfileChange() const
1746 {
1747     return mRuqolaServerConfig->serverConfigFeatureTypes() & RuqolaServerConfig::ServerConfigFeatureType::AllowUserProfileChange;
1748 }
1749 
1750 void RocketChatAccount::enable2FaEmailJob(bool enable)
1751 {
1752     restApi()->enable2FaEmailJob(enable);
1753 }
1754 
1755 bool RocketChatAccount::allowMessagePinningEnabled() const
1756 {
1757     return mRuqolaServerConfig->serverConfigFeatureTypes() & RuqolaServerConfig::ServerConfigFeatureType::AllowMessagePinning;
1758 }
1759 
1760 bool RocketChatAccount::allowMessageStarringEnabled() const
1761 {
1762     return mRuqolaServerConfig->serverConfigFeatureTypes() & RuqolaServerConfig::ServerConfigFeatureType::AllowMessageStarring;
1763 }
1764 
1765 bool RocketChatAccount::allowMessageDeletingEnabled() const
1766 {
1767     return mRuqolaServerConfig->serverConfigFeatureTypes() & RuqolaServerConfig::ServerConfigFeatureType::AllowMessageDeleting;
1768 }
1769 
1770 bool RocketChatAccount::threadsEnabled() const
1771 {
1772     return mRuqolaServerConfig->serverConfigFeatureTypes() & RuqolaServerConfig::ServerConfigFeatureType::ThreadsEnabled;
1773 }
1774 
1775 bool RocketChatAccount::autoTranslateEnabled() const
1776 {
1777     return mRuqolaServerConfig->serverConfigFeatureTypes() & RuqolaServerConfig::ServerConfigFeatureType::AutoTranslateEnabled;
1778 }
1779 
1780 bool RocketChatAccount::encryptionEnabled() const
1781 {
1782     return mRuqolaServerConfig->serverConfigFeatureTypes() & RuqolaServerConfig::ServerConfigFeatureType::EncryptionEnabled;
1783 }
1784 
1785 bool RocketChatAccount::twoFactorAuthenticationEnabled() const
1786 {
1787     return mRuqolaServerConfig->serverConfigFeatureTypes() & RuqolaServerConfig::ServerConfigFeatureType::TwoFactorAuthenticationEnabled;
1788 }
1789 
1790 bool RocketChatAccount::twoFactorAuthenticationByEmailEnabled() const
1791 {
1792     return mRuqolaServerConfig->serverConfigFeatureTypes() & RuqolaServerConfig::ServerConfigFeatureType::TwoFactorAuthenticationByEmailEnabled;
1793 }
1794 
1795 bool RocketChatAccount::twoFactorAuthenticationEnforcePasswordFallback() const
1796 {
1797     return mRuqolaServerConfig->serverConfigFeatureTypes() & RuqolaServerConfig::ServerConfigFeatureType::TwoFactorAuthenticationEnforcePasswordFallback;
1798 }
1799 
1800 bool RocketChatAccount::twoFactorAuthenticationByTOTPEnabled() const
1801 {
1802     return mRuqolaServerConfig->serverConfigFeatureTypes() & RuqolaServerConfig::ServerConfigFeatureType::TwoFactorAuthenticationByTOTPEnabled;
1803 }
1804 
1805 bool RocketChatAccount::broadCastEnabled() const
1806 {
1807     return mRuqolaServerConfig->serverConfigFeatureTypes() & RuqolaServerConfig::ServerConfigFeatureType::BroadCastEnabled;
1808 }
1809 
1810 bool RocketChatAccount::registrationFromEnabled() const
1811 {
1812     return mRuqolaServerConfig->serverConfigFeatureTypes() & RuqolaServerConfig::ServerConfigFeatureType::RegistrationFromEnabled;
1813 }
1814 
1815 bool RocketChatAccount::allowDeleteOwnAccount() const
1816 {
1817     return mRuqolaServerConfig->serverConfigFeatureTypes() & RuqolaServerConfig::ServerConfigFeatureType::AllowDeleteOwnAccount;
1818 }
1819 
1820 bool RocketChatAccount::discussionEnabled() const
1821 {
1822     return mRuqolaServerConfig->serverConfigFeatureTypes() & RuqolaServerConfig::ServerConfigFeatureType::DiscussionEnabled;
1823 }
1824 
1825 bool RocketChatAccount::allowAvatarChanged() const
1826 {
1827     return mRuqolaServerConfig->serverConfigFeatureTypes() & RuqolaServerConfig::ServerConfigFeatureType::AllowUserAvatarChange;
1828 }
1829 
1830 bool RocketChatAccount::audioRecorderEnabled() const
1831 {
1832     return mRuqolaServerConfig->serverConfigFeatureTypes() & RuqolaServerConfig::ServerConfigFeatureType::AudioRecorderEnabled;
1833 }
1834 
1835 bool RocketChatAccount::videoRecorderEnabled() const
1836 {
1837     return mRuqolaServerConfig->serverConfigFeatureTypes() & RuqolaServerConfig::ServerConfigFeatureType::VideoRecorderEnabled;
1838 }
1839 
1840 bool RocketChatAccount::teamEnabled() const
1841 {
1842     return mRuqolaServerConfig->hasAtLeastVersion(3, 13, 0) && hasPermission(QStringLiteral("create-team"));
1843 }
1844 
1845 bool RocketChatAccount::ldapEnabled() const
1846 {
1847     return mRuqolaServerConfig->serverConfigFeatureTypes() & RuqolaServerConfig::ServerConfigFeatureType::LdapEnabled;
1848 }
1849 
1850 ServerConfigInfo *RocketChatAccount::serverConfigInfo() const
1851 {
1852     return mServerConfigInfo;
1853 }
1854 
1855 bool RocketChatAccount::jitsiEnabled() const
1856 {
1857     return mRuqolaServerConfig->serverConfigFeatureTypes() & RuqolaServerConfig::ServerConfigFeatureType::JitsiEnabled;
1858 }
1859 
1860 void RocketChatAccount::groupInfo(const QString &roomId)
1861 {
1862     restApi()->groupInfo(roomId);
1863 }
1864 
1865 void RocketChatAccount::switchEditingMode(bool b)
1866 {
1867     if (mEditingMode != b) {
1868         mEditingMode = b;
1869         Q_EMIT editingModeChanged();
1870     }
1871 }
1872 
1873 void RocketChatAccount::setSortUnreadOnTop(bool checked)
1874 {
1875     RocketChatRestApi::UsersSetPreferencesJob::UsersSetPreferencesInfo info;
1876     info.userId = userId();
1877     info.sidebarShowUnread = RocketChatRestApi::UsersSetPreferencesJob::UsersSetPreferencesInfo::convertToState(checked);
1878     setUserPreferences(info);
1879 }
1880 
1881 bool RocketChatAccount::sortUnreadOnTop() const
1882 {
1883     return ownUser().ownUserPreferences().showUnread();
1884 }
1885 
1886 bool RocketChatAccount::sortFavoriteChannels() const
1887 {
1888     return ownUser().ownUserPreferences().showFavorite();
1889 }
1890 
1891 void RocketChatAccount::setRoomListSortOrder(OwnUserPreferences::RoomListSortOrder roomListSortOrder)
1892 {
1893     RocketChatRestApi::UsersSetPreferencesJob::UsersSetPreferencesInfo info;
1894     info.userId = userId();
1895     switch (roomListSortOrder) {
1896     case OwnUserPreferences::RoomListSortOrder::ByLastMessage:
1897         info.sidebarSortby = QStringLiteral("activity");
1898         break;
1899     case OwnUserPreferences::RoomListSortOrder::Alphabetically:
1900         info.sidebarSortby = QStringLiteral("alphabetical");
1901         break;
1902     case OwnUserPreferences::RoomListSortOrder::Unknown:
1903         qCWarning(RUQOLA_LOG) << " OwnUserPreferences::RoomListSortOrder::Unknown is a bug";
1904         return;
1905     }
1906     setUserPreferences(info);
1907 }
1908 
1909 OwnUserPreferences::RoomListSortOrder RocketChatAccount::roomListSortOrder() const
1910 {
1911     return ownUser().ownUserPreferences().roomListSortOrder();
1912 }
1913 
1914 void RocketChatAccount::kickUser(const QString &roomId, const QString &userId, Room::RoomType channelType)
1915 {
1916     switch (channelType) {
1917     case Room::RoomType::Private:
1918         restApi()->groupKick(roomId, userId);
1919         break;
1920     case Room::RoomType::Channel:
1921         restApi()->channelKick(roomId, userId);
1922         break;
1923     case Room::RoomType::Direct:
1924         break;
1925     case Room::RoomType::Unknown:
1926         qCWarning(RUQOLA_LOG) << " unsupported kickUser room for type " << channelType;
1927         break;
1928     }
1929 }
1930 
1931 void RocketChatAccount::rolesInRoom(const QString &roomId, Room::RoomType channelType)
1932 {
1933     switch (channelType) {
1934     case Room::RoomType::Private:
1935         restApi()->getGroupRoles(roomId);
1936         break;
1937     case Room::RoomType::Channel:
1938         restApi()->getChannelRoles(roomId);
1939         break;
1940     case Room::RoomType::Direct:
1941         break;
1942     case Room::RoomType::Unknown:
1943         qCWarning(RUQOLA_LOG) << " unsupported get roles room for type " << channelType;
1944         break;
1945     }
1946 }
1947 
1948 void RocketChatAccount::switchingToRoom(const QString &roomID)
1949 {
1950     clearTypingNotification();
1951     checkInitializedRoom(roomID);
1952 }
1953 
1954 void RocketChatAccount::changeRoles(const QString &roomId, const QString &userId, Room::RoomType channelType, RocketChatAccount::RoleType roleType)
1955 {
1956     switch (channelType) {
1957     case Room::RoomType::Private:
1958         switch (roleType) {
1959         case RocketChatAccount::AddOwner:
1960             restApi()->groupAddOwner(roomId, userId);
1961             break;
1962         case RocketChatAccount::AddLeader:
1963             restApi()->groupAddLeader(roomId, userId);
1964             break;
1965         case RocketChatAccount::AddModerator:
1966             restApi()->groupAddModerator(roomId, userId);
1967             break;
1968         case RocketChatAccount::RemoveOwner:
1969             restApi()->groupRemoveOwner(roomId, userId);
1970             break;
1971         case RocketChatAccount::RemoveLeader:
1972             restApi()->groupRemoveLeader(roomId, userId);
1973             break;
1974         case RocketChatAccount::RemoveModerator:
1975             restApi()->groupRemoveModerator(roomId, userId);
1976             break;
1977         }
1978 
1979         break;
1980     case Room::RoomType::Channel:
1981         switch (roleType) {
1982         case RocketChatAccount::AddOwner:
1983             restApi()->channelAddOwner(roomId, userId);
1984             break;
1985         case RocketChatAccount::AddLeader:
1986             restApi()->channelAddLeader(roomId, userId);
1987             break;
1988         case RocketChatAccount::AddModerator:
1989             restApi()->channelAddModerator(roomId, userId);
1990             break;
1991         case RocketChatAccount::RemoveOwner:
1992             restApi()->channelRemoveOwner(roomId, userId);
1993             break;
1994         case RocketChatAccount::RemoveLeader:
1995             restApi()->channelRemoveLeader(roomId, userId);
1996             break;
1997         case RocketChatAccount::RemoveModerator:
1998             restApi()->channelRemoveModerator(roomId, userId);
1999             break;
2000         }
2001 
2002         break;
2003     case Room::RoomType::Direct:
2004     case Room::RoomType::Unknown:
2005         qCWarning(RUQOLA_LOG) << " unsupported changeRoles room for type " << channelType;
2006         break;
2007     }
2008 }
2009 
2010 void RocketChatAccount::channelInfo(const QString &roomId)
2011 {
2012     restApi()->channelInfo(roomId);
2013 }
2014 
2015 bool RocketChatAccount::allowEditingMessages() const
2016 {
2017     return mRuqolaServerConfig->serverConfigFeatureTypes() & RuqolaServerConfig::ServerConfigFeatureType::AllowEditingMessage;
2018 }
2019 
2020 bool RocketChatAccount::uploadFileEnabled() const
2021 {
2022     return mRuqolaServerConfig->serverConfigFeatureTypes() & RuqolaServerConfig::ServerConfigFeatureType::UploadFileEnabled;
2023 }
2024 
2025 bool RocketChatAccount::isMessageEditable(const Message &message) const
2026 {
2027     if (!allowEditingMessages()) {
2028         return false;
2029     }
2030     if (hasPermission(QStringLiteral("edit-message"))) {
2031         return true;
2032     }
2033     if (message.userId() != userId()) {
2034         return false;
2035     }
2036     if (ruqolaServerConfig()->blockEditingMessageInMinutes() == 0) {
2037         return true;
2038     }
2039     constexpr int minute = 60 * 1000;
2040     return (message.timeStamp() + ruqolaServerConfig()->blockEditingMessageInMinutes() * minute) > QDateTime::currentMSecsSinceEpoch();
2041 }
2042 
2043 bool RocketChatAccount::isMessageDeletable(const Message &message) const
2044 {
2045     if (!allowMessageDeletingEnabled()) {
2046         return false;
2047     }
2048     if (hasPermission(QStringLiteral("force-delete-message"))) {
2049         return true;
2050     }
2051     if (hasPermission(QStringLiteral("delete-message"))) {
2052         return true;
2053     }
2054     if (message.userId() != userId()) {
2055         return false;
2056     }
2057     if (ruqolaServerConfig()->blockDeletingMessageInMinutes() == 0) { // TODO verify it
2058         return true;
2059     }
2060     constexpr int minutes = 60 * 1000;
2061     return (message.timeStamp() + ruqolaServerConfig()->blockDeletingMessageInMinutes() * minutes) > QDateTime::currentMSecsSinceEpoch();
2062 }
2063 
2064 void RocketChatAccount::parseVideoConference(const QJsonArray &contents)
2065 {
2066     qDebug() << " RocketChatAccount::parseVideoConference(const QJsonArray &contents) " << contents;
2067     mVideoConferenceManager->parseVideoConference(contents);
2068 }
2069 
2070 void RocketChatAccount::parseOtr(const QJsonArray &contents)
2071 {
2072     qDebug() << " void RocketChatAccount::parseOtr(const QJsonArray &contents)" << contents << " account name" << accountName();
2073     mOtrManager->parseOtr(contents);
2074 }
2075 
2076 void RocketChatAccount::sendNotification(const QJsonArray &contents)
2077 {
2078     // Conference call
2079     // NOTIFICATION:  QJsonObject({"collection":"stream-notify-user","fields":
2080     // {"args":[{"payload":{"_id":"vD3CXNB2oK5uB8nDT","message":{"msg":"","t":"videoconf"},
2081     // "rid":"YbwG4T2uB3wZSZSKBxkNpoB3T98EEPCj2K","sender":{"_id":"YbwG4T2uB3wZSZSKB",
2082     // "name":"laurent","username":"laurent-montel"},"type":"d"},"text":"","title":"laurent"}],
2083     // "eventName":"xkNpoB3T98EEPCj2K/notification"},"id":"id","msg":"changed"})
2084 
2085     NotificationInfo info;
2086     info.setAccountName(accountName());
2087     info.setDateTime(QDateTime::currentDateTime().toString());
2088     info.parseNotification(contents);
2089     if (!info.isValid()) {
2090         qCWarning(RUQOLA_LOG) << " Info is invalid ! " << contents;
2091     } else {
2092         switch (info.notificationType()) {
2093         case NotificationInfo::StandardMessage: {
2094             const QString iconFileName = mCache->avatarUrlFromCacheOnly(info.senderUserName());
2095             // qDebug() << " iconFileName" << iconFileName << " sender " << info.senderId() << " info.senderUserName() " << info.senderUserName();
2096             QPixmap pix;
2097             if (!iconFileName.isEmpty()) {
2098                 const QUrl url = QUrl::fromLocalFile(iconFileName);
2099                 // qDebug() << "url.toLocalFile()" << url.toLocalFile();
2100                 const bool loaded = pix.load(url.toLocalFile().remove(QStringLiteral("file://")), "JPEG");
2101                 // qDebug() << " load pixmap : " << loaded;
2102                 // qDebug() << " pix " << pix.isNull();
2103                 Q_UNUSED(loaded)
2104                 info.setPixmap(pix);
2105             }
2106             break;
2107         }
2108         case NotificationInfo::ConferenceCall: {
2109             // Nothing
2110             break;
2111         }
2112         }
2113 
2114         Q_EMIT notification(info);
2115     }
2116 }
2117 
2118 void RocketChatAccount::inputAutocomplete(const QString &pattern, const QString &exceptions, InputTextManager::CompletionForType type, bool threadDialog)
2119 {
2120     // TODO look at for restapi support.
2121     switch (type) {
2122     case InputTextManager::CompletionForType::Channel:
2123         ddp()->inputChannelAutocomplete(pattern, exceptions, threadDialog);
2124         break;
2125     case InputTextManager::CompletionForType::User:
2126         ddp()->inputUserAutocomplete(pattern, exceptions, threadDialog);
2127         break;
2128     default:
2129         break;
2130     }
2131 }
2132 
2133 AutotranslateLanguagesModel *RocketChatAccount::autoTranslateLanguagesModel() const
2134 {
2135     return mAutoTranslateLanguagesModel;
2136 }
2137 
2138 void RocketChatAccount::updateUser(const QJsonObject &object)
2139 {
2140     mUserModel->updateUser(object);
2141 }
2142 
2143 void RocketChatAccount::userStatusChanged(const User &user)
2144 {
2145     if (user.userId() == userId()) {
2146         mPresenceStatus = user.status();
2147         statusModel()->setCurrentPresenceStatus(mPresenceStatus);
2148         statusModel()->setCustomText(user.statusText());
2149         Q_EMIT userStatusUpdated(mPresenceStatus, user.statusText(), accountName());
2150     }
2151     mUserModel->addUser(user);
2152     mRoomModel->userStatusChanged(user);
2153 }
2154 
2155 void RocketChatAccount::ignoreUser(const QString &rid, const QString &userId, bool ignore)
2156 {
2157     restApi()->ignoreUser(rid, userId, ignore);
2158 }
2159 
2160 void RocketChatAccount::blockUser(const QString &rid, bool block)
2161 {
2162     // TODO use restapi
2163     if (rid.isEmpty()) {
2164         qCWarning(RUQOLA_LOG) << " void RocketChatAccount::blockUser EMPTY rid ! block " << block;
2165     } else {
2166         // qDebug() << " void RocketChatAccount::blockUser userId " << userId << " block " << block << " rid " << rid << " own userdId" << userID();
2167 
2168         const QString userIdFromDirectChannel = Utils::userIdFromDirectChannel(rid, userId());
2169         if (block) {
2170             ddp()->blockUser(rid, userIdFromDirectChannel);
2171         } else {
2172             ddp()->unBlockUser(rid, userIdFromDirectChannel);
2173         }
2174     }
2175 }
2176 
2177 void RocketChatAccount::clearTypingNotification()
2178 {
2179     mReceiveTypingNotificationManager->clearTypingNotification();
2180 }
2181 
2182 void RocketChatAccount::checkInitializedRoom(const QString &roomId)
2183 {
2184     Room *r = mRoomModel->findRoom(roomId);
2185     if (r && !r->wasInitialized()) {
2186         r->setWasInitialized(true);
2187         ddp()->subscribeRoomMessage(roomId);
2188         if (!r->archived()) {
2189             membersInRoom(r->roomId(), r->channelType());
2190             rolesInRoom(r->roomId(), r->channelType());
2191             if (r->channelType() == Room::RoomType::Channel) {
2192                 restApi()->getChannelsCounter(r->roomId());
2193             }
2194         }
2195         loadHistory(r->roomId(), true /*initial loading*/);
2196     } else if (!r) {
2197         qWarning() << " Room " << roomId << " was no found! Need to open it";
2198         // openDirectChannel(roomId);
2199     }
2200     QMetaObject::invokeMethod(this, &RocketChatAccount::switchedRooms, Qt::QueuedConnection);
2201 }
2202 
2203 void RocketChatAccount::openDocumentation()
2204 {
2205     QDesktopServices::openUrl(QUrl(QStringLiteral("help:/")));
2206 }
2207 
2208 void RocketChatAccount::updateAvatarCache(const Utils::AvatarInfo &info)
2209 {
2210     mCache->updateAvatar(info);
2211 }
2212 
2213 void RocketChatAccount::avatarChanged(const QJsonArray &contents)
2214 {
2215     // qDebug() << " void RocketChatAccount::avatarChanged(const QJsonArray &contents)*******************" << contents;
2216     for (int i = 0; i < contents.count(); ++i) {
2217         const QJsonObject obj = contents.at(i).toObject();
2218         if (obj.contains(QLatin1String("username"))) {
2219             const QString userName = obj[QLatin1String("username")].toString();
2220             Utils::AvatarInfo info;
2221             info.avatarType = Utils::AvatarType::User;
2222             info.identifier = userName;
2223             Q_EMIT avatarWasChanged(info);
2224         } else if (obj.contains(QLatin1String("rid"))) {
2225             const QString roomId = obj[QLatin1String("rid")].toString();
2226             const QString etag = obj[QLatin1String("etag")].toString();
2227             qDebug() << "need to update room avatar " << accountName() << "room" << roomId << "etag " << etag;
2228             Utils::AvatarInfo info;
2229             info.avatarType = Utils::AvatarType::Room;
2230             info.etag = etag; // Etag
2231             info.identifier = roomId; // roomId
2232             Q_EMIT avatarWasChanged(info);
2233         } else {
2234             qWarning() << "avatar changed but missing roomId or userId. It seems to be a regression in RC? " << contents;
2235         }
2236     }
2237 
2238     // TODO parse "QJsonObject({"args":[{"username":"foo"}],"eventName":"updateAvatar"})"
2239 }
2240 
2241 void RocketChatAccount::rolesChanged(const QJsonArray &contents)
2242 {
2243     // TODO verify this code when role change. It seems weird.
2244     for (int i = 0; i < contents.count(); ++i) {
2245         const QJsonObject obj = contents.at(i).toObject();
2246         const QString scope = obj[QLatin1String("scope")].toString();
2247         Room *room = mRoomModel->findRoom(scope);
2248         if (room) {
2249             room->updateRoles(obj);
2250         } else {
2251             qWarning() << " Impossible to find room associate to " << scope << contents;
2252         }
2253     }
2254 }
2255 
2256 void RocketChatAccount::createDiscussion(const QString &parentRoomId,
2257                                          const QString &discussionName,
2258                                          const QString &replyMessage,
2259                                          const QString &messageId,
2260                                          const QStringList &users)
2261 {
2262     restApi()->createDiscussion(parentRoomId, discussionName, replyMessage, messageId, users);
2263 }
2264 
2265 void RocketChatAccount::threadsInRoom(const QString &roomId, bool onlyUnread)
2266 {
2267     if (threadsEnabled()) {
2268         mListMessageModel->clear();
2269         mListMessageModel->setRoomId(roomId);
2270         restApi()->getThreadsList(roomId, onlyUnread);
2271     }
2272 }
2273 
2274 void RocketChatAccount::discussionsInRoom(const QString &roomId)
2275 {
2276     mDiscussionsModel->initialize();
2277     mDiscussionsModel->setLoadMoreDiscussionsInProgress(true);
2278     restApi()->getDiscussions(roomId);
2279 }
2280 
2281 void RocketChatAccount::followMessage(const QString &messageId, bool follow)
2282 {
2283     if (follow) {
2284         restApi()->followMessage(messageId);
2285     } else {
2286         restApi()->unFollowMessage(messageId);
2287     }
2288 }
2289 
2290 void RocketChatAccount::getSupportedLanguages()
2291 {
2292     if (autoTranslateEnabled()) {
2293         bool needTargetLanguage = false;
2294         if (ruqolaServerConfig()->hasAtLeastVersion(5, 1, 0)) {
2295             needTargetLanguage = true;
2296         }
2297         restApi()->getSupportedLanguagesMessages(needTargetLanguage);
2298     } else {
2299         qCWarning(RUQOLA_LOG) << " RocketChatAccount::getSupportedLanguages is not supported before server 2.0.0";
2300     }
2301 }
2302 
2303 void RocketChatAccount::slotGetSupportedLanguagesDone(const QJsonObject &obj)
2304 {
2305     mAutoTranslateLanguagesModel->parseLanguages(obj);
2306 }
2307 
2308 void RocketChatAccount::autoTranslateSaveLanguageSettings(const QString &roomId, const QString &language)
2309 {
2310     restApi()->autoTranslateSaveLanguageSettings(roomId, language);
2311 }
2312 
2313 void RocketChatAccount::autoTranslateSaveAutoTranslateSettings(const QString &roomId, bool autoTranslate)
2314 {
2315     restApi()->autoTranslateSaveAutoTranslateSettings(roomId, autoTranslate);
2316 }
2317 
2318 void RocketChatAccount::slotUsersPresenceDone(const QJsonObject &obj)
2319 {
2320     // qDebug() << " void RocketChatAccount::slotUsersPresenceDone(const QJsonObject &obj)" << obj;
2321     const auto lst = obj.value(QStringLiteral("users")).toArray();
2322     for (const auto &var : lst) {
2323         const QJsonObject userJson = var.toObject();
2324         User user;
2325         user.parseUserRestApi(userJson, roleInfo());
2326         if (user.isValid()) {
2327             userStatusChanged(user);
2328         }
2329     }
2330 }
2331 
2332 void RocketChatAccount::slotReconnectToServer()
2333 {
2334     // This happens when we didn't react to pings for a while
2335     // (e.g. while stopped in gdb, or if network went down for a bit)
2336     // Let's try connecting in again
2337     QTimer::singleShot(mDelayReconnect, this, [this]() {
2338         qCDebug(RUQOLA_RECONNECT_LOG) << "Attempting to reconnect after the server disconnected us: " << accountName();
2339         if (mDelayReconnect == 100) {
2340             mDelayReconnect = 1000;
2341         } else {
2342             mDelayReconnect *= 2;
2343         }
2344         Q_EMIT displayReconnectWidget(mDelayReconnect / 1000);
2345         tryLogin();
2346     });
2347 }
2348 
2349 void RocketChatAccount::usersPresence()
2350 {
2351     restApi()->usersPresence();
2352 }
2353 
2354 void RocketChatAccount::customUsersStatus()
2355 {
2356     restApi()->customUserStatus();
2357 }
2358 
2359 void RocketChatAccount::initializeAccount()
2360 {
2361     listEmojiCustom();
2362 
2363     // load when necessary
2364     usersPresence();
2365     // Force set online.
2366     // TODO don't reset message status !
2367     if (RuqolaGlobalConfig::self()->setOnlineAccounts()) {
2368         ddp()->setDefaultStatus(User::PresenceStatus::PresenceOnline);
2369     }
2370     // Initialize sounds
2371     mCustomSoundManager->initializeDefaultSounds();
2372     ddp()->listCustomSounds();
2373     customUsersStatus();
2374     slotLoadRoles();
2375     if (mRuqolaServerConfig->hasAtLeastVersion(5, 0, 0)) {
2376         checkLicenses();
2377     }
2378 
2379     Q_EMIT accountInitialized();
2380 }
2381 
2382 void RocketChatAccount::checkLicenses()
2383 {
2384     auto job = new RocketChatRestApi::LicensesIsEnterpriseJob(this);
2385     restApi()->initializeRestApiJob(job);
2386     connect(job, &RocketChatRestApi::LicensesIsEnterpriseJob::licensesIsEnterpriseDone, this, [this](bool isEnterprise) {
2387         mRuqolaServerConfig->setHasEnterpriseSupport(isEnterprise);
2388         if (isEnterprise) {
2389             ddp()->licenseGetModules();
2390         }
2391     });
2392     if (!job->start()) {
2393         qCWarning(RUQOLA_LOG) << "Impossible to start LicensesIsEnterpriseJob job";
2394     }
2395 }
2396 
2397 LocalDatabaseManager *RocketChatAccount::localDatabaseManager() const
2398 {
2399     return mLocalDatabaseManager.get();
2400 }
2401 
2402 VideoConferenceMessageInfoManager *RocketChatAccount::videoConferenceMessageInfoManager() const
2403 {
2404     return mVideoConferenceMessageInfoManager;
2405 }
2406 
2407 VideoConferenceManager *RocketChatAccount::videoConferenceManager() const
2408 {
2409     return mVideoConferenceManager;
2410 }
2411 
2412 void RocketChatAccount::downloadAppsLanguages()
2413 {
2414     mDownloadAppsLanguagesManager->setServerVersion(mServerConfigInfo->serverVersionStr());
2415     mDownloadAppsLanguagesManager->setAccountName(mSettings->accountName());
2416     mDownloadAppsLanguagesManager->parse(mSettings->serverUrl());
2417 }
2418 
2419 void RocketChatAccount::slotFileLanguagedParsed()
2420 {
2421     // We need mDownloadAppsLanguagesManager result for updating command
2422     getListCommands();
2423 }
2424 
2425 void RocketChatAccount::getListCommands()
2426 {
2427     restApi()->listCommands();
2428 }
2429 
2430 void RocketChatAccount::slotListCommandDone(const QJsonObject &obj)
2431 {
2432     Commands commands;
2433     commands.setDownloadManager(mDownloadAppsLanguagesManager);
2434     commands.parseCommands(obj, this);
2435     if (!mCommandsModel->commands().isEmpty()) { // Don't show command listview if we already have command (for example when we logout/login)
2436         QSignalBlocker blockSignal(mCommandsModel);
2437         mCommandsModel->setCommands(commands);
2438     } else {
2439         // Initialize it after loading otherwise we will see listview at startup
2440         mCommandsModel->setCommands(commands);
2441         mInputTextManager->setCommandModel(mCommandsModel);
2442         mInputThreadMessageTextManager->setCommandModel(mCommandsModel);
2443     }
2444     // qCDebug(RUQOLA_COMMANDS_LOG) << "accountname " << accountName() << "\nslotListCommandDone " << obj;
2445 }
2446 
2447 bool RocketChatAccount::runCommand(const QString &msg, const QString &roomId, const QString &tmid)
2448 {
2449     const RocketChatRestApi::RunCommandJob::RunCommandInfo info = RocketChatRestApi::RunCommandJob::parseString(msg, roomId, tmid);
2450     if (info.isValid()) {
2451         runCommand(info);
2452         return true;
2453     }
2454     return false;
2455 }
2456 
2457 void RocketChatAccount::runCommand(const RocketChatRestApi::RunCommandJob::RunCommandInfo &runCommandInfo)
2458 {
2459     restApi()->runCommand(runCommandInfo);
2460 }
2461 
2462 User RocketChatAccount::fullUserInfo(const QString &userName) const
2463 {
2464     return mUserModel->fullUserInfo(userName);
2465 }
2466 
2467 void RocketChatAccount::markMessageAsUnReadFrom(const QString &messageId)
2468 {
2469     restApi()->markMessageAsUnReadFrom(messageId);
2470 }
2471 
2472 void RocketChatAccount::markRoomAsUnRead(const QString &roomId)
2473 {
2474     restApi()->markRoomAsUnRead(roomId);
2475 }
2476 
2477 void RocketChatAccount::loginStatusChangedSlot()
2478 {
2479     if (loginStatus() == DDPAuthenticationManager::LoggedOut) {
2480         Q_EMIT logoutDone(accountName());
2481         qCDebug(RUQOLA_LOG) << "Successfully logged out!";
2482     } else if (loginStatus() == DDPAuthenticationManager::LoggedIn) {
2483         // Reset it.
2484         mDelayReconnect = 100;
2485     } else if (loginStatus() == DDPAuthenticationManager::LoginFailedInvalidUserOrPassword) {
2486         // clear auth token to refresh it with the next login
2487         setAuthToken({});
2488     }
2489     Q_EMIT loginStatusChanged();
2490 }
2491 
2492 void RocketChatAccount::sendUserEmailCode()
2493 {
2494     restApi()->sendUserEmailCode(userName());
2495 }
2496 
2497 void RocketChatAccount::requestNewPassword(const QString &email)
2498 {
2499     restApi()->forgotPassword(email);
2500 }
2501 
2502 void RocketChatAccount::registerNewUser(const RocketChatRestApi::RegisterUserJob::RegisterUserInfo &userInfo)
2503 {
2504     restApi()->registerNewUser(userInfo);
2505 }
2506 
2507 void RocketChatAccount::deleteOwnAccount(const QString &password)
2508 {
2509     restApi()->deleteOwnAccount(password);
2510 }
2511 
2512 bool RocketChatAccount::allowEmailChange() const
2513 {
2514     return serverConfigFeatureTypes() & RuqolaServerConfig::ServerConfigFeatureType::AllowEmailChange;
2515 }
2516 
2517 bool RocketChatAccount::allowPasswordChange() const
2518 {
2519     return serverConfigFeatureTypes() & RuqolaServerConfig::ServerConfigFeatureType::AllowPasswordChange;
2520 }
2521 
2522 bool RocketChatAccount::allowPasswordReset() const
2523 {
2524     return serverConfigFeatureTypes() & RuqolaServerConfig::ServerConfigFeatureType::AllowPasswordReset;
2525 }
2526 
2527 bool RocketChatAccount::allowUsernameChange() const
2528 {
2529     return serverConfigFeatureTypes() & RuqolaServerConfig::ServerConfigFeatureType::AllowUsernameChange;
2530 }
2531 
2532 void RocketChatAccount::slotRegisterUserDone()
2533 {
2534     Q_EMIT registerUserSuccess();
2535 }
2536 
2537 void RocketChatAccount::updateOwnBasicInfo(const RocketChatRestApi::UsersUpdateOwnBasicInfoJob::UpdateOwnBasicInfo &info)
2538 {
2539     restApi()->updateOwnBasicInfo(info);
2540 }
2541 
2542 RuqolaServerConfig::ServerConfigFeatureTypes RocketChatAccount::serverConfigFeatureTypes() const
2543 {
2544     return mRuqolaServerConfig->serverConfigFeatureTypes();
2545 }
2546 
2547 void RocketChatAccount::parseOwnInfoDone(const QJsonObject &replyObject)
2548 {
2549     mOwnUser.parseOwnUserInfo(replyObject);
2550     mAwayManager->updateSettings();
2551     mBannerInfos.parseBannerInfos(replyObject);
2552     const User user = mOwnUser.user();
2553     // qDebug() << " USER  " << user;
2554     if (user.isValid()) {
2555         usersModel()->addUser(user);
2556         if (!RuqolaGlobalConfig::self()->setOnlineAccounts()) {
2557             // Need to update own status.
2558             setOwnStatus(user);
2559         }
2560     } else {
2561         qCWarning(RUQOLA_LOG) << " Error during parsing user" << replyObject;
2562     }
2563     downloadAppsLanguages();
2564     Q_EMIT bannerInfoChanged();
2565     Q_EMIT ownInfoChanged();
2566     Q_EMIT ownUserPreferencesChanged();
2567 }
2568 
2569 bool RocketChatAccount::isAdministrator() const
2570 {
2571     return mOwnUser.isAdministrator();
2572 }
2573 
2574 void RocketChatAccount::slotChannelGetCountersDone(const QJsonObject &obj, const RocketChatRestApi::ChannelGroupBaseJob::ChannelGroupInfo &channelInfo)
2575 {
2576     Room *room = mRoomModel->findRoom(channelInfo.identifier);
2577     if (room) {
2578         ChannelCounterInfo info;
2579         info.parseCounterInfo(obj);
2580         room->setChannelCounterInfo(info);
2581     }
2582 }
2583 
2584 void RocketChatAccount::slotMarkAsReadDone(const QString &roomId)
2585 {
2586     Room *room = this->room(roomId);
2587     if (room) {
2588         room->setChannelCounterInfo({});
2589     }
2590 }
2591 
2592 void RocketChatAccount::logoutFromOtherLocation()
2593 {
2594     restApi()->removeOtherTokens();
2595 }
2596 
2597 void RocketChatAccount::createDirectMessages(const QStringList &usernames)
2598 {
2599     restApi()->createDirectMessage(usernames);
2600 }
2601 
2602 void RocketChatAccount::slotCustomUserStatusDone(const QJsonObject &customList)
2603 {
2604     mCustomUserStatuses.parseCustomUserStatuses(customList);
2605     // qDebug() << "customList  " << mCustomUserStatuses;
2606     Q_EMIT customUserStatusChanged();
2607 }
2608 
2609 CustomUserStatuses RocketChatAccount::customUserStatuses() const
2610 {
2611     return mCustomUserStatuses;
2612 }
2613 
2614 void RocketChatAccount::removeCustomUserStatus(const QString &customUserStatusId)
2615 {
2616     restApi()->deleteCustomUserStatus(customUserStatusId);
2617 }
2618 
2619 void RocketChatAccount::updateCustomUserStatus(const RocketChatRestApi::CustomUserStatusUpdateJob::StatusUpdateInfo &statusUpdateInfo)
2620 {
2621     restApi()->updateCustomUserStatus(statusUpdateInfo);
2622 }
2623 
2624 void RocketChatAccount::createCustomUserStatus(const RocketChatRestApi::CustomUserStatusCreateJob::StatusCreateInfo &statusCreateInfo)
2625 {
2626     restApi()->createCustomUserStatus(statusCreateInfo);
2627 }
2628 
2629 void RocketChatAccount::slotPostMessageDone(const QJsonObject &replyObject)
2630 {
2631     // qDebug() << "replyObject " << replyObject;
2632     const QJsonObject roomInfo = replyObject[QLatin1String("message")].toObject();
2633     addMessage(roomInfo, true, true);
2634 }
2635 
2636 void RocketChatAccount::updateUserData(const QJsonArray &contents)
2637 {
2638     qDebug() << " void RocketChatAccount::updateUserData(const QJsonArray &contents)" << contents;
2639     for (const auto &array : contents) {
2640         const QJsonObject updateJson = array[QLatin1String("diff")].toObject();
2641         const QStringList keys = updateJson.keys();
2642         OwnUserPreferences ownUserPreferences = mOwnUser.ownUserPreferences();
2643         for (const QString &key : keys) {
2644             if (key == QLatin1String("settings.preferences.highlights")) {
2645                 const QJsonArray highlightsArray = updateJson.value(key).toArray();
2646                 ownUserPreferences.updateHighlightWords(highlightsArray);
2647                 mOwnUser.setOwnUserPreferences(ownUserPreferences);
2648                 Q_EMIT needUpdateMessageView();
2649             } else if (key == QLatin1String("settings.preferences.enableAutoAway")) {
2650                 ownUserPreferences.setEnableAutoAway(updateJson.value(key).toBool());
2651                 mOwnUser.setOwnUserPreferences(ownUserPreferences);
2652             } else if (key == QLatin1String("settings.preferences.convertAsciiEmoji")) {
2653                 ownUserPreferences.setConvertAsciiEmoji(updateJson.value(key).toBool());
2654                 mOwnUser.setOwnUserPreferences(ownUserPreferences);
2655                 Q_EMIT needUpdateMessageView();
2656             } else if (key == QLatin1String("settings.preferences.hideRoles")) {
2657                 ownUserPreferences.setHideRoles(updateJson.value(key).toBool());
2658                 mOwnUser.setOwnUserPreferences(ownUserPreferences);
2659                 Q_EMIT needUpdateMessageView();
2660             } else if (key == QLatin1String("settings.preferences.displayAvatars")) {
2661                 ownUserPreferences.setDisplayAvatars(updateJson.value(key).toBool());
2662                 mOwnUser.setOwnUserPreferences(ownUserPreferences);
2663                 Q_EMIT needUpdateMessageView();
2664             } else if (key == QLatin1String("settings.preferences.messageViewMode")) {
2665                 ownUserPreferences.setMessageViewMode(updateJson.value(key).toInt());
2666                 mOwnUser.setOwnUserPreferences(ownUserPreferences);
2667                 Q_EMIT needUpdateMessageView();
2668             } else if (key == QLatin1String("settings.preferences.sidebarViewMode")) { // Channel List view mode
2669                 // TODO
2670             } else if (key == QLatin1String("settings.preferences.sidebarShowUnread")) {
2671                 ownUserPreferences.setShowUnread(updateJson.value(key).toBool());
2672                 mOwnUser.setOwnUserPreferences(ownUserPreferences);
2673                 Q_EMIT ownUserPreferencesChanged();
2674             } else if (key == QLatin1String("settings.preferences.sidebarDisplayAvatar")) { // Avatar in channel list view
2675                 ownUserPreferences.setShowRoomAvatar(updateJson.value(key).toBool());
2676                 mOwnUser.setOwnUserPreferences(ownUserPreferences);
2677                 Q_EMIT ownUserPreferencesChanged();
2678             } else if (key == QLatin1String("settings.preferences.sidebarShowFavorites")) {
2679                 ownUserPreferences.setShowFavorite(updateJson.value(key).toBool());
2680                 mOwnUser.setOwnUserPreferences(ownUserPreferences);
2681                 Q_EMIT ownUserPreferencesChanged();
2682             } else if (key == QLatin1String("settings.preferences.sidebarSortby")) {
2683                 const QString value = updateJson.value(key).toString();
2684                 if (value == QLatin1String("activity")) {
2685                     ownUserPreferences.setRoomListSortOrder(OwnUserPreferences::RoomListSortOrder::ByLastMessage);
2686                 } else if (value == QLatin1String("alphabetical")) {
2687                     ownUserPreferences.setRoomListSortOrder(OwnUserPreferences::RoomListSortOrder::Alphabetically);
2688                 } else {
2689                     qCWarning(RUQOLA_LOG) << "Sortby is not defined ?  " << value;
2690                 }
2691                 mOwnUser.setOwnUserPreferences(ownUserPreferences);
2692                 Q_EMIT ownUserPreferencesChanged();
2693             } else {
2694                 const static QRegularExpression bannerRegularExpression(QStringLiteral("banners.(.*).read"));
2695                 QRegularExpressionMatch rmatch;
2696                 if (key.contains(bannerRegularExpression, &rmatch)) {
2697                     if (rmatch.hasMatch()) {
2698                         const QString bannerName = rmatch.captured(1);
2699                         const bool result = updateJson.value(key).toBool();
2700                         mBannerInfos.updateBannerReadInfo(bannerName, result);
2701                     }
2702                 }
2703             }
2704         }
2705     }
2706     // QJsonArray([{"diff":{"_updatedAt":{"$date":1639552419120},"avatarETag":"MCGFkLtBKkhb5GXBj","avatarOrigin":"rest"},"type":"updated","unset":{}}])
2707     // QJsonArray([{"diff":{"_updatedAt":{"$date":1639552237550}},"type":"updated","unset":{"avatarETag":1,"avatarOrigin":1}}])
2708     // QJsonArray([{"diff":{"_updatedAt":{"$date":1639552298748},"nickname":"ss"},"type":"updated","unset":{}}])
2709     // QJsonArray([{"diff":{"_updatedAt":{"$date":1639552390152}},"type":"updated","unset":{"nickname":1}}])
2710     // TODO
2711 }
2712 
2713 void RocketChatAccount::addMessage(const QJsonObject &replyObject, bool useRestApi, bool temporaryMessage)
2714 {
2715     const QString roomId = replyObject.value(QLatin1String("rid")).toString();
2716     if (!roomId.isEmpty()) {
2717         MessagesModel *messageModel = messageModelForRoom(roomId);
2718         if (!messageModel) {
2719             qCWarning(RUQOLA_LOG) << "Unexpected null message model.";
2720             return;
2721         }
2722         Message m(emojiManager());
2723         m.parseMessage(replyObject, useRestApi);
2724         m.setMessageType(Message::MessageType::Information);
2725         m.setPendingMessage(temporaryMessage);
2726         if (!m.threadMessageId().isEmpty()) {
2727             // qDebug() << " It's a thread message id ****************************" << m.threadMessageId();
2728             updateThreadMessageList(m);
2729         }
2730         // m.setMessageType(Message::System);
2731         // TODO add special element!See roomData QJsonObject({"_id":"u9xnnzaBQoQithsxP","msg":"You have been muted and cannot speak in this
2732         // room","rid":"Dic5wZD4Zu9ze5gk3","ts":{"$date":1534166745895}})
2733         // Temporary => we don't add it in database
2734         messageModel->addMessages({m});
2735     } else {
2736         qCWarning(RUQOLA_LOG) << "stream-notify-user : Message: ROOMID is empty ";
2737     }
2738 }
2739 
2740 OwnUserPreferences RocketChatAccount::ownUserPreferences() const
2741 {
2742     return ownUser().ownUserPreferences();
2743 }
2744 
2745 QStringList RocketChatAccount::highlightWords() const
2746 {
2747     return ownUser().ownUserPreferences().highlightWords();
2748 }
2749 
2750 void RocketChatAccount::resetAvatar()
2751 {
2752     RocketChatRestApi::UserBaseJob::UserInfo info;
2753     info.userInfoType = RocketChatRestApi::UserBaseJob::UserInfoType::UserId;
2754     info.userIdentifier = userId();
2755     restApi()->resetAvatar(info);
2756     // TODO update avatar when we reset it.
2757 }
2758 
2759 void RocketChatAccount::setAvatarUrl(const QString &url)
2760 {
2761     RocketChatRestApi::UserBaseJob::UserInfo userInfo;
2762     userInfo.userInfoType = RocketChatRestApi::UserBaseJob::UserInfoType::UserId;
2763     userInfo.userIdentifier = userId();
2764     RocketChatRestApi::SetAvatarJob::SetAvatarInfo avatarInfo;
2765     avatarInfo.mAvatarUrl = url;
2766     restApi()->setAvatar(userInfo, avatarInfo);
2767 }
2768 
2769 void RocketChatAccount::setImageUrl(const QUrl &url)
2770 {
2771     RocketChatRestApi::UserBaseJob::UserInfo userInfo;
2772     userInfo.userInfoType = RocketChatRestApi::UserBaseJob::UserInfoType::UserId;
2773     userInfo.userIdentifier = userId();
2774     RocketChatRestApi::SetAvatarJob::SetAvatarInfo avatarInfo;
2775     avatarInfo.mImageUrl = url;
2776     restApi()->setAvatar(userInfo, avatarInfo);
2777 }
2778 
2779 void RocketChatAccount::exportMessages(const RocketChatRestApi::RoomsExportJob::RoomsExportInfo &info)
2780 {
2781     restApi()->exportMessages(info);
2782 }
2783 
2784 void RocketChatAccount::slotRoomExportDone()
2785 {
2786     auto notification = new KNotification(QStringLiteral("export-message"), KNotification::CloseOnTimeout);
2787     notification->setTitle(i18n("Export Messages"));
2788     notification->setText(i18n("Your email has been queued for sending."));
2789     notification->sendEvent();
2790 }
2791 
2792 void RocketChatAccount::slotPermissionListAllDone(const QJsonObject &replyObject)
2793 {
2794     // qDebug() << accountName() << " replyObject " << replyObject;
2795     mPermissionManager.parsePermissions(replyObject);
2796     Q_EMIT permissionChanged();
2797     // We can't load until permission loaded.
2798     if (hasAutotranslateSupport()) {
2799         getSupportedLanguages();
2800     }
2801 }
2802 
2803 QStringList RocketChatAccount::permissions(const QString &permissionId) const
2804 {
2805     return mPermissionManager.roles(permissionId);
2806 }
2807 
2808 QStringList RocketChatAccount::ownUserPermission() const
2809 {
2810     return mOwnUser.roles();
2811 }
2812 
2813 bool RocketChatAccount::hasPermission(const QString &permissionId) const
2814 {
2815     const QStringList ownUserRoles{mOwnUser.roles()};
2816     const QStringList permissionRoles{mPermissionManager.roles(permissionId)};
2817     if (permissionRoles.isEmpty()) { // Check if we don't have stored permissionId in permission manager
2818         if (!mPermissionManager.contains(permissionId)) {
2819             qCWarning(RUQOLA_LOG) << "permissionId not loaded during setup:" << permissionId;
2820         }
2821     }
2822     for (const QString &role : permissionRoles) {
2823         if (ownUserRoles.contains(role)) {
2824             return true;
2825         }
2826     }
2827     return false;
2828 }
2829 
2830 void RocketChatAccount::setUserPreferences(const RocketChatRestApi::UsersSetPreferencesJob::UsersSetPreferencesInfo &info)
2831 {
2832     restApi()->setUserPreferences(info);
2833 }
2834 
2835 void RocketChatAccount::slotUsersSetPreferencesDone(const QJsonObject &replyObject)
2836 {
2837     // qDebug() << " void RocketChatAccount::slotUsersSetPreferencesDone(const QJsonObject &replyObject)" << replyObject;
2838     const QJsonObject user = replyObject.value(QLatin1String("user")).toObject();
2839     if (user.value(QLatin1String("_id")).toString() == userId()) {
2840         OwnUserPreferences ownUserPreferences;
2841         ownUserPreferences.parsePreferences(user.value(QLatin1String("settings")).toObject().value(QLatin1String("preferences")).toObject());
2842         mOwnUser.setOwnUserPreferences(ownUserPreferences);
2843         Q_EMIT ownUserPreferencesChanged();
2844     }
2845 }
2846 
2847 bool RocketChatAccount::hasAutotranslateSupport() const
2848 {
2849     return autoTranslateEnabled() && hasPermission(QStringLiteral("auto-translate"));
2850 }
2851 
2852 MessageCache *RocketChatAccount::messageCache() const
2853 {
2854     return mMessageCache;
2855 }
2856 
2857 void RocketChatAccount::slotUpdateCustomUserStatus()
2858 {
2859     mStatusModel->updateCustomStatus(mCustomUserStatuses.customUserses());
2860     Q_EMIT customStatusChanged();
2861 }
2862 
2863 bool RocketChatAccount::hideRoles() const
2864 {
2865     return ownUser().ownUserPreferences().hideRoles();
2866 }
2867 
2868 bool RocketChatAccount::displayAvatars() const
2869 {
2870     return ownUser().ownUserPreferences().displayAvatars();
2871 }
2872 
2873 int RocketChatAccount::messageViewMode() const
2874 {
2875     return ownUser().ownUserPreferences().messageViewMode();
2876 }
2877 
2878 void RocketChatAccount::slotLoadRoles()
2879 {
2880     // First load list of roles.
2881     auto job = new RocketChatRestApi::RolesListJob(this);
2882     restApi()->initializeRestApiJob(job);
2883     connect(job, &RocketChatRestApi::RolesListJob::rolesListDone, &mRolesManager, &RolesManager::parseRoles);
2884     if (!job->start()) {
2885         qCWarning(RUQOLA_LOG) << "Impossible to start RolesListJob job";
2886     }
2887 }
2888 
2889 CustomSoundsManager *RocketChatAccount::customSoundManager() const
2890 {
2891     return mCustomSoundManager;
2892 }
2893 
2894 void RocketChatAccount::slotAwayStatusChanged(bool away)
2895 {
2896     if (away) {
2897         restApi()->setUserStatus(userId(), RocketChatRestApi::SetStatusJob::Away, {});
2898     } else {
2899         restApi()->setUserStatus(userId(), RocketChatRestApi::SetStatusJob::OnLine, {});
2900     }
2901     qCDebug(RUQOLA_LOG) << "RocketChatAccount::slotAwayStatusChanged  " << away;
2902 }
2903 
2904 void RocketChatAccount::generate2FaTotp(const QJsonObject &obj)
2905 {
2906     // qDebug() << "RocketChatAccount::generate2FaTotp " << obj;
2907     const QString secret = obj.value(QStringLiteral("secret")).toString();
2908     const QString url = obj.value(QStringLiteral("url")).toString();
2909     Q_EMIT totpResult(secret, url);
2910 }
2911 
2912 void RocketChatAccount::totpDisabledVerify(const QJsonObject &root)
2913 {
2914     const int result = root.value(QStringLiteral("result")).toInt();
2915     Q_EMIT disabledTotpValid(result == 1);
2916 }
2917 
2918 void RocketChatAccount::totpVerify(const QJsonObject &obj)
2919 {
2920     if (obj.isEmpty()) {
2921         Q_EMIT totpInvalid();
2922     } else {
2923         // qDebug() << "totpValid " << obj;
2924         QStringList lstCodes;
2925         const QJsonArray codes = obj.value(QStringLiteral("codes")).toArray();
2926         const auto nbCodes{codes.count()};
2927         lstCodes.reserve(nbCodes);
2928         for (auto i = 0; i < nbCodes; ++i) {
2929             lstCodes.append(codes.at(i).toString());
2930         }
2931         Q_EMIT totpValid(lstCodes);
2932     }
2933 }
2934 
2935 void RocketChatAccount::setOauthAppAdded(const QJsonObject &obj)
2936 {
2937     // TODO return error
2938     Q_EMIT oauthAppAdded(obj);
2939 }
2940 
2941 void RocketChatAccount::setOauthAppUpdated(const QJsonObject &obj)
2942 {
2943     // TODO return error
2944     Q_EMIT oauthAppUpdated(obj);
2945 }
2946 
2947 bool RocketChatAccount::hasLicense(const QString &name)
2948 {
2949     return mLicensesManager.hasLicense(name);
2950 }
2951 
2952 void RocketChatAccount::parseLicenses(const QJsonArray &replyArray)
2953 {
2954     mLicensesManager.parseLicenses(replyArray);
2955 }
2956 
2957 void RocketChatAccount::addMessageToDataBase(const QString &roomName, const Message &message)
2958 {
2959     mLocalDatabaseManager->addMessage(accountName(), roomName, message);
2960 }
2961 
2962 void RocketChatAccount::deleteMessageFromDatabase(const QString &roomName, const QString &messageId)
2963 {
2964     mLocalDatabaseManager->deleteMessage(accountName(), roomName, messageId);
2965 }
2966 
2967 // Only for debugging permissions. (debug mode)
2968 QVector<Permission> RocketChatAccount::permissions() const
2969 {
2970     return mPermissionManager.permissions();
2971 }
2972 
2973 #include "moc_rocketchataccount.cpp"