2
* Copyright 2007 Glenn Ergeerts <glenn.ergeerts@telenet.be>
4
* This program is free software; you can redistribute it and/or modify
5
* it under the terms of the GNU Library General Public License as
6
* published by the Free Software Foundation; either version 2, or
7
* (at your option) any later version.
9
* This program is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU General Public License for more details
14
* You should have received a copy of the GNU Library General Public
15
* License along with this program; if not, write to the
16
* Free Software Foundation, Inc.,
17
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20
#include "bookmarksrunner.h"
22
#include <QDBusInterface>
30
#include <KBookmarkManager>
32
#include <KMimeTypeTrader>
33
#include <KToolInvocation>
35
#include <KStandardDirs>
39
BookmarksRunner::BookmarksRunner( QObject* parent, const QVariantList &args )
40
: Plasma::AbstractRunner(parent, args)
43
setObjectName( QLatin1String("Bookmarks" ));
44
m_icon = KIcon("bookmarks");
45
m_bookmarkManager = KBookmarkManager::userBookmarksManager();
46
m_browser = whichBrowser();
47
addSyntax(Plasma::RunnerSyntax(":q:", i18n("Finds web browser bookmarks matching :q:.")));
48
setDefaultSyntax(Plasma::RunnerSyntax(i18nc("list of all web browser bookmarks", "bookmarks"),
49
i18n("List all web browser bookmarks")));
51
connect(this, SIGNAL(prepare()), this, SLOT(prep()));
52
connect(this, SIGNAL(teardown()), this, SLOT(down()));
54
reloadConfiguration();
57
BookmarksRunner::~BookmarksRunner()
59
if (!m_dbCacheFile.isEmpty()) {
60
QFile db_CacheFile(m_dbCacheFile);
61
if (db_CacheFile.exists()) {
62
kDebug() << "Cache file was removed: " << db_CacheFile.remove();
67
void BookmarksRunner::reloadConfiguration()
69
if (m_browser == Firefox) {
70
if (QSqlDatabase::isDriverAvailable("QSQLITE")) {
71
KConfigGroup grp = config();
72
/* This allows the user to specify a profile database */
73
m_dbFile = grp.readEntry<QString>("dbfile", "");
74
if (m_dbFile.isEmpty() || QFile::exists(m_dbFile)) {
75
//Try to get the right database file, the default profile is used
76
KConfig firefoxProfile(QDir::homePath() + "/.mozilla/firefox/profiles.ini",
77
KConfig::SimpleConfig);
78
QStringList profilesList = firefoxProfile.groupList();
79
profilesList = profilesList.filter(QRegExp("^Profile\\d+$"));
80
int size = profilesList.size();
84
// There is only 1 profile so we select it
85
KConfigGroup fGrp = firefoxProfile.group(profilesList.first());
86
profilePath = fGrp.readEntry("Path", "");
88
// There are multiple profiles, find the default one
89
foreach(const QString & profileName, profilesList) {
90
KConfigGroup fGrp = firefoxProfile.group(profileName);
91
if (fGrp.readEntry<int>("Default", 0)) {
92
profilePath = fGrp.readEntry("Path", "");
98
if (profilePath.isEmpty()) {
99
kDebug() << "No default firefox profile found";
100
m_db = QSqlDatabase();
104
profilePath.prepend(QString("%1/.mozilla/firefox/").arg(QDir::homePath()));
105
m_dbFile = profilePath + "/places.sqlite";
106
grp.writeEntry("dbfile", m_dbFile);
108
m_db = QSqlDatabase::addDatabase("QSQLITE");
109
m_db.setHostName("localhost");
111
kDebug() << "SQLITE driver isn't available";
112
m_db = QSqlDatabase();
117
void BookmarksRunner::prep()
119
m_browser = whichBrowser();
120
if (m_browser == Firefox) {
121
if (m_db.isValid()) {
122
if (m_dbCacheFile.isEmpty()) {
123
m_dbCacheFile = KStandardDirs::locateLocal("cache", "") + "bookmarkrunnerfirefoxdbfile.sqlite";
126
// ### DO NOT USE KIO FROM RUNNER THREADS!
127
// ### This looks like a local copy, so use QFile::copy instead.
128
KIO::Job *job = KIO::file_copy(m_dbFile, m_dbCacheFile, -1,
129
KIO::HideProgressInfo | KIO::Overwrite);
130
connect(job, SIGNAL(result(KJob*)), this, SLOT(dbCopied(KJob*)));
132
} else if (m_browser == Opera) {
133
// open bookmarks file
134
QString operaBookmarksFilePath = QDir::homePath() + "/.opera/bookmarks.adr";
135
QFile operaBookmarksFile(operaBookmarksFilePath);
136
if (!operaBookmarksFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
137
kDebug() << "Could not open Operas Bookmark File " + operaBookmarksFilePath;
142
QString firstLine = operaBookmarksFile.readLine();
143
if (firstLine.compare("Opera Hotlist version 2.0\n")) {
144
kDebug() << "Format of Opera Bookmarks File might have changed.";
146
operaBookmarksFile.readLine(); // skip options line ("Options: encoding = utf8, version=3")
147
operaBookmarksFile.readLine(); // skip empty line
150
QString contents = operaBookmarksFile.readAll();
151
m_operaBookmarkEntries = contents.split("\n\n", QString::SkipEmptyParts);
154
operaBookmarksFile.close();
158
void BookmarksRunner::dbCopied(KJob *)
160
m_db.setDatabaseName(m_dbCacheFile);
161
m_dbOK = m_db.open();
162
kDebug() << "Database was opened: " << m_dbOK;
165
void BookmarksRunner::down()
167
if (m_browser == Firefox) {
172
} else if (m_browser == Opera) {
173
m_operaBookmarkEntries.clear();
177
void BookmarksRunner::match(Plasma::RunnerContext &context)
179
const QString term = context.query();
180
if ((term.length() < 3) && (!context.singleRunnerQueryMode())) {
184
bool allBookmarks = term.compare(i18nc("list of all konqueror bookmarks", "bookmarks"),
185
Qt::CaseInsensitive) == 0;
188
matchFirefoxBookmarks(context, allBookmarks, term);
191
matchOperaBookmarks(context, allBookmarks, term);
194
matchKonquerorBookmarks(context, allBookmarks, term);
199
KIcon BookmarksRunner::favicon(const KUrl &url)
201
// query the favicons module
202
const QString iconFile = KMimeType::favIconForUrl(url);
204
if (iconFile.isEmpty()) {
208
return KIcon(iconFile);
211
void BookmarksRunner::matchOperaBookmarks(Plasma::RunnerContext& context, bool allBookmarks,
214
QList<Plasma::QueryMatch> matches;
216
QLatin1String nameStart("\tNAME=");
217
QLatin1String urlStart("\tURL=");
218
QLatin1String descriptionStart("\tDESCRIPTION=");
221
foreach (const QString & entry, m_operaBookmarkEntries) {
222
if (!context.isValid()) {
226
QStringList entryLines = entry.split("\n");
227
if (!entryLines.first().startsWith(QString("#URL"))) {
228
continue; // skip folder entries
230
entryLines.pop_front();
236
foreach (const QString & line, entryLines) {
237
if (line.startsWith(nameStart)) {
238
name = line.mid( QString(nameStart).length() ).simplified();
239
} else if (line.startsWith(urlStart)) {
240
url = line.mid( QString(urlStart).length() ).simplified();
241
} else if (line.startsWith(descriptionStart)) {
242
description = line.mid(QString(descriptionStart).length())
247
if (url.simplified().isEmpty())
248
continue; // skip useless entries
250
Plasma::QueryMatch::Type type = Plasma::QueryMatch::NoMatch;
253
if (name.compare(term, Qt::CaseInsensitive) == 0
254
|| description.compare(term, Qt::CaseInsensitive) == 0) {
255
type = Plasma::QueryMatch::ExactMatch;
257
} else if (name.contains(term, Qt::CaseInsensitive)) {
258
type = Plasma::QueryMatch::PossibleMatch;
260
} else if (description.contains(term, Qt::CaseInsensitive)) {
261
type = Plasma::QueryMatch::PossibleMatch;
263
} else if (url.contains(term, Qt::CaseInsensitive)) {
264
type = Plasma::QueryMatch::PossibleMatch;
266
} else if (allBookmarks) {
267
type = Plasma::QueryMatch::PossibleMatch;
271
if (type != Plasma::QueryMatch::NoMatch) {
272
bool isNameEmpty = name.isEmpty();
273
bool isDescriptionEmpty = description.isEmpty();
275
Plasma::QueryMatch match(this);
277
match.setRelevance(relevance);
278
match.setIcon(m_icon);
280
// Try to set the following as text in this order: name, description, url
281
match.setText( isNameEmpty
283
(!isDescriptionEmpty ? description : url)
291
context.addMatches(term, matches);
294
void BookmarksRunner::matchKonquerorBookmarks(Plasma::RunnerContext& context, bool allBookmarks,
297
KBookmarkGroup bookmarkGroup = m_bookmarkManager->root();
299
QList<Plasma::QueryMatch> matches;
300
QStack<KBookmarkGroup> groups;
302
KBookmark bookmark = bookmarkGroup.first();
303
while (!bookmark.isNull()) {
304
if (!context.isValid()) {
308
if (bookmark.isSeparator()) {
309
bookmark = bookmarkGroup.next(bookmark);
313
if (bookmark.isGroup()) { // descend
314
//kDebug () << "descending into" << bookmark.text();
315
groups.push(bookmarkGroup);
316
bookmarkGroup = bookmark.toGroup();
317
bookmark = bookmarkGroup.first();
319
while (bookmark.isNull() && !groups.isEmpty()) {
320
if (!context.isValid()) {
324
bookmark = bookmarkGroup;
325
bookmarkGroup = groups.pop();
326
bookmark = bookmarkGroup.next(bookmark);
332
Plasma::QueryMatch::Type type = Plasma::QueryMatch::NoMatch;
335
const QString text = bookmark.text();
336
const QString url = bookmark.url().prettyUrl();
337
if (text.compare(term, Qt::CaseInsensitive) == 0) {
338
type = Plasma::QueryMatch::ExactMatch;
340
} else if (text.contains(term, Qt::CaseInsensitive)) {
341
type = Plasma::QueryMatch::PossibleMatch;
343
} else if (url.contains(term, Qt::CaseInsensitive)) {
344
type = Plasma::QueryMatch::PossibleMatch;
346
} else if (allBookmarks) {
347
type = Plasma::QueryMatch::PossibleMatch;
351
if (type != Plasma::QueryMatch::NoMatch) {
352
//kDebug() << "Found bookmark: " << bookmark.text() << " (" << bookmark.url().prettyUrl() << ")";
353
Plasma::QueryMatch match(this);
355
QIcon icon = favicon(bookmark.url());
357
match.setIcon(m_icon);
363
match.setRelevance(relevance);
364
match.setText(bookmark.text());
365
match.setData(bookmark.url().url());
369
bookmark = bookmarkGroup.next(bookmark);
370
while (bookmark.isNull() && !groups.isEmpty()) {
371
if (!context.isValid()) {
375
bookmark = bookmarkGroup;
376
bookmarkGroup = groups.pop();
377
//kDebug() << "ascending from" << bookmark.text() << "to" << bookmarkGroup.text();
378
bookmark = bookmarkGroup.next(bookmark);
381
context.addMatches(term, matches);
384
void BookmarksRunner::matchFirefoxBookmarks(Plasma::RunnerContext& context, bool allBookmarks, const QString& term)
390
QList<Plasma::QueryMatch> matches;
391
QString tmpTerm = term;
394
query = QSqlQuery("SELECT moz_bookmarks.fk, moz_bookmarks.title, moz_places.url," \
395
"moz_places.favicon_id FROM moz_bookmarks, moz_places WHERE " \
396
"moz_bookmarks.type = 1 AND moz_bookmarks.fk = moz_places.id");
398
const QString escapedTerm = tmpTerm.replace('\'', "\\'");
399
query = QSqlQuery("SELECT moz_bookmarks.fk, moz_bookmarks.title, moz_places.url," \
400
"moz_places.favicon_id FROM moz_bookmarks, moz_places WHERE " \
401
"moz_bookmarks.type = 1 AND moz_bookmarks.fk = moz_places.id AND " \
402
"(moz_bookmarks.title LIKE '%" + escapedTerm + "%' or moz_places.url LIKE '%"
403
+ escapedTerm + "%')");
406
while (query.next() && context.isValid()) {
407
const QString title = query.value(1).toString();
408
const QUrl url = query.value(2).toString();
409
//const int favicon_id = query.value(3).toInt();
411
if (title.isEmpty() || url.isEmpty() || url.scheme().contains("place")) {
412
//Don't use bookmarks with empty title, url or Firefox intern url
413
kDebug() << "element was not added";
417
Plasma::QueryMatch::Type type = Plasma::QueryMatch::NoMatch;
420
if (title.compare(term, Qt::CaseInsensitive) == 0) {
421
type = Plasma::QueryMatch::ExactMatch;
423
} else if (title.contains(term, Qt::CaseInsensitive)) {
424
type = Plasma::QueryMatch::PossibleMatch;
426
} else if (url.toString().contains(term, Qt::CaseInsensitive)) {
427
type = Plasma::QueryMatch::PossibleMatch;
429
} else if (allBookmarks) {
430
type = Plasma::QueryMatch::PossibleMatch;
434
Plasma::QueryMatch match(this);
435
QIcon icon = favicon(url);
437
match.setIcon(m_icon);
441
match.setText(title);
444
match.setRelevance(relevance);
447
context.addMatches(term, matches);
450
BookmarksRunner::Browser BookmarksRunner::whichBrowser()
452
//HACK find the default browser
453
KConfigGroup config(KSharedConfig::openConfig("kdeglobals"), QLatin1String("General") );
454
QString exec = config.readPathEntry(QLatin1String("BrowserApplication"), QString());
455
if (exec.isEmpty()) {
456
KService::Ptr service = KMimeTypeTrader::self()->preferredService("text/html");
458
exec = service->exec();
463
if (exec.contains("firefox", Qt::CaseInsensitive)) {
465
} else if (exec.contains("opera", Qt::CaseInsensitive)) {
472
void BookmarksRunner::run(const Plasma::RunnerContext &context, const Plasma::QueryMatch &action)
475
const QString term = action.data().toString();
476
KUrl url = KUrl(term);
478
//support urls like "kde.org" by transforming them to http://kde.org
479
if (url.protocol().isEmpty()) {
480
const int idx = term.indexOf('/');
483
url.setHost(term.left(idx));
486
const int queryStart = term.indexOf('?', idx);
488
if ((queryStart > -1) && (idx < queryStart)) {
489
pathLength = queryStart - idx;
490
url.setQuery(term.mid(queryStart));
493
url.setPath(term.mid(idx, pathLength));
495
url.setProtocol("http");
498
//kDebug() << "BookmarksRunner::run opening: " << url.url();
499
KToolInvocation::invokeBrowser(url.url());
502
QMimeData * BookmarksRunner::mimeDataForMatch(const Plasma::QueryMatch * match)
504
QMimeData * result = new QMimeData();
506
urls << QUrl(match->data().toString());
507
result->setUrls(urls);
509
result->setText(match->data().toString());
514
#include "bookmarksrunner.moc"