File indexing completed on 2024-04-28 16:11:07

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