File indexing completed on 2025-01-19 06:54:50
0001 // 0002 // C++ Implementation: cProfileManager 0003 // 0004 // Description: profile manager 0005 // 0006 /* 0007 Copyright 2008-2011 Tomas Mecir <kmuddy@kmuddy.com> 0008 0009 This program is free software; you can redistribute it and/or 0010 modify it under the terms of the GNU General Public License as 0011 published by the Free Software Foundation; either version 2 of 0012 the License, or (at your option) any later version. 0013 0014 This program is distributed in the hope that it will be useful, 0015 but WITHOUT ANY WARRANTY; without even the implied warranty of 0016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0017 GNU General Public License for more details. 0018 0019 You should have received a copy of the GNU General Public License 0020 along with this program. If not, see <http://www.gnu.org/licenses/>. 0021 */ 0022 0023 #include "cprofilemanager.h" 0024 0025 #include <QAbstractTableModel> 0026 #include <QDebug> 0027 #include <QDir> 0028 #include <QFile> 0029 #include <QXmlStreamReader> 0030 #include <QXmlStreamWriter> 0031 0032 #include <KLocalizedString> 0033 0034 #include <map> 0035 0036 #include "cprofilesettings.h" 0037 0038 #include "cglobalsettings.h" 0039 0040 using namespace std; 0041 0042 // cProfileModel - a model showing all the profiles. 0043 class cProfileModel : public QAbstractTableModel { 0044 public: 0045 cProfileModel (cProfileManager *manager) 0046 : QAbstractTableModel (nullptr), mgr (manager) 0047 { 0048 } 0049 0050 ~cProfileModel () override 0051 { 0052 } 0053 0054 int columnCount (const QModelIndex &parent = QModelIndex()) const override 0055 { 0056 if (parent.isValid()) return 0; // because Qt docs say so 0057 return 4; // we have 4 columns 0058 } 0059 0060 int rowCount (const QModelIndex &parent = QModelIndex()) const override 0061 { 0062 if (parent.isValid()) return 0; // because Qt docs say so 0063 return mgr->profileList().size(); 0064 } 0065 0066 QVariant headerData ( int section, Qt::Orientation /*orientation*/, 0067 int role = Qt::DisplayRole ) const override 0068 { 0069 if (role != Qt::DisplayRole) return QVariant(); 0070 switch (section) { 0071 case 0: return i18n ("Profile name"); 0072 case 1: return i18n ("Server"); 0073 case 2: return i18n ("Port"); 0074 case 3: return i18n ("Login name"); 0075 default: return QVariant(); 0076 } 0077 } 0078 0079 QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const override 0080 { 0081 // display role only 0082 if (role != Qt::DisplayRole) return QVariant(); 0083 0084 if (index.parent().isValid()) return QVariant(); 0085 int row = index.row(); 0086 int col = index.column(); 0087 if ((col < 0) || (col > 3)) return QVariant(); 0088 0089 if ((row < 0) || (row > mgr->profileList().size())) return QVariant(); 0090 QString profile = mgr->profileList()[row]; 0091 // okay, we have the profile 0092 cProfileSettings *sett = mgr->settings (profile); 0093 // now fetch the requested information and return it 0094 switch (col) { 0095 case 0: return mgr->visibleProfileName (profile); 0096 case 1: return sett->getString ("server"); 0097 case 2: return sett->getInt ("port"); 0098 case 3: return sett->getString ("login"); 0099 default: return QVariant(); 0100 }; 0101 } 0102 0103 void rowChanged (int row) { 0104 emit dataChanged (index (row, 0), index(row, 3)); 0105 } 0106 0107 void addRow (int row) { 0108 beginInsertRows (QModelIndex(), row, row); 0109 } 0110 0111 void rowAdded () { 0112 endInsertRows (); 0113 } 0114 0115 void removeRow (int row) { 0116 beginRemoveRows (QModelIndex(), row, row); 0117 } 0118 0119 void rowRemoved () { 0120 endRemoveRows (); 0121 } 0122 0123 private: 0124 cProfileManager *mgr; 0125 }; 0126 0127 struct cProfileManager::Private { 0128 QStringList profiles; 0129 map<QString, QString> profileNames; 0130 map<int, QString> sessions; 0131 map<QString, cProfileSettings *> profileSettings; 0132 0133 cProfileModel *model; 0134 }; 0135 0136 cProfileManager *cProfileManager::_self = nullptr; 0137 0138 cProfileManager *cProfileManager::self () 0139 { 0140 if (!_self) { 0141 _self = new cProfileManager; 0142 // we can't load in the constructor, because we need the self() pointer there 0143 _self->load (); 0144 /** Initialize the model. */ 0145 _self->init (); 0146 } 0147 return _self; 0148 } 0149 0150 cProfileManager::cProfileManager () 0151 { 0152 d = new Private; 0153 } 0154 0155 cProfileManager::~cProfileManager () 0156 { 0157 map<QString, cProfileSettings *>::iterator it; 0158 for (it = d->profileSettings.begin(); it != d->profileSettings.end(); ++it) 0159 delete it->second; 0160 delete d->model; 0161 delete d; 0162 } 0163 0164 QAbstractTableModel *cProfileManager::model () const 0165 { 0166 return d->model; 0167 } 0168 0169 QStringList cProfileManager::profileList () const 0170 { 0171 return d->profiles; 0172 } 0173 0174 bool cProfileManager::profileExists (const QString &name) 0175 { 0176 return (d->profileNames.count (name) != 0); 0177 } 0178 0179 QString cProfileManager::profilePath (const QString &profileName) const 0180 { 0181 QString path = cGlobalSettings::self()->profilePath(); 0182 return path + "/" + profileName + "/"; 0183 } 0184 0185 void cProfileManager::assignSession (int sess, const QString &profileName) 0186 { 0187 if (d->profileNames.count (profileName)) 0188 d->sessions[sess] = profileName; 0189 } 0190 0191 bool cProfileManager::sessionAssigned (int sess) 0192 { 0193 return (d->sessions.count (sess)); 0194 } 0195 0196 QString cProfileManager::profileName (int sess) 0197 { 0198 if (!sessionAssigned (sess)) return QString(); 0199 return d->sessions[sess]; 0200 } 0201 0202 bool cProfileManager::hasSessionAssigned (const QString &profile) 0203 { 0204 map<int, QString>::iterator it; 0205 for (it = d->sessions.begin(); it != d->sessions.end(); ++it) 0206 if (it->second == profile) 0207 return true; 0208 return false; 0209 } 0210 0211 void cProfileManager::unassignSession (int sess) 0212 { 0213 // save session settings 0214 if (sessionAssigned (sess)) { 0215 cProfileSettings *s = settings (sess); 0216 if (s) s->save(); 0217 } 0218 // and remove it 0219 d->sessions.erase (sess); 0220 } 0221 0222 QString cProfileManager::profilePath (int sess) 0223 { 0224 if (d->sessions.count (sess)) 0225 return profilePath (d->sessions[sess]); 0226 // return QString(); // not found 0227 return cGlobalSettings::self()->profilePath() + "/"; 0228 } 0229 0230 QString cProfileManager::visibleProfileName (QString name) 0231 { 0232 if (d->profileNames.count (name)) 0233 return d->profileNames[name]; 0234 return QString(); 0235 } 0236 0237 cProfileSettings *cProfileManager::settings (const QString &profileName) 0238 { 0239 if (d->profileSettings.count (profileName)) 0240 return d->profileSettings[profileName]; 0241 return nullptr; 0242 } 0243 0244 cProfileSettings *cProfileManager::settings (int sess) 0245 { 0246 QString name; 0247 if (!d->sessions.count (sess)) return nullptr; 0248 name = d->sessions[sess]; 0249 if (d->profileSettings.count (name)) 0250 return d->profileSettings[name]; 0251 return nullptr; 0252 } 0253 0254 QString cProfileManager::newProfile (const QString &name) 0255 { 0256 // find first available directory name 0257 0258 QDir mainDir (cGlobalSettings::self()->profilePath()); 0259 int num = 0; 0260 QString dirname; 0261 bool found = false; 0262 while (num < 1000000) { // nobody will have more than a million profiles :) 0263 ++num; 0264 dirname = "profile" + QString::number (num); 0265 // check if the profile and/or directory name exists 0266 if (d->profileNames.count (dirname)) continue; 0267 if (mainDir.exists (dirname)) continue; 0268 // they do not - we're good to go 0269 found = true; 0270 break; 0271 } 0272 if (!found) return QString(); 0273 0274 // notify the model 0275 d->model->addRow (d->profiles.size()); 0276 0277 // create the directory 0278 mainDir.mkdir (dirname); 0279 0280 // adjust the structures 0281 d->profiles.push_back (dirname); 0282 d->profileNames[dirname] = name; 0283 cProfileSettings *s = new cProfileSettings (dirname); 0284 d->profileSettings[dirname] = s; 0285 0286 // we are done - notify the model 0287 d->model->rowAdded (); 0288 0289 save (); 0290 return dirname; 0291 } 0292 0293 bool cProfileManager::renameProfile (const QString &name, const QString &newName) 0294 { 0295 d->profileNames[name] = newName; 0296 profileInfoChanged (name); 0297 save (); 0298 return true; 0299 } 0300 0301 bool cProfileManager::deleteProfile (const QString &name, bool deleteFiles) 0302 { 0303 // check if any open connection uses the profile 0304 if (hasSessionAssigned (name)) return false; 0305 if (!profileExists (name)) return false; 0306 0307 int idx = d->profiles.indexOf (name); 0308 d->model->removeRow (idx); 0309 0310 // remove the profile 0311 d->profiles.removeAll (name); 0312 d->profileNames.erase (name); 0313 if (d->profileSettings.count (name)) 0314 delete d->profileSettings[name]; 0315 d->profileSettings.erase (name); 0316 0317 // delete files, if requested 0318 if (deleteFiles) { 0319 QString path = profilePath (name); 0320 QDir dir (path); 0321 QFileInfoList list = dir.entryInfoList(); 0322 for (int i = 0; i < list.size(); ++i) { 0323 QFileInfo fileInfo = list.at(i); 0324 dir.remove (fileInfo.fileName()); 0325 } 0326 QDir mainDir (cGlobalSettings::self()->profilePath()); 0327 mainDir.rmdir (name); 0328 } 0329 0330 d->model->rowRemoved (); 0331 save (); 0332 return true; 0333 } 0334 0335 bool cProfileManager::duplicateProfile (const QString &name, const QString &newName) 0336 { 0337 // create a new profile 0338 QString profile = newProfile (newName); 0339 if (profile.isEmpty()) return false; 0340 0341 // and copy over everything to it 0342 QString path = profilePath (name); 0343 QString path2 = profilePath (profile); 0344 QDir dir (path); 0345 QFileInfoList list = dir.entryInfoList(); 0346 for (int i = 0; i < list.size(); ++i) { 0347 QFileInfo fileInfo = list.at(i); 0348 QFile(path + "/" + fileInfo.fileName()).copy (path2 + "/" + fileInfo.fileName()); 0349 } 0350 0351 // and reload the settings 0352 cProfileSettings *sett = new cProfileSettings (profile); 0353 if (d->profileSettings.count (profile)) 0354 delete d->profileSettings[profile]; 0355 d->profileSettings[profile] = sett; 0356 sett->save (); // just in case the old settings have overwritten the XML 0357 0358 profileInfoChanged (profile); 0359 save (); 0360 return true; 0361 } 0362 0363 void cProfileManager::profileInfoChanged (const QString &name) 0364 { 0365 int idx = d->profiles.indexOf (name); 0366 if (idx >= 0) 0367 d->model->rowChanged (idx); 0368 } 0369 0370 // TODO: error reporting !!! 0371 void cProfileManager::load () 0372 { 0373 // create the profile directory if it does not exist 0374 QString path = cGlobalSettings::self()->profilePath(); 0375 QDir dir = QDir (path); 0376 if (!dir.exists()) QDir::root().mkpath (dir.absolutePath()); 0377 0378 // create the XML loader 0379 QFile f (path + "/profiles.xml"); 0380 if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { 0381 qDebug() << "No profiles file - nothing to do."; 0382 return; // no profiles - nothing to do 0383 } 0384 QXmlStreamReader *reader = new QXmlStreamReader (&f); 0385 0386 // load the profiles 0387 reader->readNext (); // read the document start 0388 reader->readNext (); 0389 if (reader->isStartElement ()) 0390 if (reader->name() == "profiles") 0391 if (reader->attributes().value ("version") == "1.0") { 0392 // okay, read the list 0393 while (!reader->atEnd()) { 0394 reader->readNext (); 0395 if (reader->isStartElement () && (reader->name() == "profile")) { 0396 // found another profile 0397 QString id = reader->attributes().value ("id").toString(); 0398 QString name = reader->attributes().value ("name").toString(); 0399 if (!id.isEmpty() && (!d->profileNames.count (id))) { 0400 // add the profile 0401 d->profiles.push_back (id); 0402 d->profileNames[id] = name; 0403 cProfileSettings *sett = new cProfileSettings (id); 0404 d->profileSettings[id] = sett; 0405 } 0406 } 0407 } 0408 } else reader->raiseError ("Unknown profile file version."); 0409 else reader->raiseError ("This is not a valid profile list file."); 0410 else reader->raiseError ("This file is corrupted."); 0411 0412 if (reader->hasError()) { 0413 qDebug() << ("Error in profiles.xml at line " + QString::number (reader->lineNumber()) + ", column " + QString::number (reader->columnNumber()) + QString (": ") + reader->errorString()); 0414 } 0415 0416 // close the file 0417 f.close (); 0418 delete reader; 0419 } 0420 0421 void cProfileManager::init () 0422 { 0423 d->model = new cProfileModel (this); 0424 } 0425 0426 void cProfileManager::save () 0427 { 0428 // create the profile directory if it does not exist 0429 QString path = cGlobalSettings::self()->profilePath(); 0430 QDir dir = QDir (path); 0431 if (!dir.exists()) QDir::root().mkpath (dir.absolutePath()); 0432 0433 // backup the old profile file, if any 0434 dir.remove ("profiles.backup"); 0435 if (!QFile(path + "/profiles.xml").copy (path + "/profiles.backup")) { 0436 qDebug() << "Unable to backup profiles.xml."; // not fatal, may simply not exist 0437 } 0438 0439 QFile f (path + "/profiles.xml"); 0440 if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) 0441 { 0442 qDebug() << "Unable to open profile.xml for writing."; 0443 return; 0444 } 0445 // save the profile file 0446 QXmlStreamWriter *writer = new QXmlStreamWriter (&f); 0447 0448 writer->setAutoFormatting (true); // make the generated XML more readable 0449 writer->writeStartDocument (); 0450 0451 writer->writeStartElement ("profiles"); 0452 writer->writeAttribute ("version", "1.0"); 0453 0454 map<QString, QString>::iterator it; 0455 for (it = d->profileNames.begin(); it != d->profileNames.end(); ++it) { 0456 writer->writeStartElement ("profile"); 0457 writer->writeAttribute ("id", it->first); 0458 writer->writeAttribute ("name", it->second); 0459 writer->writeEndElement (); 0460 } 0461 0462 writer->writeEndElement (); 0463 writer->writeEndDocument (); 0464 0465 f.close (); 0466 delete writer; 0467 } 0468