File indexing completed on 2024-04-28 08:54:20

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 }