diff --git a/libs/xdgbasedirs.cpp b/libs/xdgbasedirs.cpp index f0e99946..8e8849ee 100644 --- a/libs/xdgbasedirs.cpp +++ b/libs/xdgbasedirs.cpp @@ -1,616 +1,616 @@ /*************************************************************************** * Copyright (C) 2007 by Kevin Krammer * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "xdgbasedirs_p.h" #include "akonadi-prefix.h" // for prefix defines #include #include #include #include #include #include #include -#include +#include #include #ifdef Q_OS_WIN # include #endif static QStringList alternateExecPaths( const QString &path ) { QStringList pathList; pathList << path; #if defined(Q_OS_WIN) //krazy:exclude=cpp pathList << path + QLatin1String( ".exe" ); #elif defined(Q_OS_MAC) //krazy:exclude=cpp pathList << path + QLatin1String( ".app/Contents/MacOS/" ) + path.section( QLatin1Char( '/' ), -1 ); #endif return pathList; } static QStringList splitPathList( const QString &pathList ) { #if defined(Q_OS_WIN) //krazy:exclude=cpp return pathList.split( QLatin1Char( ';' ) ); #else return pathList.split( QLatin1Char( ':' ) ); #endif } #ifdef Q_OS_WIN static QMap getEnvironment() { QMap ret; Q_FOREACH(const QString& str, QProcessEnvironment::systemEnvironment().toStringList()) { const int p = str.indexOf(QLatin1Char('=')); ret[str.left(p)] = str.mid(p + 1); } return ret; } QString expandEnvironmentVariables(const QString& str) { static QMap envVars = getEnvironment(); static QRegExp possibleVars(QLatin1String("((\\{|%)(\\w+)(\\}|%))")); QString ret = str; while(possibleVars.indexIn(ret) != -1) { QStringList caps = possibleVars.capturedTexts(); if(caps[2] == QLatin1String("{")) { ret.replace(QLatin1String("$") + caps[1], envVars[caps[3]]); } else { ret.replace(caps[1], envVars[caps[3]]); } QString key = possibleVars.cap(); ret.replace(key, envVars[key]); } return ret; } static QSettings* getKdeConf() { WCHAR wPath[MAX_PATH+1]; GetModuleFileNameW(NULL, wPath, MAX_PATH); QString kdeconfPath = QString::fromUtf16((const ushort *) wPath); kdeconfPath = kdeconfPath.left(kdeconfPath.lastIndexOf(QLatin1Char('\\'))).replace(QLatin1Char('\\'), QLatin1Char('/')); if(QFile::exists(kdeconfPath + QString::fromLatin1("/kde.conf"))) { return new QSettings(kdeconfPath + QString::fromLatin1("/kde.conf"), QSettings::IniFormat); } else { return 0; } } #endif namespace Akonadi { class XdgBaseDirsPrivate { public: XdgBaseDirsPrivate() { } ~XdgBaseDirsPrivate() { } }; class XdgBaseDirsSingleton { public: QString homePath( const char *variable, const char *defaultSubDir ); QStringList systemPathList( const char *variable, const char *defaultDirList ); public: QString mConfigHome; QString mDataHome; QStringList mConfigDirs; QStringList mDataDirs; QStringList mExecutableDirs; QStringList mPluginDirs; }; Q_GLOBAL_STATIC( XdgBaseDirsSingleton, instance ) } using namespace Akonadi; XdgBaseDirs::XdgBaseDirs() : d( new XdgBaseDirsPrivate() ) { } XdgBaseDirs::~XdgBaseDirs() { delete d; } QString XdgBaseDirs::homePath( const char *resource ) { #ifdef Q_OS_WIN static QSettings* kdeconf = getKdeConf(); #endif if ( qstrncmp( "data", resource, 4 ) == 0 ) { if ( instance()->mDataHome.isEmpty() ) { #ifdef Q_OS_WIN if(kdeconf) { kdeconf->beginGroup(QLatin1String("XDG")); if(kdeconf->childKeys().contains(QLatin1String("XDG_DATA_HOME"))) instance()->mDataHome = expandEnvironmentVariables(kdeconf->value(QLatin1String("XDG_DATA_HOME")).toString()); else instance()->mDataHome = instance()->homePath( "XDG_DATA_HOME", ".local/share" ); kdeconf->endGroup(); } else { #else { #endif instance()->mDataHome = instance()->homePath( "XDG_DATA_HOME", ".local/share" ); } } return instance()->mDataHome; } else if ( qstrncmp( "config", resource, 6 ) == 0 ) { if ( instance()->mConfigHome.isEmpty() ) { #ifdef Q_OS_WIN if(kdeconf) { kdeconf->beginGroup(QLatin1String("XDG")); if(kdeconf->childKeys().contains(QLatin1String("XDG_CONFIG_HOME"))) instance()->mConfigHome = expandEnvironmentVariables(kdeconf->value(QLatin1String("XDG_CONFIG_HOME")).toString()); else instance()->mConfigHome = instance()->homePath( "XDG_CONFIG_HOME", ".config" ); kdeconf->endGroup(); } else { #else { #endif instance()->mConfigHome = instance()->homePath( "XDG_CONFIG_HOME", ".config" ); } } return instance()->mConfigHome; } return QString(); } QStringList XdgBaseDirs::systemPathList( const char *resource ) { #ifdef Q_OS_WIN static QSettings* kdeconf = getKdeConf(); #endif if ( qstrncmp( "data", resource, 4 ) == 0 ) { if ( instance()->mDataDirs.isEmpty() ) { #ifdef Q_OS_WIN QStringList dataDirs; if(kdeconf) { kdeconf->beginGroup(QLatin1String("XDG")); if(kdeconf->childKeys().contains(QLatin1String("XDG_DATA_DIRS"))) { dataDirs = instance()->systemPathList( "XDG_DATA_DIRS", expandEnvironmentVariables(kdeconf->value(QLatin1String("XDG_DATA_DIRS")).toString()).toLocal8Bit().constData() ); } else { QDir dir( QCoreApplication::applicationDirPath() ); dir.cdUp(); const QString defaultPathList = dir.absoluteFilePath( QLatin1String( "share" ) ); dataDirs = instance()->systemPathList( "XDG_DATA_DIRS", defaultPathList.toLocal8Bit().constData() ); } kdeconf->endGroup(); } else { QDir dir( QCoreApplication::applicationDirPath() ); dir.cdUp(); const QString defaultPathList = dir.absoluteFilePath( QLatin1String( "share" ) ); dataDirs = instance()->systemPathList( "XDG_DATA_DIRS", defaultPathList.toLocal8Bit().constData() ); } #else QStringList dataDirs = instance()->systemPathList( "XDG_DATA_DIRS", "/usr/local/share:/usr/share" ); #endif #ifdef Q_OS_WIN const QString prefixDataDir = QLatin1String( AKONADIPREFIX "/" AKONADIDATA ); #else const QString prefixDataDir = QLatin1String( AKONADIDATA ); #endif if ( !dataDirs.contains( prefixDataDir ) ) { dataDirs << prefixDataDir; } // fallback for users with KDE in a different prefix and not correctly set up XDG_DATA_DIRS, hi David ;-) QProcess proc; // ### should probably rather be --path xdg-something const QStringList args = QStringList() << QLatin1String( "--prefix" ); proc.start( QLatin1String( "kde4-config" ), args ); if ( proc.waitForStarted() && proc.waitForFinished() && proc.exitCode() == 0 ) { proc.setReadChannel( QProcess::StandardOutput ); Q_FOREACH ( const QString &basePath, splitPathList( QString::fromLocal8Bit( proc.readLine().trimmed() ) ) ) { const QString path = basePath + QDir::separator() + QLatin1String( "share" ); if ( !dataDirs.contains( path ) ) { dataDirs << path; } } } instance()->mDataDirs = dataDirs; } #ifdef Q_OS_WIN QStringList dataDirs = instance()->mDataDirs; // on Windows installation might be scattered across several directories // so check if any installer providing agents has registered its base path QSettings agentProviders( QSettings::SystemScope, QLatin1String( "Akonadi" ), QLatin1String( "Akonadi" ) ); agentProviders.beginGroup( QLatin1String( "AgentProviders" ) ); Q_FOREACH ( const QString &agentProvider, agentProviders.childKeys() ) { const QString basePath = agentProviders.value( agentProvider ).toString(); if ( !basePath.isEmpty() ) { const QString path = basePath + QDir::separator() + QLatin1String( "share" ); if ( !dataDirs.contains( path ) ) { dataDirs << path; } } } return dataDirs; #else return instance()->mDataDirs; #endif } else if ( qstrncmp( "config", resource, 6 ) == 0 ) { if ( instance()->mConfigDirs.isEmpty() ) { #ifdef Q_OS_WIN QStringList configDirs; if(kdeconf) { kdeconf->beginGroup(QLatin1String("XDG")); if(kdeconf->childKeys().contains(QLatin1String("XDG_CONFIG_DIRS"))) { configDirs = instance()->systemPathList( "XDG_CONFIG_DIRS", expandEnvironmentVariables(kdeconf->value(QLatin1String("XDG_CONFIG_DIRS")).toString()).toLocal8Bit().constData() ); } else { QDir dir( QCoreApplication::applicationDirPath() ); dir.cdUp(); const QString defaultPathList = dir.absoluteFilePath( QLatin1String( "etc" ) ) + QLatin1Char( ';' ) + dir.absoluteFilePath( QLatin1String( "share/config" ) ); configDirs = instance()->systemPathList( "XDG_CONFIG_DIRS", defaultPathList.toLocal8Bit().constData() ); } kdeconf->endGroup(); } else { QDir dir( QCoreApplication::applicationDirPath() ); dir.cdUp(); const QString defaultPathList = dir.absoluteFilePath( QLatin1String( "etc" ) ) + QLatin1Char( ';' ) + dir.absoluteFilePath( QLatin1String( "share/config" ) ); configDirs = instance()->systemPathList( "XDG_CONFIG_DIRS", defaultPathList.toLocal8Bit().constData() ); } #else QStringList configDirs = instance()->systemPathList( "XDG_CONFIG_DIRS", "/etc/xdg" ); #endif #ifdef Q_OS_WIN const QString prefixConfigDir = QLatin1String( AKONADIPREFIX "/" AKONADICONFIG ); #else const QString prefixConfigDir = QLatin1String( AKONADICONFIG ); #endif if ( !configDirs.contains( prefixConfigDir ) ) { configDirs << prefixConfigDir; } instance()->mConfigDirs = configDirs; } return instance()->mConfigDirs; } return QStringList(); } QString XdgBaseDirs::findResourceFile( const char *resource, const QString &relPath ) { const QString fullPath = homePath( resource ) + QLatin1Char( '/' ) + relPath; QFileInfo fileInfo( fullPath ); if ( fileInfo.exists() && fileInfo.isFile() && fileInfo.isReadable() ) { return fullPath; } const QStringList pathList = systemPathList( resource ); Q_FOREACH ( const QString &path, pathList ) { fileInfo = QFileInfo( path + QLatin1Char( '/' ) + relPath ); if ( fileInfo.exists() && fileInfo.isFile() && fileInfo.isReadable() ) { return fileInfo.absoluteFilePath(); } } return QString(); } QString XdgBaseDirs::findExecutableFile( const QString &relPath, const QStringList &searchPath ) { if ( instance()->mExecutableDirs.isEmpty() ) { QStringList executableDirs = instance()->systemPathList( "PATH", "/usr/local/bin:/usr/bin" ); const QString prefixExecutableDir = QLatin1String( AKONADIPREFIX "/bin" ); if ( !executableDirs.contains( prefixExecutableDir ) ) { executableDirs << prefixExecutableDir; } if ( QCoreApplication::instance() != 0 ) { const QString appExecutableDir = QCoreApplication::instance()->applicationDirPath(); if ( !executableDirs.contains( appExecutableDir ) ) { executableDirs << appExecutableDir; } } executableDirs += searchPath; #if defined(Q_OS_MAC) //krazy:exclude=cpp executableDirs += QLatin1String( AKONADIBUNDLEPATH ); #endif qWarning() << "search paths: " << executableDirs; instance()->mExecutableDirs = executableDirs; } #ifdef Q_OS_WIN QStringList executableDirs = instance()->mExecutableDirs; // on Windows installation might be scattered across several directories // so check if any installer providing agents has registered its base path QSettings agentProviders( QSettings::SystemScope, QLatin1String( "Akonadi" ), QLatin1String( "Akonadi" ) ); agentProviders.beginGroup( QLatin1String( "AgentProviders" ) ); Q_FOREACH ( const QString &agentProvider, agentProviders.childKeys() ) { const QString basePath = agentProviders.value( agentProvider ).toString(); if ( !basePath.isEmpty() ) { const QString path = basePath + QDir::separator() + QLatin1String( "bin" ); if ( !executableDirs.contains( path ) ) { executableDirs << path; } } } QStringList::const_iterator pathIt = executableDirs.constBegin(); const QStringList::const_iterator pathEndIt = executableDirs.constEnd(); #else QStringList::const_iterator pathIt = instance()->mExecutableDirs.constBegin(); const QStringList::const_iterator pathEndIt = instance()->mExecutableDirs.constEnd(); #endif for ( ; pathIt != pathEndIt; ++pathIt ) { const QStringList fullPathList = alternateExecPaths( *pathIt + QLatin1Char( '/' ) + relPath ); QStringList::const_iterator it = fullPathList.constBegin(); const QStringList::const_iterator endIt = fullPathList.constEnd(); for ( ; it != endIt; ++it ) { const QFileInfo fileInfo( *it ); // resolve symlinks, happens eg. with Maemo optify if ( fileInfo.canonicalFilePath().isEmpty() ) { continue; } const QFileInfo canonicalFileInfo( fileInfo.canonicalFilePath() ); if ( canonicalFileInfo.exists() && canonicalFileInfo.isFile() && canonicalFileInfo.isExecutable() ) { return *it; } } } return QString(); } QStringList XdgBaseDirs::findPluginDirs() { if ( instance()->mPluginDirs.isEmpty() ) { QStringList pluginDirs = instance()->systemPathList( "QT_PLUGIN_PATH", AKONADILIB ":" AKONADILIB "/qt4/plugins/:" AKONADILIB "/kde4/:" AKONADILIB "/kde4/plugins/:/usr/lib/qt4/plugins/" ); if ( QCoreApplication::instance() != 0 ) { Q_FOREACH ( const QString &libraryPath, QCoreApplication::instance()->libraryPaths() ) { if ( !pluginDirs.contains( libraryPath ) ) { pluginDirs << libraryPath; } } } // fallback for users with KDE in a different prefix and not correctly set up XDG_DATA_DIRS, hi David ;-) QProcess proc; // ### should probably rather be --path xdg-something const QStringList args = QStringList() << QLatin1String( "--path" ) << QLatin1String( "module" ); proc.start( QLatin1String( "kde4-config" ), args ); if ( proc.waitForStarted() && proc.waitForFinished() && proc.exitCode() == 0 ) { proc.setReadChannel( QProcess::StandardOutput ); Q_FOREACH ( const QString &path, splitPathList( QString::fromLocal8Bit( proc.readLine().trimmed() ) ) ) { if ( !pluginDirs.contains( path ) ) { pluginDirs.append( path ); } } } qWarning() << "search paths: " << pluginDirs; instance()->mPluginDirs = pluginDirs; } return instance()->mPluginDirs; } QString XdgBaseDirs::findPluginFile( const QString &relPath, const QStringList &searchPath ) { const QStringList searchDirs = findPluginDirs() + searchPath; #if defined(Q_OS_WIN) //krazy:exclude=cpp const QString pluginName = relPath + QLatin1String( ".dll" ); #else const QString pluginName = relPath + QLatin1String( ".so" ); #endif Q_FOREACH ( const QString &path, searchDirs ) { const QFileInfo fileInfo( path + QDir::separator() + pluginName ); // resolve symlinks, happens eg. with Maemo optify if ( fileInfo.canonicalFilePath().isEmpty() ) { continue; } const QFileInfo canonicalFileInfo( fileInfo.canonicalFilePath() ); if ( canonicalFileInfo.exists() && canonicalFileInfo.isFile() ) { return canonicalFileInfo.absoluteFilePath(); } } return QString(); } QString XdgBaseDirs::findResourceDir( const char *resource, const QString &relPath ) { QString fullPath = homePath( resource ) + QLatin1Char( '/' ) + relPath; QFileInfo fileInfo( fullPath ); if ( fileInfo.exists() && fileInfo.isDir() && fileInfo.isReadable() ) { return fullPath; } Q_FOREACH ( const QString &path, systemPathList( resource ) ) { fileInfo = QFileInfo( path + QLatin1Char( '/' ) + relPath ); if ( fileInfo.exists() && fileInfo.isDir() && fileInfo.isReadable() ) { return fileInfo.absoluteFilePath(); } } return QString(); } QStringList XdgBaseDirs::findAllResourceDirs( const char *resource, const QString &relPath ) { QStringList resultList; const QString fullPath = homePath( resource ) + QLatin1Char( '/' ) + relPath; QFileInfo fileInfo( fullPath ); if ( fileInfo.exists() && fileInfo.isDir() && fileInfo.isReadable() ) { resultList << fileInfo.absoluteFilePath(); } Q_FOREACH ( const QString &path, systemPathList( resource ) ) { fileInfo = QFileInfo( path + QLatin1Char( '/' ) + relPath ); if ( fileInfo.exists() && fileInfo.isDir() && fileInfo.isReadable() ) { const QString absPath = fileInfo.absoluteFilePath(); if ( !resultList.contains( absPath ) ) { resultList << absPath; } } } return resultList; } QString XdgBaseDirs::saveDir( const char *resource, const QString &relPath ) { const QString fullPath = homePath( resource ) + QLatin1Char( '/' ) + relPath; QFileInfo fileInfo( fullPath ); if ( fileInfo.exists() ) { if ( fileInfo.isDir() ) { return fullPath; } else { qWarning() << "XdgBaseDirs::saveDir: '" << fileInfo.absoluteFilePath() << "' exists but is not a directory"; } } else { if ( !QDir::home().mkpath( fileInfo.absoluteFilePath() ) ) { qWarning() << "XdgBaseDirs::saveDir: failed to create directory '" << fileInfo.absoluteFilePath() << "'"; } else { return fullPath; } } return QString(); } QString XdgBaseDirs::akonadiServerConfigFile( FileAccessMode openMode ) { return akonadiConfigFile( QLatin1String( "akonadiserverrc" ), openMode ); } QString XdgBaseDirs::akonadiConnectionConfigFile( FileAccessMode openMode ) { return akonadiConfigFile( QLatin1String( "akonadiconnectionrc" ), openMode ); } QString XdgBaseDirs::akonadiConfigFile( const QString &file, FileAccessMode openMode ) { const QString akonadiDir = QLatin1String( "akonadi" ); const QString savePath = saveDir( "config", akonadiDir ) + QLatin1Char( '/' ) + file; if ( openMode == WriteOnly ) { return savePath; } const QString path = findResourceFile( "config", akonadiDir + QLatin1Char( '/' ) + file ); if ( path.isEmpty() ) { return savePath; } else if ( openMode == ReadOnly || path == savePath ) { return path; } // file found in system paths and mode is ReadWrite, thus // we copy to the home path location and return this path QFile systemFile( path ); systemFile.copy( savePath ); return savePath; } QString XdgBaseDirsSingleton::homePath( const char *variable, const char *defaultSubDir ) { const QByteArray env = qgetenv( variable ); QString xdgPath; if ( env.isEmpty() ) { xdgPath = QDir::homePath() + QLatin1Char( '/' ) + QLatin1String( defaultSubDir ); #if defined(Q_OS_WIN) //krazy:exclude=cpp } else if ( QDir::isAbsolutePath( QString::fromLocal8Bit( env ) ) ) { #else } else if ( env.startsWith( '/' ) ) { #endif xdgPath = QString::fromLocal8Bit( env ); } else { xdgPath = QDir::homePath() + QLatin1Char( '/' ) + QString::fromLocal8Bit( env ); } return xdgPath; } QStringList XdgBaseDirsSingleton::systemPathList( const char *variable, const char *defaultDirList ) { const QByteArray env = qgetenv( variable ); QString xdgDirList; if ( env.isEmpty() ) { xdgDirList = QLatin1String( defaultDirList ); } else { xdgDirList = QString::fromLocal8Bit( env ); } return splitPathList( xdgDirList ); }