File indexing completed on 2024-05-05 05:02:16

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