File indexing completed on 2023-11-26 08:17:53
0001 /* 0002 SPDX-FileCopyrightText: 2018-2023 Laurent Montel <montel@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "rocketchatcache.h" 0008 #include "avatarmanager.h" 0009 #include "connection.h" 0010 #include "managerdatapaths.h" 0011 #include "rocketchataccount.h" 0012 #include "ruqola_debug.h" 0013 #include <QDateTime> 0014 #include <QDir> 0015 #include <QSettings> 0016 #include <QUrlQuery> 0017 0018 RocketChatCache::RocketChatCache(RocketChatAccount *account, QObject *parent) 0019 : QObject(parent) 0020 , mAccount(account) 0021 { 0022 mAvatarManager = new AvatarManager(mAccount, this); 0023 connect(mAvatarManager, &AvatarManager::insertAvatarUrl, this, &RocketChatCache::insertAvatarUrl); 0024 loadAvatarCache(); 0025 } 0026 0027 RocketChatCache::~RocketChatCache() 0028 { 0029 QSettings settings(ManagerDataPaths::self()->accountAvatarConfigPath(mAccount->accountName()), QSettings::IniFormat); 0030 0031 settings.beginGroup(QStringLiteral("Avatar")); 0032 QHash<QString, QUrl>::const_iterator i = mAvatarUrl.constBegin(); 0033 while (i != mAvatarUrl.constEnd()) { 0034 if (!i.value().toString().isEmpty()) { 0035 settings.setValue(i.key(), i.value().toString()); 0036 } 0037 ++i; 0038 } 0039 settings.endGroup(); 0040 } 0041 0042 void RocketChatCache::setRestApiConnection(RocketChatRestApi::Connection *restApi) 0043 { 0044 connect(restApi, &RocketChatRestApi::Connection::downloadFileDone, this, &RocketChatCache::slotDataDownloaded); 0045 } 0046 0047 bool RocketChatCache::fileInCache(const QUrl &url) 0048 { 0049 const QFileInfo f(fileCachePath(url)); 0050 return f.exists(); 0051 } 0052 0053 QString RocketChatCache::fileCachePath(const QUrl &url) 0054 { 0055 QString cachePath = ManagerDataPaths::self()->path(ManagerDataPaths::Cache, mAccount->accountName()); 0056 cachePath += QLatin1Char('/') + url.path(); 0057 if (url.hasQuery()) { 0058 const QUrlQuery query(url); 0059 cachePath += query.queryItemValue(QStringLiteral("etag")); 0060 } 0061 return cachePath; 0062 } 0063 0064 void RocketChatCache::slotDataDownloaded(const QUrl &url, const QUrl &localFileUrl) 0065 { 0066 mFileInDownload.remove(url.path()); 0067 Q_EMIT fileDownloaded(url.path(), localFileUrl); 0068 } 0069 0070 void RocketChatCache::loadAvatarCache() 0071 { 0072 QSettings settings(ManagerDataPaths::self()->accountAvatarConfigPath(mAccount->accountName()), QSettings::IniFormat); 0073 settings.beginGroup(QStringLiteral("Avatar")); 0074 const QStringList keys = settings.childKeys(); 0075 for (const QString &key : keys) { 0076 mAvatarUrl[key] = QUrl(settings.value(key).toString()); 0077 } 0078 settings.endGroup(); 0079 } 0080 0081 void RocketChatCache::downloadFile(const QString &url, const QUrl &localFile) 0082 { 0083 QFile f(fileCachePath(QUrl(url))); 0084 if (f.exists()) { 0085 if (!f.copy(localFile.toLocalFile())) { 0086 qCWarning(RUQOLA_LOG) << "Impossible to copy" << f.fileName() << "to" << localFile; 0087 } 0088 // emit fileDownloaded? 0089 } else { 0090 // Not in cache. We need to download it (e.g. file attachment). 0091 const QUrl downloadUrl = mAccount->urlForLink(url); 0092 // const QUrl destUrl = storeInCache ? QUrl::fromLocalFile(fileCachePath(downloadUrl)) : localFile; 0093 mAccount->restApi()->downloadFile(downloadUrl, localFile, QByteArray("text/plain")); 0094 // this will call slotDataDownloaded 0095 } 0096 } 0097 0098 bool RocketChatCache::attachmentIsInLocalCache(const QString &url) 0099 { 0100 const QString cachePath = fileCachePath(QUrl(url)); 0101 if (QFileInfo::exists(cachePath)) { 0102 return true; 0103 } 0104 return false; 0105 } 0106 0107 QUrl RocketChatCache::faviconLogoUrlFromLocalCache(const QString &url) 0108 { 0109 return urlFromLocalCache(url, false); 0110 } 0111 0112 QUrl RocketChatCache::urlFromLocalCache(const QString &url, bool needAuthentication) 0113 { 0114 const QString cachePath = fileCachePath(QUrl(url)); 0115 if (QFileInfo::exists(cachePath)) { 0116 // QML wants a QUrl here. The widgets code would be simpler with just a QString path. 0117 return QUrl::fromLocalFile(cachePath); 0118 } else { 0119 downloadFileFromServer(url, needAuthentication); 0120 } 0121 return {}; 0122 } 0123 0124 QUrl RocketChatCache::attachmentUrlFromLocalCache(const QString &url) 0125 { 0126 return urlFromLocalCache(url, true); 0127 } 0128 0129 void RocketChatCache::downloadAvatarFromServer(const Utils::AvatarInfo &info) 0130 { 0131 mAvatarManager->insertInDownloadQueue(info); 0132 } 0133 0134 void RocketChatCache::downloadFileFromServer(const QString &filename, bool needAuthentication) 0135 { 0136 if (!mFileInDownload.contains(filename)) { 0137 mFileInDownload.insert(filename); 0138 const QUrl downloadUrl = mAccount->urlForLink(filename); 0139 const QUrl destFileUrl = QUrl::fromLocalFile(fileCachePath(downloadUrl)); 0140 mAccount->restApi()->downloadFile(mAccount->urlForLink(filename), destFileUrl, "text/plain", needAuthentication); 0141 // this will call slotDataDownloaded 0142 } 0143 } 0144 0145 QString RocketChatCache::avatarUrlFromCacheOnly(const QString &userId) 0146 { 0147 const QUrl avatarUrl = mAvatarUrl.value(userId); 0148 if (!avatarUrl.isEmpty() && fileInCache(avatarUrl)) { 0149 const QString url = QUrl::fromLocalFile(fileCachePath(avatarUrl)).toString(); 0150 qCDebug(RUQOLA_LOG) << " Use image in cache" << url << " userId " << userId << " mUserAvatarUrl.value(userId) " << mAvatarUrl.value(userId); 0151 return url; 0152 } 0153 return {}; 0154 } 0155 0156 void RocketChatCache::removeAvatar(const QString &avatarIdentifier) 0157 { 0158 const QUrl avatarUrl = mAvatarUrl.value(avatarIdentifier); 0159 if (avatarUrl.isEmpty()) { 0160 return; 0161 } 0162 QFile f(fileCachePath(avatarUrl)); 0163 if (f.exists()) { 0164 if (!f.remove()) { 0165 qCWarning(RUQOLA_LOG) << "Impossible to remove f" << f.fileName() << " avartarUrl " << avatarUrl << " userIdentifier " << avatarIdentifier; 0166 } 0167 } 0168 } 0169 0170 void RocketChatCache::updateAvatar(const Utils::AvatarInfo &info) 0171 { 0172 const QString avatarIdentifier = info.generateAvatarIdentifier(); 0173 qDebug() << " updateAvatar" << info; 0174 removeAvatar(avatarIdentifier); 0175 mAvatarUrl.remove(avatarIdentifier); 0176 insertAvatarUrl(avatarIdentifier, QUrl()); 0177 downloadAvatarFromServer(info); 0178 } 0179 0180 QString RocketChatCache::avatarUrl(const Utils::AvatarInfo &info) 0181 { 0182 if (!info.isValid()) { 0183 return {}; 0184 } 0185 // const QString avatarIdentifier = info.identifier; 0186 const QString avatarIdentifier = info.generateAvatarIdentifier(); 0187 // qDebug() << " RocketChatCache::avatarUrl " << avatarIdentifier; 0188 // avoid to call this method several time. 0189 if (!mAvatarUrl.contains(avatarIdentifier)) { 0190 insertAvatarUrl(avatarIdentifier, QUrl()); 0191 downloadAvatarFromServer(info); 0192 return {}; 0193 } else { 0194 const QUrl valueUrl = mAvatarUrl.value(avatarIdentifier); 0195 // qDebug() << " valueUrl " << valueUrl; 0196 #if 0 0197 const QUrlQuery query{valueUrl}; 0198 const QString etagValue = query.queryItemValue(QStringLiteral("etag")); 0199 if (info.etag.isEmpty() && !etagValue.isEmpty()) { 0200 insertAvatarUrl(avatarIdentifier, QUrl()); 0201 downloadAvatarFromServer(info); 0202 return {}; 0203 } else if (!info.etag.isEmpty() && etagValue.isEmpty()) { 0204 insertAvatarUrl(avatarIdentifier, QUrl()); 0205 qDebug() << " redownload " << info; 0206 downloadAvatarFromServer(info); 0207 return {}; 0208 } 0209 #endif 0210 0211 if (!valueUrl.isEmpty() && fileInCache(valueUrl)) { 0212 const QString url = QUrl::fromLocalFile(fileCachePath(valueUrl)).toString(); 0213 // qDebug() << " Use image in cache" << url << " userId " << userId << " mUserAvatarUrl.value(userId) "<< mUserAvatarUrl.value(userId); 0214 // qDebug() << "Use image in cache " << url; 0215 0216 return url; 0217 } else { 0218 downloadAvatarFromServer(info); 0219 } 0220 return {}; 0221 } 0222 } 0223 0224 void RocketChatCache::insertAvatarUrl(const QString &userIdentifier, const QUrl &url) 0225 { 0226 mAvatarUrl.insert(userIdentifier, url); 0227 if (!url.isEmpty() && !fileInCache(url)) { 0228 mAccount->restApi()->downloadFile(url, QUrl::fromLocalFile(fileCachePath(url)), {}); 0229 // this will call slotDataDownloaded 0230 } 0231 } 0232 0233 QString RocketChatCache::recordingVideoPath(const QString &accountName) const 0234 { 0235 const QString path = ManagerDataPaths::self()->path(ManagerDataPaths::Video, accountName); 0236 if (!QDir().mkpath(path)) { 0237 qCWarning(RUQOLA_LOG) << "Unable to create folder: " << path; 0238 return {}; 0239 } 0240 const QString filePath = path + QLatin1Char('/') + QString::number(QDateTime::currentDateTime().toMSecsSinceEpoch()) + QStringLiteral(".mp4"); 0241 return filePath; 0242 } 0243 0244 QString RocketChatCache::recordingImagePath(const QString &accountName) const 0245 { 0246 const QString path = ManagerDataPaths::self()->path(ManagerDataPaths::Picture, accountName); 0247 if (!QDir().mkpath(path)) { 0248 qCWarning(RUQOLA_LOG) << "Unable to create folder: " << path; 0249 return {}; 0250 } 0251 const QString filePath = path + QLatin1Char('/') + QString::number(QDateTime::currentDateTime().toMSecsSinceEpoch()) + QStringLiteral(".jpg"); 0252 return filePath; 0253 } 0254 0255 #include "moc_rocketchatcache.cpp"