1 /*
2  *  Copyright (C) 2011 Felix Geyer <debfx@fobos.de>
3  *
4  *  This program is free software: you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation, either version 2 or (at your option)
7  *  version 3 of the License.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "FilePath.h"
19 
20 #include <QCoreApplication>
21 #include <QDir>
22 #include <QLibrary>
23 
24 #include "config-keepassx.h"
25 #include "core/Global.h"
26 
27 FilePath* FilePath::m_instance(nullptr);
28 
dataPath(const QString & name)29 QString FilePath::dataPath(const QString& name)
30 {
31     if (name.isEmpty() || name.startsWith('/')) {
32         return m_dataPath + name;
33     }
34     else {
35         return m_dataPath + "/" + name;
36     }
37 }
38 
pluginPath(const QString & name)39 QString FilePath::pluginPath(const QString& name)
40 {
41     QStringList pluginPaths;
42 
43     QDir buildDir(QCoreApplication::applicationDirPath() + "/autotype");
44     const QStringList buildDirEntryList = buildDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
45     for (const QString& dir : buildDirEntryList) {
46         pluginPaths << QCoreApplication::applicationDirPath() + "/autotype/" + dir;
47     }
48 
49     // for TestAutoType
50     pluginPaths << QCoreApplication::applicationDirPath() + "/../src/autotype/test";
51 
52     pluginPaths << QCoreApplication::applicationDirPath();
53 
54     QString configuredPluginDir = KEEPASSX_PLUGIN_DIR;
55     if (configuredPluginDir != ".") {
56         if (QDir(configuredPluginDir).isAbsolute()) {
57             pluginPaths << configuredPluginDir;
58         }
59         else {
60             QString relativePluginDir = QString("%1/../%2")
61                     .arg(QCoreApplication::applicationDirPath(), configuredPluginDir);
62             pluginPaths << QDir(relativePluginDir).canonicalPath();
63 
64             QString absolutePluginDir = QString("%1/%2")
65                     .arg(KEEPASSX_PREFIX_DIR, configuredPluginDir);
66             pluginPaths << QDir(absolutePluginDir).canonicalPath();
67         }
68     }
69 
70     QStringList dirFilter;
71     dirFilter << QString("*%1*").arg(name);
72 
73     for (const QString& path : asConst(pluginPaths)) {
74         const QStringList fileCandidates = QDir(path).entryList(dirFilter, QDir::Files);
75 
76         for (const QString& file : fileCandidates) {
77             QString filePath = path + "/" + file;
78 
79             if (QLibrary::isLibrary(filePath)) {
80                 return filePath;
81             }
82         }
83     }
84 
85     return QString();
86 }
87 
applicationIcon()88 QIcon FilePath::applicationIcon()
89 {
90     return icon("apps", "keepassx");
91 }
92 
icon(const QString & category,const QString & name,bool fromTheme)93 QIcon FilePath::icon(const QString& category, const QString& name, bool fromTheme)
94 {
95     QString combinedName = category + "/" + name;
96 
97     QIcon icon = m_iconCache.value(combinedName);
98 
99     if (!icon.isNull()) {
100         return icon;
101     }
102 
103     if (fromTheme) {
104         icon = QIcon::fromTheme(name);
105     }
106 
107     if (icon.isNull()) {
108         const QList<int> pngSizes = { 16, 22, 24, 32, 48, 64, 128 };
109         QString filename;
110         for (int size : pngSizes) {
111             filename = QString("%1/icons/application/%2x%2/%3.png").arg(m_dataPath, QString::number(size),
112                                                                         combinedName);
113             if (QFile::exists(filename)) {
114                 icon.addFile(filename, QSize(size, size));
115             }
116         }
117         filename = QString("%1/icons/application/scalable/%2.svgz").arg(m_dataPath, combinedName);
118         if (QFile::exists(filename)) {
119             icon.addFile(filename);
120         }
121     }
122 
123     m_iconCache.insert(combinedName, icon);
124 
125     return icon;
126 }
127 
onOffIcon(const QString & category,const QString & name)128 QIcon FilePath::onOffIcon(const QString& category, const QString& name)
129 {
130     QString combinedName = category + "/" + name;
131     QString cacheName = "onoff/" + combinedName;
132 
133     QIcon icon = m_iconCache.value(cacheName);
134 
135     if (!icon.isNull()) {
136         return icon;
137     }
138 
139     for (int i = 0; i < 2; i++) {
140         QIcon::State state;
141         QString stateName;
142 
143         if (i == 0) {
144             state = QIcon::Off;
145             stateName = "off";
146         }
147         else {
148             state = QIcon::On;
149             stateName = "on";
150         }
151 
152         const QList<int> pngSizes = { 16, 22, 24, 32, 48, 64, 128 };
153         QString filename;
154         for (int size : pngSizes) {
155             filename = QString("%1/icons/application/%2x%2/%3-%4.png").arg(m_dataPath, QString::number(size),
156                                                                            combinedName, stateName);
157             if (QFile::exists(filename)) {
158                 icon.addFile(filename, QSize(size, size), QIcon::Normal, state);
159             }
160         }
161         filename = QString("%1/icons/application/scalable/%2-%3.svgz").arg(m_dataPath, combinedName, stateName);
162         if (QFile::exists(filename)) {
163             icon.addFile(filename, QSize(), QIcon::Normal, state);
164         }
165     }
166 
167     m_iconCache.insert(cacheName, icon);
168 
169     return icon;
170 }
171 
FilePath()172 FilePath::FilePath()
173 {
174     const QString appDirPath = QCoreApplication::applicationDirPath();
175     bool isDataDirAbsolute = QDir::isAbsolutePath(KEEPASSX_DATA_DIR);
176     Q_UNUSED(isDataDirAbsolute);
177 
178     if (false) {
179     }
180 #ifdef QT_DEBUG
181     else if (testSetDir(QString(KEEPASSX_SOURCE_DIR) + "/share")) {
182     }
183 #endif
184 #if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
185     else if (isDataDirAbsolute && testSetDir(KEEPASSX_DATA_DIR)) {
186     }
187     else if (!isDataDirAbsolute && testSetDir(QString("%1/../%2").arg(appDirPath, KEEPASSX_DATA_DIR))) {
188     }
189     else if (!isDataDirAbsolute && testSetDir(QString("%1/%2").arg(KEEPASSX_PREFIX_DIR, KEEPASSX_DATA_DIR))) {
190     }
191 #endif
192 #ifdef Q_OS_MAC
193     else if (testSetDir(appDirPath + "/../Resources")) {
194     }
195 #endif
196 #ifdef Q_OS_WIN
197     else if (testSetDir(appDirPath + "/share")) {
198     }
199 #endif
200 
201     if (m_dataPath.isEmpty()) {
202         qWarning("FilePath::DataPath: can't find data dir");
203     }
204     else {
205         m_dataPath = QDir::cleanPath(m_dataPath);
206     }
207 }
208 
testSetDir(const QString & dir)209 bool FilePath::testSetDir(const QString& dir)
210 {
211     if (QFile::exists(dir + "/icons/database/C00_Password.png")) {
212         m_dataPath = dir;
213         return true;
214     }
215     else {
216         return false;
217     }
218 }
219 
instance()220 FilePath* FilePath::instance()
221 {
222     if (!m_instance) {
223         m_instance = new FilePath();
224     }
225 
226     return m_instance;
227 }
228