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"