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