1 // -*- c-basic-offset:4; indent-tabs-mode:nil -*-
2 /*
3     This file is part of the KDE libraries
4     SPDX-FileCopyrightText: 2000 David Faure <faure@kde.org>
5     SPDX-FileCopyrightText: 2003 Alexander Kellett <lypanov@kde.org>
6     SPDX-FileCopyrightText: 2008 Norbert Frese <nf2@scheinwelt.at>
7 
8     SPDX-License-Identifier: LGPL-2.0-only
9 */
10 
11 #include "kbookmarkmanager.h"
12 #include "kbookmarkdialog.h"
13 #include "kbookmarkimporter.h"
14 #include "kbookmarkmenu.h"
15 #include "kbookmarkmenu_p.h"
16 #include "kbookmarks_debug.h"
17 #ifndef KBOOKMARKS_NO_DBUS
18 #include "kbookmarkmanageradaptor_p.h"
19 #endif
20 
21 #include <QDir>
22 #include <QFile>
23 #include <QFileInfo>
24 #include <QProcess>
25 #include <QRegularExpression>
26 #include <QTextCodec>
27 #include <QTextStream>
28 #ifndef KBOOKMARKS_NO_DBUS
29 #include <QDBusConnection>
30 #include <QDBusMessage>
31 #endif
32 #include <QApplication>
33 #include <QMessageBox>
34 #include <QReadWriteLock>
35 #include <QThread>
36 
37 #include <KBackup>
38 #include <KConfig>
39 #include <KConfigGroup>
40 #include <KDirWatch>
41 #include <QSaveFile>
42 #include <QStandardPaths>
43 
44 namespace
45 {
46 namespace Strings
47 {
bookmarkChangeNotifyInterface()48 QString bookmarkChangeNotifyInterface()
49 {
50     return QStringLiteral("org.kde.KIO.KBookmarkManager");
51 }
piData()52 QString piData()
53 {
54     return QStringLiteral("version=\"1.0\" encoding=\"UTF-8\"");
55 }
56 }
57 }
58 
59 class KBookmarkManagerList : public QList<KBookmarkManager *>
60 {
61 public:
62     KBookmarkManagerList();
~KBookmarkManagerList()63     ~KBookmarkManagerList()
64     {
65         cleanup();
66     }
cleanup()67     void cleanup()
68     {
69         QList<KBookmarkManager *> copy = *this;
70         qDeleteAll(copy); // auto-delete functionality
71         clear();
72     }
73 
74     QReadWriteLock lock;
75 };
76 
Q_GLOBAL_STATIC(KBookmarkManagerList,s_pSelf)77 Q_GLOBAL_STATIC(KBookmarkManagerList, s_pSelf)
78 
79 static void deleteManagers()
80 {
81     if (s_pSelf.exists()) {
82         s_pSelf->cleanup();
83     }
84 }
85 
KBookmarkManagerList()86 KBookmarkManagerList::KBookmarkManagerList()
87 {
88     // Delete the KBookmarkManagers while qApp exists, since we interact with the DBus thread
89     qAddPostRoutine(deleteManagers);
90 }
91 
92 class KBookmarkMap : private KBookmarkGroupTraverser
93 {
94 public:
KBookmarkMap()95     KBookmarkMap()
96         : m_mapNeedsUpdate(true)
97     {
98     }
setNeedsUpdate()99     void setNeedsUpdate()
100     {
101         m_mapNeedsUpdate = true;
102     }
103     void update(KBookmarkManager *);
find(const QString & url) const104     QList<KBookmark> find(const QString &url) const
105     {
106         return m_bk_map.value(url);
107     }
108 
109 private:
110     void visit(const KBookmark &) override;
visitEnter(const KBookmarkGroup &)111     void visitEnter(const KBookmarkGroup &) override
112     {
113         ;
114     }
visitLeave(const KBookmarkGroup &)115     void visitLeave(const KBookmarkGroup &) override
116     {
117         ;
118     }
119 
120 private:
121     typedef QList<KBookmark> KBookmarkList;
122     QMap<QString, KBookmarkList> m_bk_map;
123     bool m_mapNeedsUpdate;
124 };
125 
update(KBookmarkManager * manager)126 void KBookmarkMap::update(KBookmarkManager *manager)
127 {
128     if (m_mapNeedsUpdate) {
129         m_mapNeedsUpdate = false;
130 
131         m_bk_map.clear();
132         KBookmarkGroup root = manager->root();
133         traverse(root);
134     }
135 }
136 
visit(const KBookmark & bk)137 void KBookmarkMap::visit(const KBookmark &bk)
138 {
139     if (!bk.isSeparator()) {
140         // add bookmark to url map
141         m_bk_map[bk.internalElement().attribute(QStringLiteral("href"))].append(bk);
142     }
143 }
144 
145 // #########################
146 // KBookmarkManagerPrivate
147 class KBookmarkManagerPrivate
148 {
149 public:
KBookmarkManagerPrivate(bool bDocIsloaded,const QString & dbusObjectName=QString ())150     KBookmarkManagerPrivate(bool bDocIsloaded, const QString &dbusObjectName = QString())
151         : m_doc(QStringLiteral("xbel"))
152         , m_dbusObjectName(dbusObjectName)
153         , m_docIsLoaded(bDocIsloaded)
154         , m_update(false)
155         , m_dialogAllowed(true)
156         , m_dialogParent(nullptr)
157         , m_browserEditor(false)
158         , m_typeExternal(false)
159         , m_dirWatch(nullptr)
160     {
161     }
162 
~KBookmarkManagerPrivate()163     ~KBookmarkManagerPrivate()
164     {
165         delete m_dirWatch;
166     }
167 
168     mutable QDomDocument m_doc;
169     mutable QDomDocument m_toolbarDoc;
170     QString m_bookmarksFile;
171     QString m_dbusObjectName;
172     mutable bool m_docIsLoaded;
173     bool m_update;
174     bool m_dialogAllowed;
175     QWidget *m_dialogParent;
176 
177     bool m_browserEditor;
178     QString m_editorCaption;
179 
180     bool m_typeExternal;
181     KDirWatch *m_dirWatch; // for external bookmark files
182 
183     KBookmarkMap m_map;
184 };
185 
186 // ################
187 // KBookmarkManager
188 
lookupExisting(const QString & bookmarksFile)189 static KBookmarkManager *lookupExisting(const QString &bookmarksFile)
190 {
191     for (KBookmarkManagerList::ConstIterator bmit = s_pSelf()->constBegin(), bmend = s_pSelf()->constEnd(); bmit != bmend; ++bmit) {
192         if ((*bmit)->path() == bookmarksFile) {
193             return *bmit;
194         }
195     }
196     return nullptr;
197 }
198 
managerForFile(const QString & bookmarksFile,const QString & dbusObjectName)199 KBookmarkManager *KBookmarkManager::managerForFile(const QString &bookmarksFile, const QString &dbusObjectName)
200 {
201     KBookmarkManager *mgr(nullptr);
202     {
203         QReadLocker readLock(&s_pSelf()->lock);
204         mgr = lookupExisting(bookmarksFile);
205         if (mgr) {
206             return mgr;
207         }
208     }
209 
210     QWriteLocker writeLock(&s_pSelf()->lock);
211     mgr = lookupExisting(bookmarksFile);
212     if (mgr) {
213         return mgr;
214     }
215 
216     mgr = new KBookmarkManager(bookmarksFile, dbusObjectName);
217     s_pSelf()->append(mgr);
218     return mgr;
219 }
220 
managerForExternalFile(const QString & bookmarksFile)221 KBookmarkManager *KBookmarkManager::managerForExternalFile(const QString &bookmarksFile)
222 {
223     KBookmarkManager *mgr(nullptr);
224     {
225         QReadLocker readLock(&s_pSelf()->lock);
226         mgr = lookupExisting(bookmarksFile);
227         if (mgr) {
228             return mgr;
229         }
230     }
231 
232     QWriteLocker writeLock(&s_pSelf()->lock);
233     mgr = lookupExisting(bookmarksFile);
234     if (mgr) {
235         return mgr;
236     }
237 
238     mgr = new KBookmarkManager(bookmarksFile);
239     s_pSelf()->append(mgr);
240     return mgr;
241 }
242 
243 // principally used for filtered toolbars
createTempManager()244 KBookmarkManager *KBookmarkManager::createTempManager()
245 {
246     KBookmarkManager *mgr = new KBookmarkManager();
247     s_pSelf()->append(mgr);
248     return mgr;
249 }
250 
createXbelTopLevelElement(QDomDocument & doc)251 static QDomElement createXbelTopLevelElement(QDomDocument &doc)
252 {
253     QDomElement topLevel = doc.createElement(QStringLiteral("xbel"));
254     topLevel.setAttribute(QStringLiteral("xmlns:mime"), QStringLiteral("http://www.freedesktop.org/standards/shared-mime-info"));
255     topLevel.setAttribute(QStringLiteral("xmlns:bookmark"), QStringLiteral("http://www.freedesktop.org/standards/desktop-bookmarks"));
256     topLevel.setAttribute(QStringLiteral("xmlns:kdepriv"), QStringLiteral("http://www.kde.org/kdepriv"));
257     doc.appendChild(topLevel);
258     doc.insertBefore(doc.createProcessingInstruction(QStringLiteral("xml"), Strings::piData()), topLevel);
259     return topLevel;
260 }
261 
KBookmarkManager(const QString & bookmarksFile,const QString & dbusObjectName)262 KBookmarkManager::KBookmarkManager(const QString &bookmarksFile, const QString &dbusObjectName)
263     : d(new KBookmarkManagerPrivate(false, dbusObjectName))
264 {
265     if (dbusObjectName.isNull()) { // get dbusObjectName from file
266         if (QFile::exists(d->m_bookmarksFile)) {
267             parse(); // sets d->m_dbusObjectName
268         }
269     }
270 
271     init(QLatin1String("/KBookmarkManager/") + d->m_dbusObjectName);
272 
273     d->m_update = true;
274 
275     Q_ASSERT(!bookmarksFile.isEmpty());
276     d->m_bookmarksFile = bookmarksFile;
277 
278     if (!QFile::exists(d->m_bookmarksFile)) {
279         QDomElement topLevel = createXbelTopLevelElement(d->m_doc);
280         topLevel.setAttribute(QStringLiteral("dbusName"), dbusObjectName);
281         d->m_docIsLoaded = true;
282     }
283 }
284 
KBookmarkManager(const QString & bookmarksFile)285 KBookmarkManager::KBookmarkManager(const QString &bookmarksFile)
286     : d(new KBookmarkManagerPrivate(false))
287 {
288     // use QFileSystemWatcher to monitor this bookmarks file
289     d->m_typeExternal = true;
290     d->m_update = true;
291 
292     Q_ASSERT(!bookmarksFile.isEmpty());
293     d->m_bookmarksFile = bookmarksFile;
294 
295     if (!QFile::exists(d->m_bookmarksFile)) {
296         createXbelTopLevelElement(d->m_doc);
297     } else {
298         parse();
299     }
300     d->m_docIsLoaded = true;
301 
302     // start KDirWatch
303     d->m_dirWatch = new KDirWatch;
304     d->m_dirWatch->addFile(d->m_bookmarksFile);
305     QObject::connect(d->m_dirWatch, &KDirWatch::dirty, this, &KBookmarkManager::slotFileChanged);
306     QObject::connect(d->m_dirWatch, &KDirWatch::created, this, &KBookmarkManager::slotFileChanged);
307     QObject::connect(d->m_dirWatch, &KDirWatch::deleted, this, &KBookmarkManager::slotFileChanged);
308 
309     // qCDebug(KBOOKMARKS_LOG) << "starting KDirWatch for" << d->m_bookmarksFile;
310 }
311 
KBookmarkManager()312 KBookmarkManager::KBookmarkManager()
313     : d(new KBookmarkManagerPrivate(true))
314 {
315     init(QStringLiteral("/KBookmarkManager/generated"));
316     d->m_update = false; // TODO - make it read/write
317 
318     createXbelTopLevelElement(d->m_doc);
319 }
320 
init(const QString & dbusPath)321 void KBookmarkManager::init(const QString &dbusPath)
322 {
323 #ifndef KBOOKMARKS_NO_DBUS
324     // A KBookmarkManager without a dbus name is a temporary one, like those used by importers;
325     // no need to register them to dbus
326     if (dbusPath != QLatin1String("/KBookmarkManager/") && dbusPath != QLatin1String("/KBookmarkManager/generated")) {
327         new KBookmarkManagerAdaptor(this);
328         QDBusConnection::sessionBus().registerObject(dbusPath, this);
329 
330         QDBusConnection::sessionBus().connect(QString(),
331                                               dbusPath,
332                                               Strings::bookmarkChangeNotifyInterface(),
333                                               QStringLiteral("bookmarksChanged"),
334                                               this,
335                                               SLOT(notifyChanged(QString, QDBusMessage)));
336         QDBusConnection::sessionBus()
337             .connect(QString(), dbusPath, Strings::bookmarkChangeNotifyInterface(), QStringLiteral("bookmarkConfigChanged"), this, SLOT(notifyConfigChanged()));
338     }
339 #endif
340 }
341 
startKEditBookmarks(const QStringList & args)342 void KBookmarkManager::startKEditBookmarks(const QStringList &args)
343 {
344     bool success = QProcess::startDetached(QStringLiteral(KEDITBOOKMARKS_BINARY), args);
345 
346     if (!success) {
347         QString err =
348             tr("Cannot launch keditbookmarks.\n\n"
349                "Most likely you do not have keditbookmarks currently installed");
350 
351         if (d->m_dialogAllowed && qobject_cast<QApplication *>(qApp) && QThread::currentThread() == qApp->thread()) {
352             QMessageBox::warning(QApplication::activeWindow(), QApplication::applicationName(), err);
353         }
354 
355         qCWarning(KBOOKMARKS_LOG) << QStringLiteral("Failed to start keditbookmarks");
356         Q_EMIT this->error(err);
357     }
358 }
359 
slotFileChanged(const QString & path)360 void KBookmarkManager::slotFileChanged(const QString &path)
361 {
362     if (path == d->m_bookmarksFile) {
363         // qCDebug(KBOOKMARKS_LOG) << "file changed (KDirWatch) " << path ;
364         // Reparse
365         parse();
366         // Tell our GUI
367         // (emit where group is "" to directly mark the root menu as dirty)
368         Q_EMIT changed(QLatin1String(""), QString());
369     }
370 }
371 
~KBookmarkManager()372 KBookmarkManager::~KBookmarkManager()
373 {
374     if (!s_pSelf.isDestroyed()) {
375         s_pSelf()->removeAll(this);
376     }
377 }
378 
autoErrorHandlingEnabled() const379 bool KBookmarkManager::autoErrorHandlingEnabled() const
380 {
381     return d->m_dialogAllowed;
382 }
383 
setAutoErrorHandlingEnabled(bool enable,QWidget * parent)384 void KBookmarkManager::setAutoErrorHandlingEnabled(bool enable, QWidget *parent)
385 {
386     d->m_dialogAllowed = enable;
387     d->m_dialogParent = parent;
388 }
389 
setUpdate(bool update)390 void KBookmarkManager::setUpdate(bool update)
391 {
392     d->m_update = update;
393 }
394 
internalDocument() const395 QDomDocument KBookmarkManager::internalDocument() const
396 {
397     if (!d->m_docIsLoaded) {
398         parse();
399         d->m_toolbarDoc.clear();
400     }
401     return d->m_doc;
402 }
403 
parse() const404 void KBookmarkManager::parse() const
405 {
406     d->m_docIsLoaded = true;
407     // qCDebug(KBOOKMARKS_LOG) << "KBookmarkManager::parse " << d->m_bookmarksFile;
408     QFile file(d->m_bookmarksFile);
409     if (!file.open(QIODevice::ReadOnly)) {
410         qCWarning(KBOOKMARKS_LOG) << "Can't open" << d->m_bookmarksFile;
411         d->m_doc = QDomDocument(QStringLiteral("xbel"));
412         createXbelTopLevelElement(d->m_doc);
413         return;
414     }
415     d->m_doc = QDomDocument(QStringLiteral("xbel"));
416     d->m_doc.setContent(&file);
417 
418     if (d->m_doc.documentElement().isNull()) {
419         qCWarning(KBOOKMARKS_LOG) << "KBookmarkManager::parse : main tag is missing, creating default " << d->m_bookmarksFile;
420         QDomElement element = d->m_doc.createElement(QStringLiteral("xbel"));
421         d->m_doc.appendChild(element);
422     }
423 
424     QDomElement docElem = d->m_doc.documentElement();
425 
426     QString mainTag = docElem.tagName();
427     if (mainTag != QLatin1String("xbel")) {
428         qCWarning(KBOOKMARKS_LOG) << "KBookmarkManager::parse : unknown main tag " << mainTag;
429     }
430 
431     if (d->m_dbusObjectName.isNull()) {
432         d->m_dbusObjectName = docElem.attribute(QStringLiteral("dbusName"));
433     } else if (docElem.attribute(QStringLiteral("dbusName")) != d->m_dbusObjectName) {
434         docElem.setAttribute(QStringLiteral("dbusName"), d->m_dbusObjectName);
435         save();
436     }
437 
438     QDomNode n = d->m_doc.documentElement().previousSibling();
439     if (n.isProcessingInstruction()) {
440         QDomProcessingInstruction pi = n.toProcessingInstruction();
441         pi.parentNode().removeChild(pi);
442     }
443 
444     QDomProcessingInstruction pi;
445     pi = d->m_doc.createProcessingInstruction(QStringLiteral("xml"), Strings::piData());
446     d->m_doc.insertBefore(pi, docElem);
447 
448     file.close();
449 
450     d->m_map.setNeedsUpdate();
451 }
452 
save(bool toolbarCache) const453 bool KBookmarkManager::save(bool toolbarCache) const
454 {
455     return saveAs(d->m_bookmarksFile, toolbarCache);
456 }
457 
saveAs(const QString & filename,bool toolbarCache) const458 bool KBookmarkManager::saveAs(const QString &filename, bool toolbarCache) const
459 {
460     // qCDebug(KBOOKMARKS_LOG) << "KBookmarkManager::save " << filename;
461 
462     // Save the bookmark toolbar folder for quick loading
463     // but only when it will actually make things quicker
464     const QString cacheFilename = filename + QLatin1String(".tbcache");
465     if (toolbarCache && !root().isToolbarGroup()) {
466         QSaveFile cacheFile(cacheFilename);
467         if (cacheFile.open(QIODevice::WriteOnly)) {
468             QString str;
469             QTextStream stream(&str, QIODevice::WriteOnly);
470             stream << root().findToolbar();
471             const QByteArray cstr = str.toUtf8();
472             cacheFile.write(cstr.data(), cstr.length());
473             cacheFile.commit();
474         }
475     } else { // remove any (now) stale cache
476         QFile::remove(cacheFilename);
477     }
478 
479     // Create parent dirs
480     QFileInfo info(filename);
481     QDir().mkpath(info.absolutePath());
482 
483     QSaveFile file(filename);
484     if (file.open(QIODevice::WriteOnly)) {
485         KBackup::simpleBackupFile(file.fileName(), QString(), QStringLiteral(".bak"));
486         QTextStream stream(&file);
487         stream.setCodec(QTextCodec::codecForName("UTF-8"));
488         stream << internalDocument().toString();
489         stream.flush();
490         if (file.commit()) {
491             return true;
492         }
493     }
494 
495     static int hadSaveError = false;
496     if (!hadSaveError) {
497         QString err = tr("Unable to save bookmarks in %1. Reported error was: %2. "
498                          "This error message will only be shown once. The cause "
499                          "of the error needs to be fixed as quickly as possible, "
500                          "which is most likely a full hard drive.")
501                           .arg(filename, file.errorString());
502 
503         if (d->m_dialogAllowed && qobject_cast<QApplication *>(qApp) && QThread::currentThread() == qApp->thread()) {
504             QMessageBox::critical(QApplication::activeWindow(), QApplication::applicationName(), err);
505         }
506 
507         qCCritical(KBOOKMARKS_LOG)
508             << QStringLiteral("Unable to save bookmarks in %1. File reported the following error-code: %2.").arg(filename).arg(file.error());
509         Q_EMIT const_cast<KBookmarkManager *>(this)->error(err);
510     }
511     hadSaveError = true;
512     return false;
513 }
514 
path() const515 QString KBookmarkManager::path() const
516 {
517     return d->m_bookmarksFile;
518 }
519 
root() const520 KBookmarkGroup KBookmarkManager::root() const
521 {
522     return KBookmarkGroup(internalDocument().documentElement());
523 }
524 
toolbar()525 KBookmarkGroup KBookmarkManager::toolbar()
526 {
527     // qCDebug(KBOOKMARKS_LOG) << "KBookmarkManager::toolbar begin";
528     // Only try to read from a toolbar cache if the full document isn't loaded
529     if (!d->m_docIsLoaded) {
530         // qCDebug(KBOOKMARKS_LOG) << "KBookmarkManager::toolbar trying cache";
531         const QString cacheFilename = d->m_bookmarksFile + QLatin1String(".tbcache");
532         QFileInfo bmInfo(d->m_bookmarksFile);
533         QFileInfo cacheInfo(cacheFilename);
534         if (d->m_toolbarDoc.isNull() && QFile::exists(cacheFilename) && bmInfo.lastModified() < cacheInfo.lastModified()) {
535             // qCDebug(KBOOKMARKS_LOG) << "KBookmarkManager::toolbar reading file";
536             QFile file(cacheFilename);
537 
538             if (file.open(QIODevice::ReadOnly)) {
539                 d->m_toolbarDoc = QDomDocument(QStringLiteral("cache"));
540                 d->m_toolbarDoc.setContent(&file);
541                 // qCDebug(KBOOKMARKS_LOG) << "KBookmarkManager::toolbar opened";
542             }
543         }
544         if (!d->m_toolbarDoc.isNull()) {
545             // qCDebug(KBOOKMARKS_LOG) << "KBookmarkManager::toolbar returning element";
546             QDomElement elem = d->m_toolbarDoc.firstChild().toElement();
547             return KBookmarkGroup(elem);
548         }
549     }
550 
551     // Fallback to the normal way if there is no cache or if the bookmark file
552     // is already loaded
553     QDomElement elem = root().findToolbar();
554     if (elem.isNull()) {
555         // Root is the bookmark toolbar if none has been set.
556         // Make it explicit to speed up invocations of findToolbar()
557         root().internalElement().setAttribute(QStringLiteral("toolbar"), QStringLiteral("yes"));
558         return root();
559     } else {
560         return KBookmarkGroup(elem);
561     }
562 }
563 
findByAddress(const QString & address)564 KBookmark KBookmarkManager::findByAddress(const QString &address)
565 {
566     // qCDebug(KBOOKMARKS_LOG) << "KBookmarkManager::findByAddress " << address;
567     KBookmark result = root();
568     // The address is something like /5/10/2+
569     const QStringList addresses = address.split(QRegularExpression(QStringLiteral("[/+]")), Qt::SkipEmptyParts);
570     // qCWarning(KBOOKMARKS_LOG) << addresses.join(",");
571     for (QStringList::const_iterator it = addresses.begin(); it != addresses.end();) {
572         bool append = ((*it) == QLatin1String("+"));
573         uint number = (*it).toUInt();
574         Q_ASSERT(result.isGroup());
575         KBookmarkGroup group = result.toGroup();
576         KBookmark bk = group.first();
577         KBookmark lbk = bk; // last non-null bookmark
578         for (uint i = 0; ((i < number) || append) && !bk.isNull(); ++i) {
579             lbk = bk;
580             bk = group.next(bk);
581             // qCWarning(KBOOKMARKS_LOG) << i;
582         }
583         it++;
584         // qCWarning(KBOOKMARKS_LOG) << "found section";
585         result = bk;
586     }
587     if (result.isNull()) {
588         qCWarning(KBOOKMARKS_LOG) << "KBookmarkManager::findByAddress: couldn't find item " << address;
589     }
590     // qCWarning(KBOOKMARKS_LOG) << "found " << result.address();
591     return result;
592 }
593 
emitChanged()594 void KBookmarkManager::emitChanged()
595 {
596     emitChanged(root());
597 }
598 
emitChanged(const KBookmarkGroup & group)599 void KBookmarkManager::emitChanged(const KBookmarkGroup &group)
600 {
601     (void)save(); // KDE5 TODO: emitChanged should return a bool? Maybe rename it to saveAndEmitChanged?
602 
603     // Tell the other processes too
604     // qCDebug(KBOOKMARKS_LOG) << "KBookmarkManager::emitChanged : broadcasting change " << group.address();
605 
606     Q_EMIT bookmarksChanged(group.address());
607 
608     // We do get our own broadcast, so no need for this anymore
609     // emit changed( group );
610 }
611 
emitConfigChanged()612 void KBookmarkManager::emitConfigChanged()
613 {
614     Q_EMIT bookmarkConfigChanged();
615 }
616 
notifyCompleteChange(const QString & caller)617 void KBookmarkManager::notifyCompleteChange(const QString &caller) // DBUS call
618 {
619     if (!d->m_update) {
620         return;
621     }
622 
623     // qCDebug(KBOOKMARKS_LOG) << "KBookmarkManager::notifyCompleteChange";
624     // The bk editor tells us we should reload everything
625     // Reparse
626     parse();
627     // Tell our GUI
628     // (emit where group is "" to directly mark the root menu as dirty)
629     Q_EMIT changed(QLatin1String(""), caller);
630 }
631 
notifyConfigChanged()632 void KBookmarkManager::notifyConfigChanged() // DBUS call
633 {
634     // qCDebug(KBOOKMARKS_LOG) << "reloaded bookmark config!";
635     KBookmarkSettings::self()->readSettings();
636     parse(); // reload, and thusly recreate the menus
637     Q_EMIT configChanged();
638 }
639 
640 #ifndef KBOOKMARKS_NO_DBUS
notifyChanged(const QString & groupAddress,const QDBusMessage & msg)641 void KBookmarkManager::notifyChanged(const QString &groupAddress, const QDBusMessage &msg) // DBUS call
642 {
643     // qCDebug(KBOOKMARKS_LOG) << "KBookmarkManager::notifyChanged ( "<<groupAddress<<")";
644     if (!d->m_update) {
645         return;
646     }
647 
648     // Reparse (the whole file, no other choice)
649     // if someone else notified us
650     if (msg.service() != QDBusConnection::sessionBus().baseService()) {
651         parse();
652     }
653 
654     // qCDebug(KBOOKMARKS_LOG) << "KBookmarkManager::notifyChanged " << groupAddress;
655     // KBookmarkGroup group = findByAddress( groupAddress ).toGroup();
656     // Q_ASSERT(!group.isNull());
657     Q_EMIT changed(groupAddress, QString());
658 }
659 #endif
660 
setEditorOptions(const QString & caption,bool browser)661 void KBookmarkManager::setEditorOptions(const QString &caption, bool browser)
662 {
663     d->m_editorCaption = caption;
664     d->m_browserEditor = browser;
665 }
666 
slotEditBookmarks()667 void KBookmarkManager::slotEditBookmarks()
668 {
669     QStringList args;
670     if (!d->m_editorCaption.isEmpty()) {
671         args << QStringLiteral("--customcaption") << d->m_editorCaption;
672     }
673     if (!d->m_browserEditor) {
674         args << QStringLiteral("--nobrowser");
675     }
676     if (!d->m_dbusObjectName.isEmpty()) {
677         args << QStringLiteral("--dbusObjectName") << d->m_dbusObjectName;
678     }
679     args << d->m_bookmarksFile;
680     startKEditBookmarks(args);
681 }
682 
slotEditBookmarksAtAddress(const QString & address)683 void KBookmarkManager::slotEditBookmarksAtAddress(const QString &address)
684 {
685     QStringList args;
686     if (!d->m_editorCaption.isEmpty()) {
687         args << QStringLiteral("--customcaption") << d->m_editorCaption;
688     }
689     if (!d->m_browserEditor) {
690         args << QStringLiteral("--nobrowser");
691     }
692     if (!d->m_dbusObjectName.isEmpty()) {
693         args << QStringLiteral("--dbusObjectName") << d->m_dbusObjectName;
694     }
695     args << QStringLiteral("--address") << address << d->m_bookmarksFile;
696     startKEditBookmarks(args);
697 }
698 
699 ///////
updateAccessMetadata(const QString & url)700 bool KBookmarkManager::updateAccessMetadata(const QString &url)
701 {
702     d->m_map.update(this);
703     QList<KBookmark> list = d->m_map.find(url);
704     if (list.isEmpty()) {
705         return false;
706     }
707 
708     for (QList<KBookmark>::iterator it = list.begin(); it != list.end(); ++it) {
709         (*it).updateAccessMetadata();
710     }
711 
712     return true;
713 }
714 
updateFavicon(const QString & url,const QString &)715 void KBookmarkManager::updateFavicon(const QString &url, const QString & /*faviconurl*/)
716 {
717     d->m_map.update(this);
718     QList<KBookmark> list = d->m_map.find(url);
719     for (QList<KBookmark>::iterator it = list.begin(); it != list.end(); ++it) {
720         // TODO - update favicon data based on faviconurl
721         //        but only when the previously used icon
722         //        isn't a manually set one.
723     }
724 }
725 
userBookmarksManager()726 KBookmarkManager *KBookmarkManager::userBookmarksManager()
727 {
728     const QString bookmarksFile =
729         QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/konqueror/bookmarks.xml");
730     KBookmarkManager *bookmarkManager = KBookmarkManager::managerForFile(bookmarksFile, QStringLiteral("konqueror"));
731     QString caption = QGuiApplication::applicationDisplayName();
732     if (caption.isEmpty()) {
733         caption = QCoreApplication::applicationName();
734     }
735     bookmarkManager->setEditorOptions(caption, true);
736     return bookmarkManager;
737 }
738 
739 KBookmarkSettings *KBookmarkSettings::s_self = nullptr;
740 
readSettings()741 void KBookmarkSettings::readSettings()
742 {
743     KConfig config(QStringLiteral("kbookmarkrc"), KConfig::NoGlobals);
744     KConfigGroup cg(&config, "Bookmarks");
745 
746     // add bookmark dialog usage - no reparse
747     s_self->m_advancedaddbookmark = cg.readEntry("AdvancedAddBookmarkDialog", false);
748 
749     // this one alters the menu, therefore it needs a reparse
750     s_self->m_contextmenu = cg.readEntry("ContextMenuActions", true);
751 }
752 
self()753 KBookmarkSettings *KBookmarkSettings::self()
754 {
755     if (!s_self) {
756         s_self = new KBookmarkSettings;
757         readSettings();
758     }
759     return s_self;
760 }
761 
762 #include "moc_kbookmarkmanager.cpp"
763