1 #include <QString>
2 #include <QLatin1String>
3 #include <QDir>
4 #include <QFile>
5 #include <QSettings>
6 #include <QLocale>
7 #include <QDesktopServices>
8 #include <QMessageBox>
9 #ifdef HAVE_QT5
10 #include <QStandardPaths>
11 #endif
12
13 #ifdef Q_OS_UNIX
14 #include <sys/stat.h> // chmod
15 #endif
16
17 #ifdef Q_OS_WIN
18 #include <windows.h>
19 #include <shellapi.h>
20 #include <shlobj.h>
21 #endif
22
23 #ifdef Q_OS_MAC
24 #include <sys/stat.h> // chmod
25 #include <CoreServices/CoreServices.h>
26 #endif
27
28 #include "psiapplication.h"
29 #include "applicationinfo.h"
30 #include "systeminfo.h"
31 #include "profiles.h"
32 #include "homedirmigration.h"
33 #include "activeprofiles.h"
34 #include "translationmanager.h"
35 #ifdef HAVE_CONFIG
36 #include "config.h"
37 #endif
38
39 #define xstr(a) str(a)
40 #define str(a) #a
41
42 // Constants. These should be moved to a more 'dynamically changeable'
43 // place (like an external file loaded through the resources system)
44 // Should also be overridable through an optional file.
45 //
46 // PROG_SNAME - read as small name, system name, soname, short name, fs name
47
48 #define PROG_NAME "Psi"
49 #define PROG_SNAME "psi"
50 #define PROG_VERSION PSI_VERSION
51 //#define PROG_VERSION "0.15-dev" " (" __DATE__ ")" //CVS Builds are dated
52 //#define PROG_VERSION "0.15";
53 #define PROG_CAPS_NODE "https://psi-im.org"
54 #define PROG_IPC_NAME "org.psi-im.Psi" // must not contain '\\' character on Windows
55 #define PROG_OPTIONS_NS "http://psi-im.org/options"
56 #define PROG_STORAGE_NS "http://psi-im.org/storage"
57 #define PROG_FILECACHE_NS "http://psi-im.org/filecache"
58 #ifdef Q_OS_MAC
59 #define PROG_APPCAST_URL "http://psi-im.org/appcast/psi-mac.xml"
60 #else
61 #define PROG_APPCAST_URL ""
62 #endif
63
name()64 QString ApplicationInfo::name()
65 {
66 return PROG_NAME;
67 }
68
sname()69 QLatin1String ApplicationInfo::sname()
70 {
71 return QLatin1String(PROG_SNAME);
72 }
73
version()74 QString ApplicationInfo::version()
75 {
76 return PROG_VERSION;
77 }
78
capsNode()79 QString ApplicationInfo::capsNode()
80 {
81 return PROG_CAPS_NODE;
82 }
83
osName()84 QString ApplicationInfo::osName()
85 {
86 return SystemInfo::instance()->os();
87 }
88
IPCName()89 QString ApplicationInfo::IPCName()
90 {
91 return PROG_IPC_NAME;
92 }
93
getAppCastURL()94 QString ApplicationInfo::getAppCastURL()
95 {
96 return PROG_APPCAST_URL;
97 }
98
optionsNS()99 QString ApplicationInfo::optionsNS()
100 {
101 return PROG_OPTIONS_NS;
102 }
103
storageNS()104 QString ApplicationInfo::storageNS()
105 {
106 return PROG_STORAGE_NS;
107 }
108
fileCacheNS()109 QString ApplicationInfo::fileCacheNS()
110 {
111 return PROG_FILECACHE_NS;
112 }
113
getCertificateStoreDirs()114 QStringList ApplicationInfo::getCertificateStoreDirs()
115 {
116 QStringList l;
117 l += ApplicationInfo::resourcesDir() + "/certs";
118 l += ApplicationInfo::homeDir(ApplicationInfo::DataLocation) + "/certs";
119 return l;
120 }
121
dataDirs()122 QStringList ApplicationInfo::dataDirs()
123 {
124 const static QStringList dirs = QStringList() << ":" << "." << homeDir(DataLocation)
125 << resourcesDir();
126 return dirs;
127 }
128
getCertificateStoreSaveDir()129 QString ApplicationInfo::getCertificateStoreSaveDir()
130 {
131 QDir certsave(homeDir(DataLocation) + "/certs");
132 if(!certsave.exists()) {
133 QDir home(homeDir(DataLocation));
134 home.mkdir("certs");
135 }
136
137 return certsave.path();
138 }
139
resourcesDir()140 QString ApplicationInfo::resourcesDir()
141 {
142 #if defined(Q_OS_WIN)
143 return qApp->applicationDirPath();
144 #elif defined(Q_OS_MAC)
145 // FIXME: Clean this up (remko)
146 // System routine locates resource files. We "know" that Psi.icns is
147 // in the Resources directory.
148 QString resourcePath;
149 CFBundleRef mainBundle = CFBundleGetMainBundle();
150 CFStringRef resourceCFStringRef = CFStringCreateWithCString(NULL,
151 "application.icns", kCFStringEncodingASCII);
152 CFURLRef resourceURLRef = CFBundleCopyResourceURL(mainBundle,
153 resourceCFStringRef, NULL, NULL);
154 if (resourceURLRef) {
155 CFStringRef resourcePathStringRef = CFURLCopyFileSystemPath(
156 resourceURLRef, kCFURLPOSIXPathStyle);
157 const char* resourcePathCString = CFStringGetCStringPtr(
158 resourcePathStringRef, kCFStringEncodingASCII);
159 if (resourcePathCString) {
160 resourcePath = resourcePathCString;
161 }
162 else { // CFStringGetCStringPtr failed; use fallback conversion
163 CFIndex bufferLength = CFStringGetLength(resourcePathStringRef) + 1;
164 char* resourcePathCString = new char[ bufferLength ];
165 Boolean conversionSuccess = CFStringGetCString(
166 resourcePathStringRef, resourcePathCString,
167 bufferLength, kCFStringEncodingASCII);
168 if (conversionSuccess) {
169 resourcePath = resourcePathCString;
170 }
171 delete [] resourcePathCString; // I own this
172 }
173 CFRelease( resourcePathStringRef ); // I own this
174 }
175 // Remove the tail component of the path
176 if (! resourcePath.isNull()) {
177 QFileInfo fileInfo(resourcePath);
178 resourcePath = fileInfo.absolutePath();
179 }
180 return resourcePath;
181 #else
182 return PSI_DATADIR;
183 #endif
184 }
185
libDir()186 QString ApplicationInfo::libDir()
187 {
188 #if defined(Q_OS_UNIX)
189 return PSI_LIBDIR;
190 #else
191 return QString();
192 #endif
193 }
194
195 /** \brief return psi's private read write data directory
196 * unix+mac: $HOME/.psi
197 * environment variable "PSIDATADIR" overrides
198 */
homeDir(ApplicationInfo::HomedirType type)199 QString ApplicationInfo::homeDir(ApplicationInfo::HomedirType type)
200 {
201 static QString configDir_;
202 static QString dataDir_;
203 static QString cacheDir_;
204
205 if (configDir_.isEmpty()) {
206 // Try the environment override first
207 configDir_ = QString::fromLocal8Bit(getenv("PSIDATADIR"));
208
209 if (configDir_.isEmpty()) {
210 #if defined Q_OS_WIN
211 QString base = QFileInfo(QCoreApplication::applicationFilePath()).fileName()
212 .toLower().indexOf("portable") == -1?
213 "" : QCoreApplication::applicationDirPath();
214 if (base.isEmpty()) {
215 wchar_t path[MAX_PATH];
216 if (SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, path) == S_OK) {
217 configDir_ = QString::fromWCharArray(path) + "\\" + name();
218 } else {
219 configDir_ = QDir::homePath() + "/" + name();
220 }
221 dataDir_ = configDir_;
222 // prefer non-roaming data location for cache which is default for qds:DataLocation
223 #ifdef HAVE_QT5
224 cacheDir_ = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
225 #else
226 cacheDir_ = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
227 #endif
228 } else {
229 configDir_ = dataDir_ = cacheDir_ = base + "/" + name();
230 }
231 // temporary store for later processing
232 QDir configDir(configDir_);
233 QDir cacheDir(cacheDir_);
234 QDir dataDir(dataDir_);
235 #elif defined Q_OS_MAC
236 QDir configDir(QDir::homePath() + "/Library/Application Support/" + name());
237 QDir cacheDir(QDir::homePath() + "/Library/Caches/" + name());
238 QDir dataDir(configDir);
239 #elif defined HAVE_FREEDESKTOP
240 #ifndef HAVE_QT5
241 QString XdgConfigHome = QString::fromLocal8Bit(getenv("XDG_CONFIG_HOME"));
242 QString XdgDataHome = QString::fromLocal8Bit(getenv("XDG_DATA_HOME"));
243 QString XdgCacheHome = QString::fromLocal8Bit(getenv("XDG_CACHE_HOME"));
244 #else
245 QString XdgConfigHome(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation));
246 QString XdgDataHome(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation));
247 QString XdgCacheHome(QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation));
248 #endif
249 if (XdgConfigHome.isEmpty()) {
250 XdgConfigHome = QDir::homePath() + "/.config";
251 }
252 if (XdgDataHome.isEmpty()) {
253 XdgDataHome = QDir::homePath() + "/.local/share";
254 }
255 if (XdgCacheHome.isEmpty()) {
256 XdgCacheHome = QDir::homePath() + "/.cache";
257 }
258 QDir configDir(XdgConfigHome + "/" + sname());
259 QDir dataDir(XdgDataHome + "/" + sname());
260 QDir cacheDir(XdgCacheHome + "/" + sname());
261
262 // migrate mix-cased to lowercase, if needed
263
264 QDir configDirOld(XdgConfigHome + "/" + name());
265 QDir dataDirOld(XdgDataHome + "/" + name());
266 QDir cacheDirOld(XdgCacheHome + "/" + name());
267
268 bool ok = true;
269 if (ok && !configDir.exists() && configDirOld.exists()) {
270 configDirOld = QDir(XdgConfigHome);
271 ok = configDirOld.rename(name(), sname());
272 }
273 if (ok && !dataDir.exists() && dataDirOld.exists()) {
274 dataDirOld = QDir(XdgDataHome);
275 ok = dataDirOld.rename(name(), sname());
276 }
277 if (ok && !cacheDir.exists() && cacheDirOld.exists()) {
278 cacheDirOld = QDir(XdgCacheHome);
279 ok = cacheDirOld.rename(name(), sname());
280 }
281
282 if(!ok)
283 {
284 QMessageBox::information(0, QObject::tr("Conversion Error"), QObject::tr("Configuration data for a previous version of Psi was found, but it was not possible to convert it to work with the current version. Ensure you have appropriate permission and that another copy of Psi is not running, and try again."));
285 exit(0);
286 }
287 #endif
288 configDir_ = configDir.path();
289 cacheDir_ = cacheDir.path();
290 dataDir_ = dataDir.path();
291
292 // To prevent from multiple startup of import wizard
293 if (ActiveProfiles::instance()->isActive("import_wizard")) {
294 exit(0);
295 }
296
297 if (!configDir.exists() && !dataDir.exists() && !cacheDir.exists()) {
298 HomeDirMigration dlg;
299
300 if (dlg.checkOldHomeDir()) {
301 ActiveProfiles::instance()->setThisProfile("import_wizard");
302 QSettings s(dlg.oldHomeDir() + "/psirc", QSettings::IniFormat);
303 QString lastLang = s.value("last_lang", QString()).toString();
304 if(lastLang.isEmpty()) {
305 lastLang = QLocale().name().section('_', 0, 0);
306 }
307 TranslationManager::instance()->loadTranslation(lastLang);
308 dlg.exec();
309 ActiveProfiles::instance()->unsetThisProfile();
310 }
311 }
312 if (!dataDir.exists()) {
313 dataDir.mkpath(".");
314 }
315 if (!cacheDir.exists()) {
316 cacheDir.mkpath(".");
317 }
318 }
319 else {
320 cacheDir_ = configDir_;
321 dataDir_ = configDir_;
322 }
323 }
324
325 QString ret;
326 switch(type) {
327 case ApplicationInfo::ConfigLocation:
328 ret = configDir_;
329 break;
330
331 case ApplicationInfo::DataLocation:
332 ret = dataDir_;
333 break;
334
335 case ApplicationInfo::CacheLocation:
336 ret = cacheDir_;
337 break;
338 }
339 return ret;
340 }
341
makeSubhomePath(const QString & path,ApplicationInfo::HomedirType type)342 QString ApplicationInfo::makeSubhomePath(const QString &path, ApplicationInfo::HomedirType type)
343 {
344 if (path.indexOf("..") == -1) { // ensure its in home dir
345 QDir dir(homeDir(type) + "/" + path);
346 if (!dir.exists()) {
347 dir.mkpath(".");
348 }
349 return dir.path();
350 }
351 return QString();
352 }
353
makeSubprofilePath(const QString & path,ApplicationInfo::HomedirType type)354 QString ApplicationInfo::makeSubprofilePath(const QString &path, ApplicationInfo::HomedirType type)
355 {
356 if (path.indexOf("..") == -1) { // ensure its in profile dir
357 QDir dir(pathToProfile(activeProfile, type) + "/" + path);
358 if (!dir.exists()) {
359 dir.mkpath(".");
360 }
361 return dir.path();
362 }
363 return QString();
364 }
365
historyDir()366 QString ApplicationInfo::historyDir()
367 {
368 return makeSubprofilePath("history", ApplicationInfo::DataLocation);
369 }
370
vCardDir()371 QString ApplicationInfo::vCardDir()
372 {
373 return makeSubprofilePath("vcard", ApplicationInfo::CacheLocation);
374 }
375
bobDir()376 QString ApplicationInfo::bobDir()
377 {
378 return makeSubhomePath("bob", ApplicationInfo::CacheLocation);
379 }
380
profilesDir(ApplicationInfo::HomedirType type)381 QString ApplicationInfo::profilesDir(ApplicationInfo::HomedirType type)
382 {
383 return makeSubhomePath("profiles", type);
384 }
385
currentProfileDir(ApplicationInfo::HomedirType type)386 QString ApplicationInfo::currentProfileDir(ApplicationInfo::HomedirType type)
387 {
388 return pathToProfile(activeProfile, type);
389 }
390
desktopFile()391 QString ApplicationInfo::desktopFile()
392 {
393 QString dFile;
394 const QString _desktopFile(xstr(APP_PREFIX) "/share/applications/" xstr(APP_BIN_NAME) ".desktop");
395 QFile f(_desktopFile);
396 if(f.open(QIODevice::ReadOnly)) {
397 dFile = QString::fromUtf8(f.readAll());
398 }
399 return dFile;
400 }
401