File indexing completed on 2024-09-15 04:36:26

0001 /*
0002     SPDX-FileCopyrightText: 2011 Volker Krause <vkrause@kde.org>
0003     SPDX-FileCopyrightText: 2018 Daniel Vrátil <dvratil@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "akonadiprivate_debug.h"
0009 #include "instance_p.h"
0010 #include "standarddirs_p.h"
0011 
0012 #include <QCoreApplication>
0013 #include <QDir>
0014 #include <QFile>
0015 #include <QFileInfo>
0016 #include <QList>
0017 #include <QStandardPaths>
0018 
0019 using namespace Akonadi;
0020 
0021 namespace
0022 {
0023 QString buildFullRelPath(const char *resource, const QString &relPath)
0024 {
0025     QString fullRelPath = QStringLiteral("/akonadi");
0026 #ifdef Q_OS_WIN
0027     // On Windows all Generic*Location fall into ~/AppData/Local so we need to disambiguate
0028     // inside the "akonadi" folder whether it's data or config.
0029     fullRelPath += QLatin1Char('/') + QString::fromLocal8Bit(resource);
0030 #else
0031     Q_UNUSED(resource)
0032 #endif
0033 
0034     if (Akonadi::Instance::hasIdentifier()) {
0035         fullRelPath += QLatin1StringView("/instance/") + Akonadi::Instance::identifier();
0036     }
0037     if (!relPath.isEmpty()) {
0038         fullRelPath += QLatin1Char('/') + relPath;
0039     }
0040     return fullRelPath;
0041 }
0042 
0043 } // namespace
0044 
0045 QString StandardDirs::configFile(const QString &configFile, FileAccessMode openMode)
0046 {
0047     const QString savePath = StandardDirs::saveDir("config") + QLatin1Char('/') + configFile;
0048     if (openMode == WriteOnly) {
0049         return savePath;
0050     }
0051 
0052     auto path = QStandardPaths::locate(QStandardPaths::GenericConfigLocation, QLatin1StringView("akonadi/") + configFile);
0053     // HACK: when using instance namespaces, ignore the non-namespaced file
0054     if (Akonadi::Instance::hasIdentifier() && path.startsWith(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation))) {
0055         path.clear();
0056     }
0057 
0058     if (path.isEmpty()) {
0059         return savePath;
0060     } else if (openMode == ReadOnly || path == savePath) {
0061         return path;
0062     }
0063 
0064     // file found in system paths and mode is ReadWrite, thus
0065     // we copy to the home path location and return this path
0066     QFile::copy(path, savePath);
0067     return savePath;
0068 }
0069 
0070 QString StandardDirs::serverConfigFile(FileAccessMode openMode)
0071 {
0072     return configFile(QStringLiteral("akonadiserverrc"), openMode);
0073 }
0074 
0075 QString StandardDirs::connectionConfigFile(FileAccessMode openMode)
0076 {
0077     return configFile(QStringLiteral("akonadiconnectionrc"), openMode);
0078 }
0079 
0080 QString StandardDirs::agentsConfigFile(FileAccessMode openMode)
0081 {
0082     return configFile(QStringLiteral("agentsrc"), openMode);
0083 }
0084 
0085 QString StandardDirs::agentConfigFile(const QString &identifier, FileAccessMode openMode)
0086 {
0087     return configFile(QStringLiteral("agent_config_") + identifier, openMode);
0088 }
0089 
0090 QString StandardDirs::saveDir(const char *resource, const QString &relPath)
0091 {
0092     const QString fullRelPath = buildFullRelPath(resource, relPath);
0093     QString fullPath;
0094     if (qstrncmp(resource, "config", 6) == 0) {
0095         fullPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + fullRelPath;
0096     } else if (qstrncmp(resource, "data", 4) == 0) {
0097         fullPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + fullRelPath;
0098     } else if (qstrncmp(resource, "runtime", 7) == 0) {
0099         fullPath = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation) + fullRelPath;
0100     } else {
0101         qt_assert_x(__FUNCTION__, "Invalid resource type", __FILE__, __LINE__);
0102         return {};
0103     }
0104 
0105     // ensure directory exists or is created
0106     QFileInfo fileInfo(fullPath);
0107     if (fileInfo.exists()) {
0108         if (fileInfo.isDir()) {
0109             return fullPath;
0110         } else {
0111             qCWarning(AKONADIPRIVATE_LOG) << "StandardDirs::saveDir: '" << fileInfo.absoluteFilePath() << "' exists but is not a directory";
0112         }
0113     } else {
0114         if (!QDir::home().mkpath(fileInfo.absoluteFilePath())) {
0115             qCWarning(AKONADIPRIVATE_LOG) << "StandardDirs::saveDir: failed to create directory '" << fileInfo.absoluteFilePath() << "'";
0116         } else {
0117             return fullPath;
0118         }
0119     }
0120 
0121     return {};
0122 }
0123 
0124 QString StandardDirs::locateResourceFile(const char *resource, const QString &relPath)
0125 {
0126     const QString fullRelPath = buildFullRelPath(resource, relPath);
0127     QList<QStandardPaths::StandardLocation> userLocations;
0128     QStandardPaths::StandardLocation genericLocation;
0129     if (qstrncmp(resource, "config", 6) == 0) {
0130         userLocations = {QStandardPaths::AppConfigLocation, QStandardPaths::ConfigLocation};
0131         genericLocation = QStandardPaths::GenericConfigLocation;
0132     } else if (qstrncmp(resource, "data", 4) == 0) {
0133         userLocations = {QStandardPaths::AppLocalDataLocation, QStandardPaths::AppDataLocation};
0134         genericLocation = QStandardPaths::GenericDataLocation;
0135     } else {
0136         qt_assert_x(__FUNCTION__, "Invalid resource type", __FILE__, __LINE__);
0137         return {};
0138     }
0139 
0140     const auto locateFile = [](QStandardPaths::StandardLocation location, const QString &relPath) -> QString {
0141         const auto path = QStandardPaths::locate(location, relPath);
0142         if (!path.isEmpty()) {
0143             QFileInfo file(path);
0144             if (file.exists() && file.isFile() && file.isReadable()) {
0145                 return path;
0146             }
0147         }
0148         return {};
0149     };
0150 
0151     // Always honor instance in user-specific locations
0152     for (const auto location : std::as_const(userLocations)) {
0153         const auto path = locateFile(location, fullRelPath);
0154         if (!path.isEmpty()) {
0155             return path;
0156         }
0157     }
0158 
0159     const auto configPath = QStringLiteral(CONFIG_INSTALL_DIR);
0160     if (!configPath.isEmpty()) {
0161         QFileInfo file(configPath);
0162         if (file.exists() && file.isFile() && file.isReadable()) {
0163             return configPath;
0164         }
0165     }
0166 
0167     // First try instance-specific path in generic locations
0168     auto path = locateFile(genericLocation, fullRelPath);
0169     if (!path.isEmpty()) {
0170         return path;
0171     }
0172 
0173     // Fallback to global instance path in generic locations
0174     path = locateFile(genericLocation, QLatin1StringView("/akonadi/") + relPath);
0175     if (!path.isEmpty()) {
0176         return path;
0177     }
0178 
0179     return {};
0180 }
0181 
0182 QStringList StandardDirs::locateAllResourceDirs(const QString &relPath)
0183 {
0184     return QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, relPath, QStandardPaths::LocateDirectory);
0185 }
0186 
0187 QString StandardDirs::findExecutable(const QString &executableName)
0188 {
0189     QString executable = QStandardPaths::findExecutable(executableName, {qApp->applicationDirPath()});
0190     if (executable.isEmpty()) {
0191         executable = QStandardPaths::findExecutable(executableName);
0192     }
0193     return executable;
0194 }