File indexing completed on 2024-05-12 16:39:36

0001 /* This file is part of the KDE project
0002    Copyright (C) 2003-2014 Jarosław Staniek <staniek@kde.org>
0003 
0004    This program is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU Library General Public
0006    License as published by the Free Software Foundation; either
0007    version 2 of the License, or (at your option) any later version.
0008 
0009    This program is distributed in the hope that it will be useful,
0010    but WITHOUT ANY WARRANTY; without even the implied warranty of
0011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012    Library General Public License for more details.
0013 
0014    You should have received a copy of the GNU Library General Public License
0015    along with this program; see the file COPYING.  If not, write to
0016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017  * Boston, MA 02110-1301, USA.
0018  */
0019 
0020 #include "kexidbconnectionset.h"
0021 #include "kexidbshortcutfile.h"
0022 
0023 #include <KLocalizedString>
0024 
0025 #include <QDebug>
0026 #include <QDirIterator>
0027 #include <QFile>
0028 #include <QHash>
0029 #include <QStandardPaths>
0030 #include <QDir>
0031 
0032 //! @internal
0033 class KexiDBConnectionSetPrivate
0034 {
0035 public:
0036     KexiDBConnectionSetPrivate()
0037             : maxid(-1) {
0038     }
0039     QList<KDbConnectionData*> list;
0040     QHash<QString, QString> filenamesForData;
0041     QHash<QString, KDbConnectionData*> dataForFilenames;
0042     int maxid;
0043 };
0044 
0045 KexiDBConnectionSet::KexiDBConnectionSet()
0046         : QObject()
0047         , d(new KexiDBConnectionSetPrivate())
0048 {
0049 }
0050 
0051 KexiDBConnectionSet::~KexiDBConnectionSet()
0052 {
0053     delete d;
0054 }
0055 
0056 bool KexiDBConnectionSet::addConnectionData(KDbConnectionData *data, const QString& _filename)
0057 {
0058     if (!data)
0059         return false;
0060 /*! @todo KEXI3
0061     if (data->id < 0)
0062         data->id = d->maxid + 1;
0063     @todo check for id-duplicates
0064     d->maxid = qMax(d->maxid, data->id);
0065 */
0066     d->maxid++;
0067 
0068     QString filename(_filename);
0069     bool generateUniqueFilename = filename.isEmpty()
0070         || (!filename.isEmpty() && data == d->dataForFilenames.value(filename));
0071 
0072     if (generateUniqueFilename) {
0073         QString dir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)
0074                       + "/kexi/connections/";
0075         if (dir.isEmpty()) {
0076             m_result.setMessage(xi18n("Could not find location to save connection data file."));
0077             return false;
0078         }
0079         QString baseFileName(dir + (data->hostName().isEmpty() ? "localhost" : data->hostName()));
0080         int i = 0;
0081         while (QFile::exists(baseFileName + (i > 0 ? QString::number(i) : QString()) + ".kexic")) {
0082             i++;
0083         }
0084         if (!QDir(dir).exists()) {
0085             //make 'connections' dir and protect it
0086             //! @todo KEXI3 set permission of the created dirs to 0700
0087             if (!QDir().mkpath(dir)) {
0088                 m_result.setMessage(xi18n("Could not create folder <filename>%1</filename> for connection data file.", dir));
0089                 return false;
0090             }
0091             //! @todo change permission of every created subdir, see KStandardDirs::makeDir() create
0092             QFile(dir).setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ExeOwner);
0093         }
0094         filename = baseFileName + (i > 0 ? QString::number(i) : QString()) + ".kexic";
0095     }
0096     addConnectionDataInternal(data, filename);
0097     bool result = saveConnectionData(data, *data);
0098     if (!result)
0099         removeConnectionDataInternal(data);
0100     return result;
0101 }
0102 
0103 void KexiDBConnectionSet::addConnectionDataInternal(KDbConnectionData *data, const QString& filename)
0104 {
0105     d->filenamesForData.insert(key(*data), filename);
0106     d->dataForFilenames.insert(filename, data);
0107     d->list.append(data);
0108 }
0109 
0110 bool KexiDBConnectionSet::saveConnectionData(KDbConnectionData *oldData,
0111         const KDbConnectionData &newData)
0112 {
0113     if (!oldData)
0114         return false;
0115     const QString oldDataKey = key(*oldData);
0116     const QString filename(d->filenamesForData.value(oldDataKey));
0117     if (filename.isEmpty()) {
0118         m_result.setCode(ERR_OTHER);
0119         return false;
0120     }
0121     KexiDBConnShortcutFile shortcutFile(filename);
0122     if (!shortcutFile.saveConnectionData(newData, newData.savePassword())) {
0123         m_result = shortcutFile.result();
0124         return false;
0125     }
0126     if (oldData != &newData) {
0127         *oldData = newData;
0128     }
0129     const QString newDataKey = key(newData);
0130     if (oldDataKey != newDataKey) {
0131         // update file info: key changed, filename is untouched
0132         d->filenamesForData.remove(oldDataKey);
0133         d->filenamesForData.insert(newDataKey, filename);
0134     }
0135     return true;
0136 }
0137 
0138 void KexiDBConnectionSet::removeConnectionDataInternal(KDbConnectionData *data)
0139 {
0140     const QString filename(d->filenamesForData.value(key(*data)));
0141     d->filenamesForData.remove(key(*data));
0142     d->dataForFilenames.remove(filename);
0143     d->list.removeAt(d->list.indexOf(data));
0144 }
0145 
0146 bool KexiDBConnectionSet::removeConnectionData(KDbConnectionData *data)
0147 {
0148     if (!data)
0149         return false;
0150     const QString filename(d->filenamesForData.value(key(*data)));
0151     if (filename.isEmpty()) {
0152         m_result.setCode(ERR_OTHER);
0153         return false;
0154     }
0155     QFile file(filename);
0156     if (!file.remove()) {
0157         m_result.setMessage(xi18n("Could not remove connection file <filename>%1</filename>.",
0158                                   filename));
0159         return false;
0160     }
0161     removeConnectionDataInternal(data);
0162     return true;
0163 }
0164 
0165 QList<KDbConnectionData*> KexiDBConnectionSet::list() const
0166 {
0167     return d->list;
0168 }
0169 
0170 void KexiDBConnectionSet::clear()
0171 {
0172     d->list.clear();
0173     d->filenamesForData.clear();
0174     d->dataForFilenames.clear();
0175 }
0176 
0177 void KexiDBConnectionSet::load()
0178 {
0179     clear();
0180     const QStringList dirs(QStandardPaths::locateAll(QStandardPaths::GenericDataLocation,
0181                                                      // not KEXI_BASE_PATH because this version-independent path in user dir
0182                                                      "kexi/connections",
0183                                                      QStandardPaths::LocateDirectory));
0184     QSet<QString> foundDirs;
0185     foreach(const QString &dir, dirs) {
0186         const QString realDir(QDir(dir).canonicalPath());
0187         // make sure we don't visit the same dir twice to avoid duplication of connection data:
0188         // - follow symlinks
0189         // - QStandardPaths::locateAll sometimes returns duplicated entries (e.g. on Windows)
0190         if (foundDirs.contains(realDir)) {
0191             continue;
0192         }
0193         foundDirs.insert(realDir);
0194         QFileInfo realDirInfo(realDir);
0195         if (!realDirInfo.exists()) {
0196             continue;
0197         }
0198         QDirIterator it(dir, QStringList() << "*.kexic", QDir::Files | QDir::Readable,
0199                         QDirIterator::FollowSymlinks);
0200         while (it.hasNext()) {
0201             KDbConnectionData *data = new KDbConnectionData();
0202             KexiDBConnShortcutFile shortcutFile(it.next());
0203             if (!shortcutFile.loadConnectionData(data)) {
0204                 delete data;
0205                 continue;
0206             }
0207             addConnectionDataInternal(data, it.filePath());
0208             //qDebug() << file << "added.";
0209         }
0210    }
0211 }
0212 
0213 QString KexiDBConnectionSet::fileNameForConnectionData(const KDbConnectionData &data) const
0214 {
0215     return d->filenamesForData.value(key(data));
0216 }
0217 
0218 KDbConnectionData* KexiDBConnectionSet::connectionDataForFileName(const QString& fileName) const
0219 {
0220     return d->dataForFilenames.value(fileName);
0221 }
0222 
0223 // static
0224 QString KexiDBConnectionSet::key(const KDbConnectionData &data)
0225 {
0226     return data.driverId().toLower() + ','
0227         + data.userName().toLower() + ','
0228         + data.hostName().toLower() + ','
0229         + QString::number(data.port()) + ','
0230         + QString::number(data.useLocalSocketFile()) + ','
0231         + data.localSocketFileName() + ','
0232         + data.databaseName().replace(',', "\\,") + ','
0233         + QString::number(data.savePassword() ? 1 : 0) + ','
0234         + data.caption().replace(',', "\\,") + ','
0235         + data.description().replace(',', "\\,");
0236 }