File indexing completed on 2024-03-24 05:04:37
0001 /* 0002 This class handles the bookmarks. 0003 0004 SPDX-FileCopyrightText: 2004-2023 Alexander Reinholdt <alexander.reinholdt@kdemail.net> 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 // application specific includes 0009 #include "smb4kbookmarkhandler.h" 0010 #include "smb4kbookmark.h" 0011 #include "smb4kglobal.h" 0012 #include "smb4khomesshareshandler.h" 0013 #include "smb4khost.h" 0014 #include "smb4knotification.h" 0015 #include "smb4kprofilemanager.h" 0016 #include "smb4ksettings.h" 0017 #include "smb4kshare.h" 0018 0019 // Qt includes 0020 #include <QApplication> 0021 #include <QDir> 0022 #include <QFile> 0023 #include <QMutableListIterator> 0024 #include <QPointer> 0025 #include <QTextStream> 0026 #include <QXmlStreamReader> 0027 #include <QXmlStreamWriter> 0028 0029 // KDE includes 0030 #include <KLocalizedString> 0031 0032 using namespace Smb4KGlobal; 0033 0034 class Smb4KBookmarkHandlerPrivate 0035 { 0036 public: 0037 QList<BookmarkPtr> bookmarks; 0038 }; 0039 0040 class Smb4KBookmarkHandlerStatic 0041 { 0042 public: 0043 Smb4KBookmarkHandler instance; 0044 }; 0045 0046 Q_GLOBAL_STATIC(Smb4KBookmarkHandlerStatic, p); 0047 0048 Smb4KBookmarkHandler::Smb4KBookmarkHandler(QObject *parent) 0049 : QObject(parent) 0050 , d(new Smb4KBookmarkHandlerPrivate) 0051 { 0052 QString path = dataLocation(); 0053 0054 QDir dir; 0055 0056 if (!dir.exists(path)) { 0057 dir.mkpath(path); 0058 } 0059 0060 readBookmarkList(); 0061 0062 connect(Smb4KProfileManager::self(), &Smb4KProfileManager::profileRemoved, this, &Smb4KBookmarkHandler::slotProfileRemoved); 0063 connect(Smb4KProfileManager::self(), &Smb4KProfileManager::profileMigrated, this, &Smb4KBookmarkHandler::slotProfileMigrated); 0064 } 0065 0066 Smb4KBookmarkHandler::~Smb4KBookmarkHandler() 0067 { 0068 while (!d->bookmarks.isEmpty()) { 0069 d->bookmarks.takeFirst().clear(); 0070 } 0071 } 0072 0073 Smb4KBookmarkHandler *Smb4KBookmarkHandler::self() 0074 { 0075 return &p->instance; 0076 } 0077 0078 void Smb4KBookmarkHandler::addBookmark(const BookmarkPtr &bookmark) 0079 { 0080 if (bookmark) { 0081 // 0082 // Create a list that will be passed to addBookmarks() 0083 // 0084 QList<BookmarkPtr> bookmarks; 0085 0086 // 0087 // Check if the share has already been bookmarked and skip it if it 0088 // already exists 0089 // 0090 BookmarkPtr knownBookmark = findBookmarkByUrl(bookmark->url()); 0091 0092 if (knownBookmark) { 0093 Smb4KNotification::bookmarkExists(knownBookmark); 0094 return; 0095 } 0096 0097 // 0098 // Copy the bookmark, add the correct profile (may be empty) and 0099 // add it to the list. 0100 // 0101 BookmarkPtr newBookmark = BookmarkPtr(bookmark); 0102 newBookmark->setProfile(Smb4KProfileManager::self()->activeProfile()); 0103 bookmarks << newBookmark; 0104 0105 // 0106 // Add the bookmark 0107 // 0108 addBookmarks(bookmarks, false); 0109 } 0110 } 0111 0112 void Smb4KBookmarkHandler::addBookmarks(const QList<BookmarkPtr> &list, bool replace) 0113 { 0114 // 0115 // Process the incoming list. 0116 // In case the internal list should be replaced, clear the internal 0117 // list first. 0118 // 0119 if (replace) { 0120 QMutableListIterator<BookmarkPtr> it(d->bookmarks); 0121 0122 while (it.hasNext()) { 0123 BookmarkPtr bookmark = it.next(); 0124 removeBookmark(bookmark); 0125 } 0126 } 0127 0128 // 0129 // Copy all bookmarks that are not in the list 0130 // 0131 for (const BookmarkPtr &bookmark : list) { 0132 // 0133 // Check if the bookmark label is already in use 0134 // 0135 if (!bookmark->label().isEmpty() && findBookmarkByLabel(bookmark->label())) { 0136 Smb4KNotification::bookmarkLabelInUse(bookmark); 0137 bookmark->setLabel(bookmark->label() + QStringLiteral(" (1)")); 0138 } 0139 0140 // 0141 // Check if we have to add the bookmark 0142 // 0143 BookmarkPtr existingBookmark = findBookmarkByUrl(bookmark->url()); 0144 0145 if (!existingBookmark) { 0146 d->bookmarks << bookmark; 0147 Q_EMIT bookmarkAdded(bookmark); 0148 } 0149 } 0150 0151 // 0152 // Save the bookmark list and emit the updated() signal 0153 // 0154 writeBookmarkList(); 0155 Q_EMIT updated(); 0156 } 0157 0158 void Smb4KBookmarkHandler::removeBookmark(const BookmarkPtr &bookmark) 0159 { 0160 if (bookmark) { 0161 for (int i = 0; i < d->bookmarks.size(); ++i) { 0162 if ((!Smb4KSettings::useProfiles() || Smb4KSettings::activeProfile() == d->bookmarks.at(i)->profile()) 0163 && QString::compare(d->bookmarks.at(i)->url().toString(QUrl::RemoveUserInfo | QUrl::RemovePort), 0164 bookmark->url().toString(QUrl::RemoveUserInfo | QUrl::RemovePort), 0165 Qt::CaseInsensitive) 0166 == 0 0167 && QString::compare(bookmark->categoryName(), d->bookmarks.at(i)->categoryName(), Qt::CaseInsensitive) == 0) { 0168 BookmarkPtr bookmark = d->bookmarks.takeAt(i); 0169 Q_EMIT bookmarkRemoved(bookmark); 0170 bookmark.clear(); 0171 break; 0172 } 0173 } 0174 0175 // Write the list to the bookmarks file. 0176 writeBookmarkList(); 0177 Q_EMIT updated(); 0178 } 0179 } 0180 0181 void Smb4KBookmarkHandler::removeCategory(const QString &name) 0182 { 0183 QMutableListIterator<BookmarkPtr> it(d->bookmarks); 0184 0185 while (it.hasNext()) { 0186 const BookmarkPtr &bookmark = it.next(); 0187 0188 if ((!Smb4KSettings::useProfiles() || Smb4KSettings::activeProfile() == bookmark->profile()) 0189 || QString::compare(bookmark->categoryName(), name, Qt::CaseInsensitive) == 0) { 0190 Q_EMIT bookmarkRemoved(bookmark); 0191 it.remove(); 0192 } 0193 } 0194 0195 // Write the list to the bookmarks file. 0196 writeBookmarkList(); 0197 Q_EMIT updated(); 0198 } 0199 0200 void Smb4KBookmarkHandler::writeBookmarkList() 0201 { 0202 QFile xmlFile(dataLocation() + QDir::separator() + QStringLiteral("bookmarks.xml")); 0203 0204 if (!d->bookmarks.isEmpty()) { 0205 if (xmlFile.open(QIODevice::WriteOnly | QIODevice::Text)) { 0206 QXmlStreamWriter xmlWriter(&xmlFile); 0207 xmlWriter.setAutoFormatting(true); 0208 xmlWriter.writeStartDocument(); 0209 xmlWriter.writeStartElement(QStringLiteral("bookmarks")); 0210 xmlWriter.writeAttribute(QStringLiteral("version"), QStringLiteral("3.1")); 0211 0212 for (const BookmarkPtr &bookmark : qAsConst(d->bookmarks)) { 0213 if (!bookmark->url().isValid()) { 0214 Smb4KNotification::invalidURLPassed(); 0215 continue; 0216 } 0217 0218 xmlWriter.writeStartElement(QStringLiteral("bookmark")); 0219 xmlWriter.writeAttribute(QStringLiteral("profile"), bookmark->profile()); 0220 xmlWriter.writeAttribute(QStringLiteral("category"), bookmark->categoryName()); 0221 0222 xmlWriter.writeTextElement(QStringLiteral("workgroup"), bookmark->workgroupName()); 0223 xmlWriter.writeTextElement(QStringLiteral("url"), bookmark->url().toString(QUrl::RemovePassword | QUrl::RemovePort)); 0224 xmlWriter.writeTextElement(QStringLiteral("ip"), bookmark->hostIpAddress()); 0225 xmlWriter.writeTextElement(QStringLiteral("label"), bookmark->label()); 0226 0227 xmlWriter.writeEndElement(); 0228 } 0229 0230 xmlWriter.writeEndDocument(); 0231 0232 xmlFile.close(); 0233 } else { 0234 Smb4KNotification::openingFileFailed(xmlFile); 0235 } 0236 } else { 0237 xmlFile.remove(); 0238 } 0239 } 0240 0241 void Smb4KBookmarkHandler::readBookmarkList() 0242 { 0243 // 0244 // Clear the list of bookmarks 0245 // 0246 while (!d->bookmarks.isEmpty()) { 0247 d->bookmarks.takeFirst().clear(); 0248 } 0249 0250 // 0251 // Locate the XML file and read the bookmarks 0252 // 0253 QFile xmlFile(dataLocation() + QDir::separator() + QStringLiteral("bookmarks.xml")); 0254 0255 if (xmlFile.open(QIODevice::ReadOnly | QIODevice::Text)) { 0256 QXmlStreamReader xmlReader(&xmlFile); 0257 0258 while (!xmlReader.atEnd()) { 0259 xmlReader.readNext(); 0260 0261 if (xmlReader.isStartElement()) { 0262 if (xmlReader.name() == QStringLiteral("bookmarks") 0263 && (xmlReader.attributes().value(QStringLiteral("version")) != QStringLiteral("3.0") 0264 && xmlReader.attributes().value(QStringLiteral("version")) != QStringLiteral("3.1"))) { 0265 xmlReader.raiseError(i18n("The format of %1 is not supported.", xmlFile.fileName())); 0266 break; 0267 } else { 0268 if (xmlReader.name() == QStringLiteral("bookmark")) { 0269 QString profile = xmlReader.attributes().value(QStringLiteral("profile")).toString(); 0270 0271 BookmarkPtr bookmark = BookmarkPtr(new Smb4KBookmark()); 0272 bookmark->setProfile(profile); 0273 0274 if (xmlReader.attributes().hasAttribute(QStringLiteral("group"))) { 0275 // For backward compatibility (since Smb4K 3.0.72) 0276 // TODO: Remove with Smb4K >> 3.1 0277 bookmark->setCategoryName(xmlReader.attributes().value(QStringLiteral("group")).toString()); 0278 } else { 0279 bookmark->setCategoryName(xmlReader.attributes().value(QStringLiteral("category")).toString()); 0280 } 0281 0282 while (!(xmlReader.isEndElement() && xmlReader.name() == QStringLiteral("bookmark"))) { 0283 xmlReader.readNext(); 0284 0285 if (xmlReader.isStartElement()) { 0286 if (xmlReader.name() == QStringLiteral("workgroup")) { 0287 bookmark->setWorkgroupName(xmlReader.readElementText()); 0288 } else if (xmlReader.name() == QStringLiteral("url")) { 0289 bookmark->setUrl(QUrl(xmlReader.readElementText())); 0290 } else if (xmlReader.name() == QStringLiteral("login")) { 0291 // For backward compatibility (since Smb4K 3.1.71) 0292 // TODO: Remove with Smb4K >> 3.2 0293 bookmark->setUserName(xmlReader.readElementText()); 0294 } else if (xmlReader.name() == QStringLiteral("ip")) { 0295 bookmark->setHostIpAddress(xmlReader.readElementText()); 0296 } else if (xmlReader.name() == QStringLiteral("label")) { 0297 bookmark->setLabel(xmlReader.readElementText()); 0298 } 0299 0300 continue; 0301 } else { 0302 continue; 0303 } 0304 } 0305 0306 d->bookmarks << bookmark; 0307 } else { 0308 continue; 0309 } 0310 } 0311 } else { 0312 continue; 0313 } 0314 } 0315 0316 xmlFile.close(); 0317 0318 if (xmlReader.hasError()) { 0319 Smb4KNotification::readingFileFailed(xmlFile, xmlReader.errorString()); 0320 } 0321 } else { 0322 if (xmlFile.exists()) { 0323 Smb4KNotification::openingFileFailed(xmlFile); 0324 } 0325 } 0326 0327 Q_EMIT updated(); 0328 } 0329 0330 BookmarkPtr Smb4KBookmarkHandler::findBookmarkByUrl(const QUrl &url) 0331 { 0332 BookmarkPtr bookmark; 0333 QList<BookmarkPtr> temporalBookmarkList = bookmarkList(); 0334 0335 if (!url.isEmpty() && url.isValid() && !temporalBookmarkList.isEmpty()) { 0336 for (const BookmarkPtr &b : qAsConst(temporalBookmarkList)) { 0337 // NOTE: Since also user provided URLs can be bookmarked, we cannot use 0338 // QUrl::matches() here, because it does not allow for case insensitive 0339 // comparison. 0340 if (QString::compare(url.toString(QUrl::RemoveUserInfo | QUrl::RemovePort), 0341 b->url().toString(QUrl::RemoveUserInfo | QUrl::RemovePort), 0342 Qt::CaseInsensitive) 0343 == 0) { 0344 bookmark = b; 0345 break; 0346 } 0347 } 0348 } 0349 0350 return bookmark; 0351 } 0352 0353 BookmarkPtr Smb4KBookmarkHandler::findBookmarkByLabel(const QString &label) 0354 { 0355 BookmarkPtr bookmark; 0356 QList<BookmarkPtr> temporalBookmarkList = bookmarkList(); 0357 0358 for (const BookmarkPtr &b : qAsConst(temporalBookmarkList)) { 0359 if (QString::compare(b->label().toUpper(), label.toUpper()) == 0) { 0360 bookmark = b; 0361 break; 0362 } 0363 } 0364 0365 return bookmark; 0366 } 0367 0368 QList<BookmarkPtr> Smb4KBookmarkHandler::bookmarkList() const 0369 { 0370 QList<BookmarkPtr> bookmarks; 0371 0372 update(); 0373 0374 if (Smb4KSettings::useProfiles()) { 0375 for (const BookmarkPtr &bookmark : qAsConst(d->bookmarks)) { 0376 if (bookmark->profile() == Smb4KSettings::activeProfile()) { 0377 bookmarks << bookmark; 0378 } 0379 } 0380 } else { 0381 bookmarks = d->bookmarks; 0382 } 0383 0384 return bookmarks; 0385 } 0386 0387 QList<BookmarkPtr> Smb4KBookmarkHandler::bookmarkList(const QString &categoryName) const 0388 { 0389 QList<BookmarkPtr> bookmarks; 0390 QList<BookmarkPtr> temporalBookmarkList = bookmarkList(); 0391 0392 for (const BookmarkPtr &bookmark : qAsConst(temporalBookmarkList)) { 0393 if (categoryName == bookmark->categoryName()) { 0394 bookmarks << bookmark; 0395 } 0396 } 0397 0398 return bookmarks; 0399 } 0400 0401 QStringList Smb4KBookmarkHandler::categoryList() const 0402 { 0403 QStringList categories; 0404 QList<BookmarkPtr> temporalBookmarkList = bookmarkList(); 0405 0406 for (const BookmarkPtr &bookmark : qAsConst(temporalBookmarkList)) { 0407 if (!categories.contains(bookmark->categoryName())) { 0408 categories << bookmark->categoryName(); 0409 } 0410 } 0411 0412 return categories; 0413 } 0414 0415 bool Smb4KBookmarkHandler::isBookmarked(const SharePtr &share) 0416 { 0417 if (findBookmarkByUrl(share->url())) { 0418 return true; 0419 } 0420 0421 return false; 0422 } 0423 0424 void Smb4KBookmarkHandler::update() const 0425 { 0426 for (const BookmarkPtr &bookmark : qAsConst(d->bookmarks)) { 0427 HostPtr host = findHost(bookmark->hostName(), bookmark->workgroupName()); 0428 0429 if (host) { 0430 if (host->hasIpAddress() && bookmark->hostIpAddress() != host->ipAddress()) { 0431 bookmark->setHostIpAddress(host->ipAddress()); 0432 } 0433 } 0434 } 0435 } 0436 0437 void Smb4KBookmarkHandler::slotProfileRemoved(const QString &name) 0438 { 0439 QMutableListIterator<BookmarkPtr> it(d->bookmarks); 0440 0441 while (it.hasNext()) { 0442 const BookmarkPtr &bookmark = it.next(); 0443 0444 if (name == bookmark->profile()) { 0445 it.remove(); 0446 } 0447 } 0448 0449 // Write the new list to the file. 0450 writeBookmarkList(); 0451 } 0452 0453 void Smb4KBookmarkHandler::slotProfileMigrated(const QString &oldName, const QString &newName) 0454 { 0455 for (const BookmarkPtr &bookmark : qAsConst(d->bookmarks)) { 0456 if (oldName == bookmark->profile()) { 0457 bookmark->setProfile(newName); 0458 continue; 0459 } 0460 } 0461 0462 // Write the new list to the file. 0463 writeBookmarkList(); 0464 }