1 /*
2    SPDX-FileCopyrightText: 2008 Xavier Vello <xavier.vello@gmail.com>
3 
4    SPDX-License-Identifier: GPL-2.0-or-later
5 
6 */
7 
8 #include "kio_bookmarks.h"
9 
10 #include <stdio.h>
11 #include <stdlib.h>
12 
13 #include <QRegularExpression>
14 #include <qtextdocument.h>
15 #include <qurlquery.h>
16 
17 #include <kshell.h>
18 
19 #include <KLocalizedString>
20 #include <kconfig.h>
21 #include <kconfiggroup.h>
22 #include <kbookmark.h>
23 #include <kbookmarkmanager.h>
24 #include <kimagecache.h>
25 #include <qdebug.h>
26 #include <kfileplacesmodel.h>
27 #include <solid/device.h>
28 #include <solid/deviceinterface.h>
29 #include <QGuiApplication>
30 #include <KIO/ApplicationLauncherJob>
31 
32 using namespace KIO;
33 
34 // Pseudo plugin class to embed meta data
35 class KIOPluginForMetaData : public QObject
36 {
37     Q_OBJECT
38     Q_PLUGIN_METADATA(IID "org.kde.kio.slave.bookmarks" FILE "bookmarks.json")
39 };
40 
BookmarksProtocol(const QByteArray & pool,const QByteArray & app)41 BookmarksProtocol::BookmarksProtocol( const QByteArray &pool, const QByteArray &app )
42     : SlaveBase( "bookmarks", pool, app )
43 {
44     manager = KBookmarkManager::userBookmarksManager();
45     cfg = new KConfig( "kiobookmarksrc" );
46     config = cfg->group("General");
47     cache = new KImageCache("kio_bookmarks", config.readEntry("CacheSize", 5 * 1024) * 1024);
48     cache->setPixmapCaching(false);
49 
50     indent = 0;
51     totalsize = 0;
52     columns = 4;
53 }
54 
~BookmarksProtocol()55 BookmarksProtocol::~BookmarksProtocol()
56 {
57     delete manager;
58     delete cache;
59     delete cfg;
60 }
61 
parseTree()62 void BookmarksProtocol::parseTree()
63 {
64     totalsize = 0;
65 
66     cfg->reparseConfiguration();
67     columns =  config.readEntry("Columns", 4);
68     if (columns < 1)
69         columns = 1;
70 
71     manager->notifyCompleteChange("kio_bookmarks");
72     tree = manager->root();
73 
74     if(tree.first().isNull())
75         return;
76 
77     if(config.readEntry("FlattenTree", false))
78         flattenTree(tree);
79 
80     KBookmarkGroup root;
81     if(config.readEntry("ShowRoot", true))
82     {
83         root = tree.createNewFolder(i18n("Root"));
84         tree.moveBookmark(root, KBookmark());
85         root.setIcon("konqueror");
86     }
87 
88     KBookmark bm = tree.first();
89     KBookmark next;
90     while(!bm.isNull())
91     {
92         next = tree.next(bm);
93         if (bm.isSeparator())
94             tree.deleteBookmark(bm);
95         else if (bm.isGroup())
96             totalsize += sizeOfGroup(bm.toGroup());
97         else
98         {
99             if(config.readEntry("ShowRoot", true))
100                 root.addBookmark(bm);
101 
102             tree.deleteBookmark(bm);
103         }
104         bm = next;
105     }
106     if(config.readEntry("ShowRoot", true))
107         totalsize += sizeOfGroup(root);
108 
109     if(config.readEntry("ShowPlaces", true))
110         totalsize += addPlaces();
111 }
112 
addPlaces()113 int BookmarksProtocol::addPlaces()
114 {
115     KFilePlacesModel placesModel;
116     KBookmarkGroup folder = tree.createNewFolder(i18n("Places"));
117     QList<Solid::Device> batteryList = Solid::Device::listFromType(Solid::DeviceInterface::Battery, QString());
118 
119     if (batteryList.isEmpty()) {
120         folder.setIcon("computer");
121     } else {
122         folder.setIcon("computer-laptop");
123     }
124 
125     for (int row = 0; row < placesModel.rowCount(); ++row) {
126         QModelIndex index = placesModel.index(row, 0);
127 
128         if (!placesModel.isHidden(index))
129             folder.addBookmark(placesModel.bookmarkForIndex(index));
130     }
131     return sizeOfGroup(folder);
132 }
133 
flattenTree(const KBookmarkGroup & folder)134 void BookmarksProtocol::flattenTree( const KBookmarkGroup &folder )
135 {
136     KBookmark bm = folder.first();
137     KBookmark prev = folder;
138     KBookmark next;
139     while (!bm.isNull())
140     {
141         if (bm.isGroup()) {
142             flattenTree(bm.toGroup());
143         }
144 
145         next = tree.next(bm);
146 
147         if (bm.isGroup() && bm.parentGroup().hasParent()) {
148             bm.setFullText("| " + bm.parentGroup().fullText() + " > " + bm.fullText());
149             tree.moveBookmark(bm, prev);
150             prev = bm;
151         }
152         bm = next;
153     }
154 }
155 
156 // Should really go to KBookmarkGroup
sizeOfGroup(const KBookmarkGroup & folder,bool real)157 int BookmarksProtocol::sizeOfGroup( const KBookmarkGroup &folder, bool real )
158 {
159     int size = 1;  // counting the title line
160     for (KBookmark bm = folder.first(); !bm.isNull(); bm = folder.next(bm))
161     {
162         if (bm.isGroup())
163             size += sizeOfGroup(bm.toGroup());
164         else
165             size += 1;
166     }
167 
168     // CSS sets a min-height for toplevel folders
169     if (folder.parentGroup() == tree && size < 8 && real == false)
170         size = 8;
171 
172     return size;
173 }
174 
get(const QUrl & url)175 void BookmarksProtocol::get( const QUrl& url )
176 {
177     QString path = url.path();
178     const QRegularExpression regexp(QStringLiteral("^/(background|icon)/([\\S]+)"));
179     QRegularExpressionMatch rmatch;
180 
181     if (path.isEmpty() || path == "/") {
182         echoIndex();
183     } else if (path == "/config") {
184         const KService::Ptr bookmarksKCM = KService::serviceByDesktopName(QStringLiteral("bookmarks"));
185         if (bookmarksKCM) {
186             auto job = new KIO::ApplicationLauncherJob(bookmarksKCM);
187             job->start();
188         } else {
189             error(KIO::ERR_SLAVE_DEFINED, i18n("Could not find bookmarks config"));
190         }
191         echoHead("bookmarks:/");
192     } else if (path == "/editbookmarks") {
193         const KService::Ptr keditbookmarks = KService::serviceByDesktopName(QStringLiteral("org.kde.keditbookmarks"));
194         if (keditbookmarks) {
195             auto job = new KIO::ApplicationLauncherJob(keditbookmarks);
196             job->start();
197         } else {
198             error(KIO::ERR_SLAVE_DEFINED, i18n("Could not find bookmarks editor"));
199         }
200         echoHead("bookmarks:/");
201     } else if (path.indexOf(regexp, 0, &rmatch) >= 0) {
202         echoImage(rmatch.captured(1), rmatch.captured(2), QUrlQuery(url).queryItemValue("size"));
203     } else {
204         echoHead();
205         echo("<p class=\"message\">" + i18n("Wrong request: %1", url.toDisplayString().toHtmlEscaped()) + "</p>");
206     }
207     finished();
208 }
209 
kdemain(int argc,char ** argv)210 extern "C" int Q_DECL_EXPORT kdemain(int argc, char **argv)
211 {
212     QGuiApplication app(argc, argv);
213     app.setApplicationName(QLatin1String("kio_bookmarks"));
214 
215     if (argc != 4) {
216         qCritical() << "Usage: kio_bookmarks protocol domain-socket1 domain-socket2";
217         exit(-1);
218     }
219 
220     BookmarksProtocol slave(argv[2], argv[3]);
221     slave.dispatchLoop();
222 
223     return 0;
224 }
225 
226 #include "kio_bookmarks.moc"
227