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 }