1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2004-2007 Torsten Rahn <tackat@kde.org>
4 // SPDX-FileCopyrightText: 2007 Inge Wallin <ingwa@kde.org>
5 //
6 
7 
8 #include "MarbleDirs.h"
9 #include "MarbleDebug.h"
10 
11 #include <QFile>
12 #include <QString>
13 #include <QStringList>
14 #include <QCoreApplication>
15 
16 #include <cstdlib>
17 
18 #include <QStandardPaths>
19 
20 #ifdef Q_OS_WIN
21 //for getting appdata path
22 //mingw-w64 Internet Explorer 5.01
23 #define _WIN32_IE 0x0501
24 #include <shlobj.h>
25 #endif
26 
27 #ifdef Q_OS_MACX
28 //for getting app bundle path
29 #include <ApplicationServices/ApplicationServices.h>
30 #endif
31 
32 #include <config-marble.h>
33 
34 using namespace Marble;
35 
36 namespace
37 {
38     QString runTimeMarbleDataPath;
39 
40     QString runTimeMarblePluginPath;
41 }
42 
MarbleDirs()43 MarbleDirs::MarbleDirs()
44     : d( nullptr )
45 {
46 }
47 
48 
path(const QString & relativePath)49 QString MarbleDirs::path( const QString& relativePath )
50 {
51     QString  localpath = localPath() + QLatin1Char('/') + relativePath;	// local path
52     QString  systempath  = systemPath() + QLatin1Char('/') + relativePath;	// system path
53 
54 
55     QString fullpath = systempath;
56     if ( QFile::exists( localpath ) ) {
57         fullpath = localpath;
58     }
59     return QDir( fullpath ).canonicalPath();
60 }
61 
62 
pluginPath(const QString & relativePath)63 QString MarbleDirs::pluginPath( const QString& relativePath )
64 {
65     const QString localpath = pluginLocalPath() + QDir::separator() + relativePath;    // local path
66     const QString systempath  = pluginSystemPath() + QDir::separator() + relativePath; // system path
67 
68 
69     QString fullpath = systempath;
70     if ( QFile::exists( localpath ) ) {
71         fullpath = localpath;
72     }
73 
74     return QDir( fullpath ).canonicalPath();
75 }
76 
entryList(const QString & relativePath,QDir::Filters filters)77 QStringList MarbleDirs::entryList( const QString& relativePath, QDir::Filters filters )
78 {
79     QStringList filesLocal = QDir(MarbleDirs::localPath() + QLatin1Char('/') + relativePath).entryList(filters);
80     QStringList filesSystem = QDir(MarbleDirs::systemPath() + QLatin1Char('/') + relativePath).entryList(filters);
81     QStringList allFiles( filesLocal );
82     allFiles << filesSystem;
83 
84     // remove duplicate entries
85     allFiles.sort();
86     for ( int i = 1; i < allFiles.size(); ++i ) {
87         if ( allFiles.at(i) == allFiles.at( i - 1 ) ) {
88             allFiles.removeAt(i);
89             --i;
90         }
91     }
92 
93     return allFiles;
94 }
95 
pluginEntryList(const QString & relativePath,QDir::Filters filters)96 QStringList MarbleDirs::pluginEntryList( const QString& relativePath, QDir::Filters filters )
97 {
98     QStringList allFiles = QDir(MarbleDirs::pluginLocalPath() + QLatin1Char('/') + relativePath).entryList(filters);
99     auto const pluginSystemPath = MarbleDirs::pluginSystemPath();
100     if (!pluginSystemPath.isEmpty()) {
101         allFiles << QDir(pluginSystemPath + QLatin1Char('/') + relativePath).entryList(filters);
102     }
103 
104     // remove duplicate entries
105     allFiles.sort();
106     for ( int i = 1; i < allFiles.size(); ++i ) {
107         if ( allFiles.at(i) == allFiles.at( i - 1 ) ) {
108             allFiles.removeAt(i);
109             --i;
110         }
111     }
112 
113     return allFiles;
114 }
115 
systemPath()116 QString MarbleDirs::systemPath()
117 {
118     if (!runTimeMarbleDataPath.isEmpty()) {
119         return runTimeMarbleDataPath;
120     }
121 
122     QString systempath;
123 
124 #ifdef MARBLE_DATA_PATH
125     //MARBLE_DATA_PATH is a compiler define set by cmake
126     QString compileTimeMarbleDataPath(MARBLE_DATA_PATH);
127 
128     if(QDir(compileTimeMarbleDataPath).exists())
129         return compileTimeMarbleDataPath;
130 #endif  // MARBLE_DATA_PATH
131 
132 
133 #ifdef Q_OS_WIN
134 	return QCoreApplication::applicationDirPath() + QDir::separator() + QLatin1String("data");
135 #endif
136 
137 #ifdef Q_OS_MACX
138     //
139     // On OSX lets try to find any file first in the bundle
140     // before branching out to home and sys dirs
141     //
142     CFURLRef myBundleRef = CFBundleCopyBundleURL(CFBundleGetMainBundle());
143     CFStringRef myMacPath = CFURLCopyFileSystemPath(myBundleRef, kCFURLPOSIXPathStyle);
144     const char *mypPathPtr = CFStringGetCStringPtr(myMacPath,CFStringGetSystemEncoding());
145     CFRelease(myBundleRef);
146     QString myPath(mypPathPtr);
147     CFRelease(myMacPath);
148     //do some magick so that we can still find data dir if
149     //marble was not built as a bundle
150     if (myPath.contains(QLatin1String(".app"))) {  //its a bundle!
151       systempath = myPath + QLatin1String("/Contents/Resources/data");
152     }
153 
154     if ( QFile::exists( systempath ) ){
155       return systempath;
156     }
157 #endif   // mac bundle
158 
159 #ifdef Q_OS_ANDROID
160     systempath = "assets:/data";
161     return systempath;
162 #endif
163 
164     // This is the preferred fallback location if no marbleDataPath is set.
165     systempath = QDir( QCoreApplication::applicationDirPath() + QLatin1String( "/data" ) ).canonicalPath();
166 
167     if ( QFile::exists( systempath ) ){
168       return systempath;
169     }
170 
171     // This fallback location is for compatibility with KDE installations.
172     return QDir( QCoreApplication::applicationDirPath()
173                  + QLatin1String( "/../share/apps/marble/data" )
174                  ).canonicalPath();
175 }
176 
pluginSystemPath()177 QString MarbleDirs::pluginSystemPath()
178 {
179     if (!runTimeMarblePluginPath.isEmpty()) {
180         return runTimeMarblePluginPath;
181     }
182 
183     QString systempath;
184 
185 #ifdef MARBLE_PLUGIN_PATH
186     //MARBLE_PLUGIN_PATH is a compiler define set by cmake
187     QString compileTimeMarblePluginPath(MARBLE_PLUGIN_PATH);
188 
189     if(QDir(compileTimeMarblePluginPath).exists())
190         return compileTimeMarblePluginPath;
191 #endif  // MARBLE_PLUGIN_PATH
192 
193 #ifdef Q_OS_MACX
194     //
195     // On OSX lets try to find any file first in the bundle
196     // before branching out to home and sys dirs
197     //
198     CFURLRef myBundleRef = CFBundleCopyBundleURL(CFBundleGetMainBundle());
199     CFStringRef myMacPath = CFURLCopyFileSystemPath(myBundleRef, kCFURLPOSIXPathStyle);
200     const char *mypPathPtr = CFStringGetCStringPtr(myMacPath,CFStringGetSystemEncoding());
201     CFRelease(myBundleRef);
202     CFRelease(myMacPath);
203     QString myPath(mypPathPtr);
204     //do some magick so that we can still find data dir if
205     //marble was not built as a bundle
206     if (myPath.contains(QLatin1String(".app"))) {  //its a bundle!
207       systempath = myPath + QLatin1String("/Contents/Resources/plugins");
208     }
209 
210     if ( QFile::exists( systempath ) ){
211       return systempath;
212     }
213 #endif   // mac bundle
214 
215 #ifdef Q_OS_WIN
216 	return QCoreApplication::applicationDirPath() + QDir::separator() + QLatin1String("plugins");
217 #endif
218 
219 #ifdef Q_OS_ANDROID
220     return "assets:/plugins";
221 #endif
222 
223     // This is the preferred fallback location if no marblePluginPath is set.
224     systempath = QDir( QCoreApplication::applicationDirPath() + QLatin1String( "/plugins" ) ).canonicalPath();
225 
226     if ( QFile::exists( systempath ) ){
227       return systempath;
228     }
229 
230     // This ultimate fallback location is for compatibility with KDE installations.
231     return QDir( QCoreApplication::applicationDirPath()
232                  + QLatin1String( "/../lib/kde4/plugins/marble" )
233                  ).canonicalPath();
234 }
235 
localPath()236 QString MarbleDirs::localPath()
237 {
238 #ifndef Q_OS_WIN
239     QString dataHome = getenv( "XDG_DATA_HOME" );
240     if( dataHome.isEmpty() )
241         dataHome = QDir::homePath() + QLatin1String("/.local/share");
242 
243     return dataHome + QLatin1String("/marble"); // local path
244 #else
245 	return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/.marble/data");
246 #endif
247 }
248 
oldLocalPaths()249 QStringList MarbleDirs::oldLocalPaths()
250 {
251     QStringList possibleOldPaths;
252 
253 #ifndef Q_OS_WIN
254     const QString oldDefault = QDir::homePath() + QLatin1String("/.marble/data");
255     possibleOldPaths.append( oldDefault );
256 
257     const QString xdgDefault = QDir::homePath() + QLatin1String("/.local/share/marble");
258     possibleOldPaths.append( xdgDefault );
259 
260     QString xdg = getenv( "XDG_DATA_HOME" );
261     xdg += QLatin1String("/marble/");
262     possibleOldPaths.append( xdg );
263 #endif
264 
265 #ifdef Q_OS_WIN
266 	HWND hwnd = 0;
267 	WCHAR *appdata_path = new WCHAR[MAX_PATH + 1];
268 
269 	SHGetSpecialFolderPathW(hwnd, appdata_path, CSIDL_APPDATA, 0);
270 	QString appdata = QString::fromUtf16(reinterpret_cast<ushort*>(appdata_path));
271 	delete[] appdata_path;
272 	possibleOldPaths << QString(QDir::fromNativeSeparators(appdata) + QLatin1String("/.marble/data")); // local path
273 #endif
274 
275     QString currentLocalPath = QDir( MarbleDirs::localPath() ).canonicalPath();
276     QStringList oldPaths;
277     for( const QString& possibleOldPath: possibleOldPaths ) {
278         if( !QDir().exists( possibleOldPath ) ) {
279             continue;
280         }
281 
282         QString canonicalPossibleOldPath = QDir( possibleOldPath ).canonicalPath();
283         if( canonicalPossibleOldPath == currentLocalPath ) {
284             continue;
285         }
286 
287         oldPaths.append( canonicalPossibleOldPath );
288     }
289 
290     return oldPaths;
291 }
292 
pluginLocalPath()293 QString MarbleDirs::pluginLocalPath()
294 {
295 #ifndef Q_OS_WIN
296     return QDir::homePath() + QLatin1String("/.marble/plugins"); // local path
297 #else
298 	return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/.marble/plugins");
299 #endif
300 }
301 
marbleDataPath()302 QString MarbleDirs::marbleDataPath()
303 {
304     return runTimeMarbleDataPath;
305 }
306 
marblePluginPath()307 QString MarbleDirs::marblePluginPath()
308 {
309     return runTimeMarblePluginPath;
310 }
311 
setMarbleDataPath(const QString & adaptedPath)312 void MarbleDirs::setMarbleDataPath( const QString& adaptedPath )
313 {
314     if ( !QDir::root().exists( adaptedPath ) )
315     {
316         qWarning() << QString( "Invalid MarbleDataPath \"%1\". Using \"%2\" instead." ).arg( adaptedPath, systemPath() );
317         return;
318     }
319 
320     runTimeMarbleDataPath = adaptedPath;
321 }
322 
setMarblePluginPath(const QString & adaptedPath)323 void MarbleDirs::setMarblePluginPath( const QString& adaptedPath )
324 {
325     if ( !QDir::root().exists( adaptedPath ) )
326     {
327         qWarning() << QString( "Invalid MarblePluginPath \"%1\". Using \"%2\" instead." ).arg( adaptedPath, pluginSystemPath() );
328         return;
329     }
330 
331     runTimeMarblePluginPath = adaptedPath;
332 }
333 
334 
debug()335 void MarbleDirs::debug()
336 {
337     mDebug() << "=== MarbleDirs: ===";
338     mDebug() << "Local Path:" << localPath();
339     mDebug() << "Plugin Local Path:" << pluginLocalPath();
340     mDebug() << "";
341     mDebug() << "Marble Data Path (Run Time) :" << runTimeMarbleDataPath;
342     mDebug() << "Marble Data Path (Compile Time):" << QString(MARBLE_DATA_PATH);
343     mDebug() << "";
344     mDebug() << "Marble Plugin Path (Run Time) :" << runTimeMarblePluginPath;
345     mDebug() << "Marble Plugin Path (Compile Time):" << QString(MARBLE_PLUGIN_PATH);
346     mDebug() << "";
347     mDebug() << "System Path:" << systemPath();
348     mDebug() << "Plugin System Path:" << pluginSystemPath();
349     mDebug() << "===================";
350 }
351