1 /*
2  * Bittorrent Client using Qt and libtorrent.
3  * Copyright (C) 2016  Eugene Shalygin <eugene.shalygin@gmail.com>
4  * Copyright (C) 2012  Christophe Dumez
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  *
20  * In addition, as a special exception, the copyright holders give permission to
21  * link this program with the OpenSSL project's "OpenSSL" library (or with
22  * modified versions of it that use the same license as the "OpenSSL" library),
23  * and distribute the linked executables. You must obey the GNU General Public
24  * License in all respects for all of the code used other than "OpenSSL".  If you
25  * modify file(s), you may extend this exception to your version of the file(s),
26  * but you are not obligated to do so. If you do not wish to do so, delete this
27  * exception statement from your version.
28  */
29 
30 #include "profile_p.h"
31 
32 #include <QCoreApplication>
33 
Profile(const QString & configurationName)34 Private::Profile::Profile(const QString &configurationName)
35     : m_configurationSuffix {configurationName.isEmpty() ? QString() : QLatin1Char('_') + configurationName}
36 {
37 }
38 
configurationSuffix() const39 QString Private::Profile::configurationSuffix() const
40 {
41     return m_configurationSuffix;
42 }
43 
profileName() const44 QString Private::Profile::profileName() const
45 {
46     return QCoreApplication::applicationName() + configurationSuffix();
47 }
48 
DefaultProfile(const QString & configurationName)49 Private::DefaultProfile::DefaultProfile(const QString &configurationName)
50     : Profile(configurationName)
51 {
52 }
53 
baseDirectory() const54 QString Private::DefaultProfile::baseDirectory() const
55 {
56     return QDir::homePath();
57 }
58 
cacheLocation() const59 QString Private::DefaultProfile::cacheLocation() const
60 {
61     return locationWithConfigurationName(QStandardPaths::CacheLocation);
62 }
63 
configLocation() const64 QString Private::DefaultProfile::configLocation() const
65 {
66 #if defined(Q_OS_WIN)
67     // On Windows QSettings stores files in FOLDERID_RoamingAppData\AppName
68     return locationWithConfigurationName(QStandardPaths::AppDataLocation);
69 #else
70     return locationWithConfigurationName(QStandardPaths::AppConfigLocation);
71 #endif
72 }
73 
dataLocation() const74 QString Private::DefaultProfile::dataLocation() const
75 {
76 #if defined(Q_OS_WIN) || defined (Q_OS_MACOS)
77     return locationWithConfigurationName(QStandardPaths::AppLocalDataLocation);
78 #else
79     // On Linux keep using the legacy directory ~/.local/share/data/ if it exists
80     const QString legacyDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)
81         + QLatin1String("/data/") + profileName() + QLatin1Char('/');
82 
83     const QString dataDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)
84         + QLatin1Char('/') + profileName() + QLatin1Char('/');
85 
86     if (!QDir(dataDir).exists() && QDir(legacyDir).exists())
87     {
88         qWarning("The legacy data directory '%s' is used. It is recommended to move its content to '%s'",
89             qUtf8Printable(legacyDir), qUtf8Printable(dataDir));
90 
91         return legacyDir;
92     }
93 
94     return dataDir;
95 #endif
96 }
97 
downloadLocation() const98 QString Private::DefaultProfile::downloadLocation() const
99 {
100     return QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
101 }
102 
applicationSettings(const QString & name) const103 SettingsPtr Private::DefaultProfile::applicationSettings(const QString &name) const
104 {
105 #if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
106     return SettingsPtr(new QSettings(QSettings::IniFormat, QSettings::UserScope, profileName(), name));
107 #else
108     return SettingsPtr(new QSettings(profileName(), name));
109 #endif
110 }
111 
locationWithConfigurationName(const QStandardPaths::StandardLocation location) const112 QString Private::DefaultProfile::locationWithConfigurationName(const QStandardPaths::StandardLocation location) const
113 {
114     return QStandardPaths::writableLocation(location) + configurationSuffix();
115 }
116 
CustomProfile(const QString & rootPath,const QString & configurationName)117 Private::CustomProfile::CustomProfile(const QString &rootPath, const QString &configurationName)
118     : Profile {configurationName}
119     , m_rootDirectory {QDir(rootPath).absoluteFilePath(this->profileName())}
120 {
121 }
122 
baseDirectory() const123 QString Private::CustomProfile::baseDirectory() const
124 {
125     return m_rootDirectory.canonicalPath();
126 }
127 
cacheLocation() const128 QString Private::CustomProfile::cacheLocation() const
129 {
130     return m_rootDirectory.absoluteFilePath(QLatin1String(cacheDirName));
131 }
132 
configLocation() const133 QString Private::CustomProfile::configLocation() const
134 {
135     return m_rootDirectory.absoluteFilePath(QLatin1String(configDirName));
136 }
137 
dataLocation() const138 QString Private::CustomProfile::dataLocation() const
139 {
140     return m_rootDirectory.absoluteFilePath(QLatin1String(dataDirName));
141 }
142 
downloadLocation() const143 QString Private::CustomProfile::downloadLocation() const
144 {
145     return m_rootDirectory.absoluteFilePath(QLatin1String(downloadsDirName));
146 }
147 
applicationSettings(const QString & name) const148 SettingsPtr Private::CustomProfile::applicationSettings(const QString &name) const
149 {
150     // here we force QSettings::IniFormat format always because we need it to be portable across platforms
151 #if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
152     constexpr const char *CONF_FILE_EXTENSION = ".ini";
153 #else
154     constexpr const char *CONF_FILE_EXTENSION = ".conf";
155 #endif
156     const QString settingsFileName {QDir(configLocation()).absoluteFilePath(name + QLatin1String(CONF_FILE_EXTENSION))};
157     return SettingsPtr(new QSettings(settingsFileName, QSettings::IniFormat));
158 }
159 
fromPortablePath(const QString & portablePath) const160 QString Private::NoConvertConverter::fromPortablePath(const QString &portablePath) const
161 {
162     return portablePath;
163 }
164 
toPortablePath(const QString & path) const165 QString Private::NoConvertConverter::toPortablePath(const QString &path) const
166 {
167     return path;
168 }
169 
Converter(const QString & basePath)170 Private::Converter::Converter(const QString &basePath)
171     : m_baseDir {basePath}
172 {
173     m_baseDir.makeAbsolute();
174 }
175 
toPortablePath(const QString & path) const176 QString Private::Converter::toPortablePath(const QString &path) const
177 {
178     if (path.isEmpty() || m_baseDir.path().isEmpty())
179         return path;
180 
181 #ifdef Q_OS_WIN
182     if (QDir::isAbsolutePath(path))
183     {
184         const QChar driveLeter = path[0].toUpper();
185         const QChar baseDriveLetter = m_baseDir.path()[0].toUpper();
186         const bool onSameDrive = (driveLeter.category() == QChar::Letter_Uppercase) && (driveLeter == baseDriveLetter);
187         if (!onSameDrive)
188             return path;
189     }
190 #endif
191     return m_baseDir.relativeFilePath(path);
192 }
193 
fromPortablePath(const QString & portablePath) const194 QString Private::Converter::fromPortablePath(const QString &portablePath) const
195 {
196     if (portablePath.isEmpty() || QDir::isAbsolutePath(portablePath))
197         return portablePath;
198 
199     return QDir::cleanPath(m_baseDir.absoluteFilePath(portablePath));
200 }
201