From 2e98b95634066c4516a974480125fed991cfc509 Mon Sep 17 00:00:00 2001 From: Jean-Francois Dockes Date: Tue, 5 Apr 2016 09:50:37 +0200 Subject: [PATCH] Created kio_recoll-kde4 as trying to maintain a kde4/5 merged version would need to many ifdefs --- src/kde/kioslave/kio_recoll-kde4/00README.txt | 94 +++++ .../kioslave/kio_recoll-kde4/CMakeLists.txt | 75 ++++ .../kioslave/kio_recoll-kde4/Makefile.kde3 | 59 +++ .../kio_recoll-kde4/cleancmakestuff.sh | 6 + .../kioslave/kio_recoll-kde4/data/help.html | 100 +++++ .../kio_recoll-kde4/data/searchable.html | 28 ++ .../kio_recoll-kde4/data/welcome.html | 29 ++ src/kde/kioslave/kio_recoll-kde4/dirif.cpp | 318 +++++++++++++++ src/kde/kioslave/kio_recoll-kde4/htmlif.cpp | 301 ++++++++++++++ .../kioslave/kio_recoll-kde4/kio_recoll.cpp | 381 ++++++++++++++++++ src/kde/kioslave/kio_recoll-kde4/kio_recoll.h | 191 +++++++++ src/kde/kioslave/kio_recoll-kde4/notes.txt | 188 +++++++++ .../kioslave/kio_recoll-kde4/recoll.protocol | 11 + .../kioslave/kio_recoll-kde4/recollf.protocol | 11 + .../kio_recoll-kde4/recollnolist.protocol | 11 + 15 files changed, 1803 insertions(+) create mode 100644 src/kde/kioslave/kio_recoll-kde4/00README.txt create mode 100644 src/kde/kioslave/kio_recoll-kde4/CMakeLists.txt create mode 100644 src/kde/kioslave/kio_recoll-kde4/Makefile.kde3 create mode 100644 src/kde/kioslave/kio_recoll-kde4/cleancmakestuff.sh create mode 100644 src/kde/kioslave/kio_recoll-kde4/data/help.html create mode 100644 src/kde/kioslave/kio_recoll-kde4/data/searchable.html create mode 100644 src/kde/kioslave/kio_recoll-kde4/data/welcome.html create mode 100644 src/kde/kioslave/kio_recoll-kde4/dirif.cpp create mode 100644 src/kde/kioslave/kio_recoll-kde4/htmlif.cpp create mode 100644 src/kde/kioslave/kio_recoll-kde4/kio_recoll.cpp create mode 100644 src/kde/kioslave/kio_recoll-kde4/kio_recoll.h create mode 100644 src/kde/kioslave/kio_recoll-kde4/notes.txt create mode 100644 src/kde/kioslave/kio_recoll-kde4/recoll.protocol create mode 100644 src/kde/kioslave/kio_recoll-kde4/recollf.protocol create mode 100644 src/kde/kioslave/kio_recoll-kde4/recollnolist.protocol diff --git a/src/kde/kioslave/kio_recoll-kde4/00README.txt b/src/kde/kioslave/kio_recoll-kde4/00README.txt new file mode 100644 index 00000000..933b73b7 --- /dev/null +++ b/src/kde/kioslave/kio_recoll-kde4/00README.txt @@ -0,0 +1,94 @@ +Recoll KIO slave +================ + +An experiment with a recoll KIO slave. + +Caveat: I am only currently testing this with a production, but very +recent, version of KDE 4.1, and I don't intend to really support +older versions. The most usable aspects work under KDE 4.0 though. As +a reference, my test system is an up to date (2009-01) Kubuntu 8.10. + +Usage +===== + +Depending on the protocol name used, the search results will be +returned either as HTML pages (looking quite like a normal Recoll +result list), or as directory entries. + +The HTML mode only works with Konqueror, not Dolphin. The directory +mode is available with both browsers, and also application open dialog +(ie Kate). + +The HTML mode is much more usable than the directory mode at this point + +More detailed help/explanations can be found a document accessible +from the slave: + +To try things out, after building and installing, enter "recoll:/" in +a Konqueror URL entry. Depending on the KDE version, this will bring +you either to an HTML search form, or to a directory listing, where +you should READ THE HELP FILE. + +Building and installing: +======================= + +Only tested with KDE 4.1 and later. + +The main Recoll installation shares its prefix with the KIO slave, +which needs to use the KDE one. This means that, if KDE lives in /usr, +Recoll must be configured with --prefix=/usr, not /usr/local. Else +you'll have run-time problems, the slave will not be able to find the +Recoll configuration. + +!!*Notice: You cannot share a build directory between recoll and kio_recoll +because they use different configure options for the main lib, but build it +in the same place. The main lib "configure" is run at "cmake" time for +kio_recoll, the build is done at "make" time. + + +Recipe: + - Make sure the KDE4 core devel packages and cmake are installed. + + - Extract the Recoll source. + + - IF Recoll is not installed yet: configure recoll with + --prefix=/usr (or wherever KDE lives), build and install + Recoll. + + - In the Recoll source, go to kde/kioslave/recoll, then build and + install the kio slave: + +mkdir builddir +cd builddir +cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DQT_QMAKE_EXECUTABLE=/usr/bin/qmake-qt4 +make +sudo make install + + - You should have a look at where "make install" copies things, + because misconfigured distribution, generating wrong targets, are + frequent. Especially, you should check that kio_recoll.so is copied + to the right place, meaning among the output of "kde4-config --path + module". As an additional check, there should be many other + kio_[xxx].so in there. Same for the protocol file, check that it's + not alone in its directory (really, this sounds strange, but, to + this point, I've seen more systems with broken cmake/KDE configs + than correct ones). + +You need to build/update the index with recollindex, the KIO slave +doesn't deal with indexing for now. + + +Misc build problems: +=================== + +KUBUNTU 8.10 (updated to 2008-27-11) +------------------------------------ +cmake generates a bad dependancy on + /build/buildd/kde4libs-4.1.2/obj-i486-linux-gnu/lib/libkdecore.so +inside CMakeFiles/kio_recoll.dir/build.make + +Found no way to fix this. You need to edit the line and replace the +/build/[...]/lib with /usr/lib. This manifests itself with the +following error message: + + make[2]: *** No rule to make target `/build/buildd/kde4libs-4.1.2/obj-i486-linux-gnu/lib/libkdecore.so', needed by `lib/kio_recoll.so'. Stop. diff --git a/src/kde/kioslave/kio_recoll-kde4/CMakeLists.txt b/src/kde/kioslave/kio_recoll-kde4/CMakeLists.txt new file mode 100644 index 00000000..c8dbd240 --- /dev/null +++ b/src/kde/kioslave/kio_recoll-kde4/CMakeLists.txt @@ -0,0 +1,75 @@ +cmake_minimum_required(VERSION 2.6) + +project(kio_recoll) + +find_package(KDE4 REQUIRED) + +add_definitions(${QT_DEFINITIONS} ${KDE4_DEFINITIONS}) +add_definitions(-DKDE_DEFAULT_DEBUG_AREA=7130 + -DRECOLL_DATADIR=\\"${CMAKE_INSTALL_PREFIX}/share/recoll\\" + -DLIBDIR=\\"${CMAKE_INSTALL_PREFIX}/lib\\" + -DHAVE_CONFIG_H +) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${KDE4_ENABLE_EXCEPTIONS}") + +set(rcltop ${CMAKE_CURRENT_SOURCE_DIR}/../../../) + +# Execute recoll configuration to create autoconfig.h and version.h and +# generate a PIC lib +execute_process(COMMAND ${rcltop}/configure --disable-static --disable-qtgui --disable-x11mon --prefix=${CMAKE_INSTALL_PREFIX} --mandir=${CMAKE_INSTALL_PREFIX}/share/man + WORKING_DIRECTORY ${rcltop} +) + +link_directories(${rcltop}/.libs ${CMAKE_INSTALL_PREFIX}/lib) + +include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${KDE4_INCLUDES} + ${rcltop}/aspell + ${rcltop}/bincimapmime + ${rcltop}/common + ${rcltop}/index + ${rcltop}/internfile + ${rcltop}/query + ${rcltop}/rcldb + ${rcltop}/unac + ${rcltop}/utils + ${rcltop}/qtgui +) + +set(kio_recoll_SRCS kio_recoll.cpp htmlif.cpp dirif.cpp ${rcltop}/qtgui/guiutils.cpp) + +CHECK_LIBRARY_EXISTS(dl dlopen "" DLOPEN_IN_LIBDL) +IF(DLOPEN_IN_LIBDL) + LIST(APPEND EXTRA_LIBS dl) +ENDIF(DLOPEN_IN_LIBDL) +CHECK_LIBRARY_EXISTS(pthread pthread_sigmask "" PTHREAD_IN_LIBPTHREAD) +IF(PTHREAD_IN_LIBPTHREAD) + LIST(APPEND EXTRA_LIBS pthread) +ENDIF(PTHREAD_IN_LIBPTHREAD) + +# Had the idea to add e.g. /usr/lib/recoll to the rpath so that the dyn lib +# will be found at run time. But this does not seem to work with debian +# which strips RPATH by default (I think there is a way for libs in app-specific +# paths but I did not find it). Link with the .a instead. +#SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib/recoll") + +kde4_add_plugin(kio_recoll ${kio_recoll_SRCS}) + +add_custom_target(rcllib + COMMAND make PicStatic + WORKING_DIRECTORY ${rcltop} +) +add_dependencies(kio_recoll rcllib) + +target_link_libraries(kio_recoll recoll xapian z ${EXTRA_LIBS} ${KDE4_KIO_LIBS}) + +install(TARGETS kio_recoll DESTINATION ${PLUGIN_INSTALL_DIR}) + +IF ("${KDE_VERSION_MAJOR}.${KDE_VERSION_MINOR}" GREATER 4.0) + install(FILES recoll.protocol recollf.protocol DESTINATION ${SERVICES_INSTALL_DIR}) +ELSE ("${KDE_VERSION_MAJOR}.${KDE_VERSION_MINOR}" GREATER 4.0) + install(FILES recollnolist.protocol DESTINATION ${SERVICES_INSTALL_DIR} + RENAME recoll.protocol) +ENDIF ("${KDE_VERSION_MAJOR}.${KDE_VERSION_MINOR}" GREATER 4.0) + +install(FILES data/welcome.html data/help.html + DESTINATION ${DATA_INSTALL_DIR}/kio_recoll) diff --git a/src/kde/kioslave/kio_recoll-kde4/Makefile.kde3 b/src/kde/kioslave/kio_recoll-kde4/Makefile.kde3 new file mode 100644 index 00000000..634087f7 --- /dev/null +++ b/src/kde/kioslave/kio_recoll-kde4/Makefile.kde3 @@ -0,0 +1,59 @@ +depth=../../.. +include $(depth)/mk/sysconf + +# Need to set this by hand until we decide how to autoconf this without +# bringing 2mbytes of kde config files. +# May also need to adjust stuff such as the --rpath's below +# +KDE_INCLUDES=/usr/include/kde +QT_INCLUDES=/usr/include/qt3 + + +all: kio_recoll.so + +DEPS_CXXFLAGS = -MT pop3.lo -MD -MP -MF .deps/pop3.Tpo + +INC_CXXFLAGS = -I. \ + -I$(KDE_INCLUDES) -I$(QT_INCLUDES) \ + -I$(depth)/common -I$(depth)/query -I$(depth)/utils \ + -I$(depth)/qtgui -I$(depth)/rcldb + +PIC_CXXFLAGS = -fPIC -DPIC +DEBUG_CXXFLAGS = -DNDEBUG -DNO_DEBUG -O2 -O +LANG_CXXFLAGS = -fno-exceptions -fno-check-new -fno-common +QT_CXXFLAGS = -DQT_CLEAN_NAMESPACE -DQT_NO_ASCII_CAST -DQT_NO_STL \ + -DQT_NO_COMPAT -DQT_NO_TRANSLATION -DQT_THREAD_SUPPORT +SYS_CXXFLAGS = -D_GNU_SOURCE +THREAD_CXXFLAGS = -D_THREAD_SAFE -pthread -D_THREAD_SAFE -pthread + + +# -rpath=/usr/lib:/usr/local/lib \ + +LDFLAGS = \ + -Wl,--rpath -Wl,/usr/local/lib \ + -Wl,--rpath -Wl,/usr/X11R6/lib \ + -Wl,-export-dynamic -Wl,-soname -Wl,kio_recoll.so +THREAD_LDFLAGS = -pthread + +kio_recoll.so : kio_recoll.o libpic + c++ -shared $(LDFLAGS) $(THREAD_LDFLAGS) \ + -Wl,--no-undefined \ + kio_recoll.o piclib/librcl.a \ + $(LIBXAPIAN) $(LIBICONV) \ + -L/opt/kde3/lib -L/usr/local/lib -L/usr/X11R6/lib -lkio -lkdecore \ + -L/usr/lib/qt3/lib -lqt-mt \ + -L/usr/lib -lstdc++ -lm -lc \ + -o kio_recoll.so + +kio_recoll.o : kio_recoll.cpp kio_recoll.h + $(CXX) -c -pipe kio_recoll.cpp $(INC_CXXFLAGS) $(PIC_CXXFLAGS) \ + $(DEBUG_CXXFLAGS) $(LANG_CXXFLAGS) $(QT_CXXFLAGS) $(SYS_CXXFLAGS) \ + $(THREAD_CXXFLAGS) \ + -o kio_recoll.o + +libpic: + cd piclib;test -f Makefile || depth=$(depth)/.. sh $(depth)/../lib/mkMake + cd piclib;$(MAKE) CXXFLAGS="$(CXXFLAGS) $(PIC_CXXFLAGS)" \ + CFLAGS="$(CFLAGS) $(PIC_CXXFLAGS)" + +.PHONY: all libpic diff --git a/src/kde/kioslave/kio_recoll-kde4/cleancmakestuff.sh b/src/kde/kioslave/kio_recoll-kde4/cleancmakestuff.sh new file mode 100644 index 00000000..52176afe --- /dev/null +++ b/src/kde/kioslave/kio_recoll-kde4/cleancmakestuff.sh @@ -0,0 +1,6 @@ +#!/bin/sh +rm -rf CMakeCache.txt CMakeFiles CTestTestfile.cmake \ + cmake_install.cmake CMakeTmp cmake_uninstall.cmake \ + CPackConfig.cmake CPackSourceConfig.cmake DartTestfile.txt \ + install_manifest.txt kio_recoll_automoc.cpp \ + kio_recoll_automoc.cpp.files kio_recoll.so lib Makefile diff --git a/src/kde/kioslave/kio_recoll-kde4/data/help.html b/src/kde/kioslave/kio_recoll-kde4/data/help.html new file mode 100644 index 00000000..689cbdf8 --- /dev/null +++ b/src/kde/kioslave/kio_recoll-kde4/data/help.html @@ -0,0 +1,100 @@ + + + + Recoll Kio Slave + + + Recoll search +

Recoll kio slave

+ +

Use this module to perform Recoll searches from any program with + a KIO interface.

+ +

The module can work in two modes:

+ + +

The module is still in its infancy. You will undoubtedly obtain + strange effects from time to time. If you have any remarks or + ideas about improving kio_recoll, or observe an interesting and + reproducible sequence, please + report it.

+

kio_recoll is primarily + designed and tested with konqueror, and you will + undoubtedly get even more surprising effects with other tools.

+ +

The Html interface is currently much more usable. The directory + interface is extremely quirky.

+ +

The module is particularly unhelpful with search hits inside + email folders, which Konqueror has no way to access.

+ + +

HTML interface

+ +

This works more or less like the Recoll QT GUI, much simplified. The + + Recoll manual describes the queries that can be performed.

+ +

Most pages in the interface should quite self-explanatory.

+ +

You normally enter this interface by entering "recoll:" or + "recoll:/" in the Konqueror URL entry, and following the "search" + link. You can also directly enter "recoll:/search.html".
+ + In most circumstances, entering a link like + recoll:/john smith will also + yield an HTML result list.

+ +

Compared to QT Recoll, the nice point is that you can click or + drag/drop the icons to access the results in the standard desktop + way.

+ +

File manager interface

+ +

The path part of the URI is taken as a Recoll query + language string and executed. The results are displayed as + directory entries.

+ +

There are several ways to enter this interface:

+ + +

No search result details (samples, relevance etc.) are available, + but this interface allows multiple selections and copies, usage + inside any KDE open dialog, etc.

+ +

To avoid swamping the interface with thousands of results, the + result count is limited to 100 by default. You can change this value + by setting the kio_max_direntries parameter in your recoll + configuration file (typically ~/.recoll/recoll.conf)

+ +

Because of limitations in the current KIO slave usage, the actual + entry names are not those displayed but synthetic ones like + "recollResultxxx". This has unfortunate side-effects when + dragging/dropping the entries to some other application, or when + using an open dialog (the opened file doesn't have the correct path + to the original file).

+ +

Recoll Search

+ + + diff --git a/src/kde/kioslave/kio_recoll-kde4/data/searchable.html b/src/kde/kioslave/kio_recoll-kde4/data/searchable.html new file mode 100644 index 00000000..6e44f269 --- /dev/null +++ b/src/kde/kioslave/kio_recoll-kde4/data/searchable.html @@ -0,0 +1,28 @@ + + + Recoll searchable HTML + + + + + + + +

A Recoll-searchable HTML page

+ +

This is a text sample in which links have been inserted for + words, such as system installation, + which can be searched for in the whole document set by + using recoll

+ +

Also a little bit of javascript magic can make + all words searchable (try double-clicking any word).

+ + + diff --git a/src/kde/kioslave/kio_recoll-kde4/data/welcome.html b/src/kde/kioslave/kio_recoll-kde4/data/welcome.html new file mode 100644 index 00000000..0f13d428 --- /dev/null +++ b/src/kde/kioslave/kio_recoll-kde4/data/welcome.html @@ -0,0 +1,29 @@ + + + + Recoll Search + + + +

Recoll search

+ +

+

+ Query type:
+ Query language
+ All terms
+ Any term
+ File name
+ + + Enter search string: + + + + +
+

+ + + diff --git a/src/kde/kioslave/kio_recoll-kde4/dirif.cpp b/src/kde/kioslave/kio_recoll-kde4/dirif.cpp new file mode 100644 index 00000000..07d89ed8 --- /dev/null +++ b/src/kde/kioslave/kio_recoll-kde4/dirif.cpp @@ -0,0 +1,318 @@ +/* Copyright (C) 2008 J.F.Dockes + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * A lot of code in this file was copied from kio_beagle 0.4.0, + * which is a GPL program. The authors listed are: + * Debajyoti Bera + * + * KDE4 port: + * Stephan Binner + */ + +#include "autoconfig.h" + +#include + +#if KDE_IS_VERSION(4,1,0) +// Couldn't get listDir() to work with kde 4.0, konqueror keeps +// crashing because of kdirmodel, couldn't find a workaround (not +// saying it's impossible)... + +#include + +#include +#include +#include +#include + +#include "kio_recoll.h" +#include "pathut.h" + +using namespace KIO; + +static const QString resultBaseName("recollResult"); + +// Check if the input URL is of the form that konqueror builds by +// appending one of our result file names to the directory name (which +// is the search string). If it is, extract return the result document +// number. Possibly restart the search if the search string does not +// match the current one +bool RecollProtocol::isRecollResult(const KUrl &url, int *num, QString *q) +{ + *num = -1; + kDebug() << "url" << url; + + // Basic checks + if (!url.host().isEmpty() || url.path().isEmpty() || + (url.protocol().compare("recoll") && url.protocol().compare("recollf"))) + return false; + QString path = url.path(); + if (!path.startsWith("/")) + return false; + + // Look for the last '/' and check if it is followed by + // resultBaseName (riiiight...) + int slashpos = path.lastIndexOf("/"); + if (slashpos == -1 || slashpos == 0 || slashpos == path.length() -1) + return false; + slashpos++; + //kDebug() << "Comparing " << path.mid(slashpos, resultBaseName.length()) << + // "and " << resultBaseName; + if (path.mid(slashpos, resultBaseName.length()).compare(resultBaseName)) + return false; + + // Extract the result number + QString snum = path.mid(slashpos + resultBaseName.length()); + sscanf(snum.toAscii(), "%d", num); + if (*num == -1) + return false; + + //kDebug() << "URL analysis ok, num:" << *num; + + // We do have something that ressembles a recoll result locator. Check if + // this matches the current search, else have to run the requested one + *q = path.mid(1, slashpos-2); + return true; +} + +// Translate rcldoc result into directory entry +static const UDSEntry resultToUDSEntry(const Rcl::Doc& doc, int num) +{ + UDSEntry entry; + + KUrl url(doc.url.c_str()); +// kDebug() << doc.url.c_str(); + + entry.insert(KIO::UDSEntry::UDS_DISPLAY_NAME, url.fileName()); + char cnum[30];sprintf(cnum, "%04d", num); + entry.insert(KIO::UDSEntry::UDS_NAME, resultBaseName + cnum); + + if (!doc.mimetype.compare("application/x-fsdirectory") || + !doc.mimetype.compare("inode/directory")) { + entry.insert(KIO::UDSEntry::UDS_MIME_TYPE, "inode/directory"); + entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); + } else { + entry.insert(KIO::UDSEntry::UDS_MIME_TYPE, doc.mimetype.c_str()); + entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG); + } + entry.insert(KIO::UDSEntry::UDS_LOCAL_PATH, url.path()); + // For local files, supply the usual file stat information + struct stat info; + if (lstat(url.path().toAscii(), &info) >= 0) { + entry.insert( KIO::UDSEntry::UDS_SIZE, info.st_size); + entry.insert( KIO::UDSEntry::UDS_ACCESS, info.st_mode); + entry.insert( KIO::UDSEntry::UDS_MODIFICATION_TIME, info.st_mtime); + entry.insert( KIO::UDSEntry::UDS_ACCESS_TIME, info.st_atime); + entry.insert( KIO::UDSEntry::UDS_CREATION_TIME, info.st_ctime); + } + entry.insert(KIO::UDSEntry::UDS_TARGET_URL, doc.url.c_str()); + + return entry; +} + + +// From kio_beagle +static void createRootEntry(KIO::UDSEntry& entry) +{ + entry.clear(); + entry.insert( KIO::UDSEntry::UDS_NAME, "."); + entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); + entry.insert( KIO::UDSEntry::UDS_ACCESS, 0700); + entry.insert( KIO::UDSEntry::UDS_MIME_TYPE, "inode/directory"); +} + +// Points to html query screen +static void createGoHomeEntry(KIO::UDSEntry& entry) +{ + entry.clear(); + entry.insert(KIO::UDSEntry::UDS_NAME, "search.html"); + entry.insert(KIO::UDSEntry::UDS_DISPLAY_NAME, "Recoll search (click me)"); + entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG); + entry.insert(KIO::UDSEntry::UDS_TARGET_URL, "recoll:///search.html"); + entry.insert(KIO::UDSEntry::UDS_ACCESS, 0500); + entry.insert(KIO::UDSEntry::UDS_MIME_TYPE, "text/html"); + entry.insert(KIO::UDSEntry::UDS_ICON_NAME, "recoll"); +} + +// Points to help file +static void createGoHelpEntry(KIO::UDSEntry& entry) +{ + QString location = + KStandardDirs::locate("data", "kio_recoll/help.html"); + entry.clear(); + entry.insert(KIO::UDSEntry::UDS_NAME, "help"); + entry.insert(KIO::UDSEntry::UDS_DISPLAY_NAME, "Recoll help (click me first)"); + entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG); + entry.insert(KIO::UDSEntry::UDS_TARGET_URL, QString("file://") + + location); + entry.insert(KIO::UDSEntry::UDS_ACCESS, 0500); + entry.insert(KIO::UDSEntry::UDS_MIME_TYPE, "text/html"); + entry.insert(KIO::UDSEntry::UDS_ICON_NAME, "help"); +} + +void RecollProtocol::stat(const KUrl & url) +{ + kDebug() << url << endl ; + + UrlIngester ingest(this, url); + + KIO::UDSEntry entry; + entry.insert(KIO::UDSEntry::UDS_TARGET_URL, url.url()); + UrlIngester::RootEntryType rettp; + QueryDesc qd; + int num; + if (ingest.isRootEntry(&rettp)) { + switch(rettp) { + case UrlIngester::UIRET_ROOT: + createRootEntry(entry); + break; + case UrlIngester::UIRET_HELP: + createGoHelpEntry(entry); + break; + case UrlIngester::UIRET_SEARCH: + createGoHomeEntry(entry); + break; + default: + error(ERR_DOES_NOT_EXIST, ""); + break; + } + } else if (ingest.isResult(&qd, &num)) { + if (syncSearch(qd)) { + Rcl::Doc doc; + if (num >= 0 && !m_source.isNull() && + m_source->getDoc(num, doc)) { + entry = resultToUDSEntry(doc, num); + } else { + error(ERR_DOES_NOT_EXIST, ""); + } + } else { + // hopefully syncSearch() set the error? + } + } else if (ingest.isQuery(&qd)) { + // ie "recoll:/some string" or "recoll:/some string/" + // + // We have a problem here. We'd like to let the user enter + // either form and get an html or a dir contents result, + // depending on the ending /. Otoh this makes the name space + // inconsistent, because /toto can't be a file (the html + // result page) while /toto/ would be a directory ? or can it + // + // Another approach would be to use different protocol names + // to avoid any possibility of mixups + if (m_alwaysdir || ingest.alwaysDir() || ingest.endSlashQuery()) { + kDebug() << "Directory type"; + entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); + entry.insert(KIO::UDSEntry::UDS_ACCESS, 0700); + entry.insert(KIO::UDSEntry::UDS_MIME_TYPE, "inode/directory"); + entry.insert(KIO::UDSEntry::UDS_NAME, qd.query); + entry.insert( KIO::UDSEntry::UDS_MODIFICATION_TIME, time(0)); + entry.insert( KIO::UDSEntry::UDS_CREATION_TIME, time(0)); + } + } + statEntry(entry); + finished(); +} + +void RecollProtocol::listDir(const KUrl& url) +{ + kDebug() << url << endl; + + UrlIngester ingest(this, url); + UrlIngester::RootEntryType rettp; + QueryDesc qd; + + if (ingest.isRootEntry(&rettp)) { + switch(rettp) { + case UrlIngester::UIRET_ROOT: + { + kDebug() << "list /" << endl; + UDSEntryList entries; + KIO::UDSEntry entry; + createRootEntry(entry); + entries.append(entry); + createGoHomeEntry(entry); + entries.append(entry); + createGoHelpEntry(entry); + entries.append(entry); + listEntries(entries); + finished(); + } + return; + default: + error(ERR_CANNOT_ENTER_DIRECTORY, ""); + return; + } + } else if (ingest.isQuery(&qd)) { + // At this point, it seems that when the request is from + // konqueror autocompletion it comes with a / at the end, + // which offers an opportunity to not perform it. + if (ingest.endSlashQuery()) { + kDebug() << "Ends With /" << endl; + error(ERR_SLAVE_DEFINED, "Autocompletion search aborted"); + return; + } + if (!syncSearch(qd)) { + // syncSearch did the error thing + return; + } + // Fallthrough to actually listing the directory + } else { + kDebug() << "Cant grok input url"; + error(ERR_CANNOT_ENTER_DIRECTORY, ""); + return; + } + + static int maxentries = -1; + if (maxentries == -1) { + if (o_rclconfig) + o_rclconfig->getConfParam("kio_max_direntries", &maxentries); + if (maxentries == -1) + maxentries = 10000; + } + static const int pagesize = 200; + int pagebase = 0; + while (pagebase < maxentries) { + vector page; + int pagelen = m_source->getSeqSlice(pagebase, pagesize, page); + UDSEntry entry; + if (pagelen < 0) { + error(ERR_SLAVE_DEFINED, "Internal error"); + listEntry(entry, true); + break; + } + for (int i = 0; i < pagelen; i++) { + listEntry(resultToUDSEntry(page[i].doc, i), false); + } + if (pagelen != pagesize) { + listEntry(entry, true); + break; + } + pagebase += pagelen; + } + finished(); +} + +#else // <--- KDE 4.1+ + +#include +#include "kio_recoll.h" +bool RecollProtocol::isRecollResult(const KUrl &, int *, QString *) +{ + return false; +} +#endif diff --git a/src/kde/kioslave/kio_recoll-kde4/htmlif.cpp b/src/kde/kioslave/kio_recoll-kde4/htmlif.cpp new file mode 100644 index 00000000..d31dd72c --- /dev/null +++ b/src/kde/kioslave/kio_recoll-kde4/htmlif.cpp @@ -0,0 +1,301 @@ +/* Copyright (C) 2005 J.F.Dockes + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include + +#include + +using namespace std; + +#include +#include + +#include "rclconfig.h" +#include "rcldb.h" +#include "rclinit.h" +#include "pathut.h" +#include "searchdata.h" +#include "rclquery.h" +#include "wasatorcl.h" +#include "kio_recoll.h" +#include "docseqdb.h" +#include "readfile.h" +#include "smallut.h" +#include "plaintorich.h" +#include "internfile.h" +#include "wipedir.h" +#include "hldata.h" + +using namespace KIO; + +bool RecollKioPager::append(const string& data) +{ + if (!m_parent) + return false; + m_parent->data(QByteArray(data.c_str())); + return true; +} +#include +string RecollProtocol::makeQueryUrl(int page, bool isdet) +{ + ostringstream str; + str << "recoll://search/query?q=" << + url_encode((const char*)m_query.query.toUtf8()) << + "&qtp=" << (const char*)m_query.opt.toUtf8(); + if (page >= 0) + str << "&p=" << page; + if (isdet) + str << "&det=1"; + return str.str(); +} + +string RecollKioPager::detailsLink() +{ + string chunk = string("makeQueryUrl(m_parent->m_pager.pageNumber(), true) + "\">" + + "(show query)" + ""; + return chunk; +} + +static string parformat; +const string& RecollKioPager::parFormat() +{ + // Need to escape the % inside the query url + string qurl = m_parent->makeQueryUrl(-1, false), escurl; + for (string::size_type pos = 0; pos < qurl.length(); pos++) { + switch(qurl.at(pos)) { + case '%': + escurl += "%%"; + break; + default: + escurl += qurl.at(pos); + } + } + + ostringstream str; + str << + "" + "%R %S " + "Preview  " << + "Open " << + "%T
" + "%M %D   %U  %i
" + "%A %K"; + return parformat = str.str(); +} + +string RecollKioPager::pageTop() +{ + string pt = "

m_query.query.toUtf8())); + pt += "\">New Search"; + return pt; +// Would be nice to have but doesnt work because the query may be executed +// by another kio instance which has no idea of the current page o +#if 0 && KDE_IS_VERSION(4,1,0) + "    m_query.query.toUtf8())) + + "/\">Directory view (you may need to reload the page)" +#endif +} + +string RecollKioPager::nextUrl() +{ + int pagenum = pageNumber(); + if (pagenum < 0) + pagenum = 0; + else + pagenum++; + return m_parent->makeQueryUrl(pagenum); +} + +string RecollKioPager::prevUrl() +{ + int pagenum = pageNumber(); + if (pagenum <= 0) + pagenum = 0; + else + pagenum--; + return m_parent->makeQueryUrl(pagenum); +} + +static string welcomedata; + +void RecollProtocol::searchPage() +{ + mimeType("text/html"); + if (welcomedata.empty()) { + QString location = + KStandardDirs::locate("data", "kio_recoll/welcome.html"); + string reason; + if (location.isEmpty() || + !file_to_string((const char *)location.toUtf8(), + welcomedata, &reason)) { + welcomedata = "Recoll Error" + "

Could not locate Recoll welcome.html file: "; + welcomedata += reason; + welcomedata += "

"; + } + } + + string catgq; +#if 0 + // Catg filtering. A bit complicated to do because of the + // stateless thing (one more thing to compare to check if same + // query) right now. Would be easy by adding to the query + // language, but not too useful in this case, so scrap it for now. + list cats; + if (o_rclconfig->getMimeCategories(cats) && !cats.empty()) { + catgq = "

Filter on types: " + "All"; + for (list::iterator it = cats.begin(); it != cats.end();it++) { + catgq += "\n" + *it ; + } + } +#endif + + string tmp; + map subs; + subs['Q'] = (const char *)m_query.query.toUtf8(); + subs['C'] = catgq; + subs['S'] = ""; + pcSubst(welcomedata, tmp, subs); + data(tmp.c_str()); +} + +void RecollProtocol::queryDetails() +{ + mimeType("text/html"); + QByteArray array; + QTextStream os(&array, QIODevice::WriteOnly); + + os << "" << endl; + os << "" << endl; + os << "" << "Recoll query details" << "\n" << endl; + os << "" << endl; + os << "

Query details:

" << endl; + os << "

" << m_pager.queryDescription().c_str() <<"

"<< endl; + os << "

Return to results" << endl; + os << "" << endl; + data(array); +} + +class PlainToRichKio : public PlainToRich { +public: + PlainToRichKio(const string& nm) + : m_name(nm) + { + } + + virtual string header() { + if (m_inputhtml) { + return cstr_null; + } else { + return string("" + ""). + append(m_name). + append("

");
+	}
+    }
+
+    virtual string startMatch(unsigned int)
+    {
+	return string("");
+    }
+
+    virtual string endMatch() 
+    {
+	return string("");
+    }
+
+    const string &m_name;
+};
+
+void RecollProtocol::showPreview(const Rcl::Doc& idoc)
+{
+    FileInterner interner(idoc, o_rclconfig, FileInterner::FIF_forPreview);
+    Rcl::Doc fdoc;
+    string ipath = idoc.ipath;
+    if (!interner.internfile(fdoc, ipath)) {
+	error(KIO::ERR_SLAVE_DEFINED, "Cannot convert file to internal format");
+	return;
+    }
+    if (!interner.get_html().empty()) {
+	fdoc.text = interner.get_html();
+	fdoc.mimetype = "text/html";
+    }
+
+    mimeType("text/html");
+
+    string fname =  path_getsimple(fdoc.url).c_str();
+    PlainToRichKio ptr(fname);
+    ptr.set_inputhtml(!fdoc.mimetype.compare("text/html"));
+    list otextlist;
+    HighlightData hdata;
+    if (!m_source.isNull())
+	m_source->getTerms(hdata);
+    ptr.plaintorich(fdoc.text, otextlist, hdata);
+
+    QByteArray array;
+    QTextStream os(&array, QIODevice::WriteOnly);
+    for (list::iterator it = otextlist.begin(); 
+	 it != otextlist.end(); it++) {
+	os << (*it).c_str();
+    }
+    os << "" << endl;
+    data(array);
+}
+
+void RecollProtocol::htmlDoSearch(const QueryDesc& qd)
+{
+    kDebug() << "q" << qd.query << "option" << qd.opt << "page" << qd.page <<
+	"isdet" << qd.isDetReq << endl;
+ 
+    mimeType("text/html");
+
+    if (!syncSearch(qd))
+	return;
+    // syncSearch/doSearch do the setDocSource when needed
+    if (m_pager.pageNumber() < 0) {
+	m_pager.resultPageNext();
+    }
+    if (qd.isDetReq) {
+	queryDetails();
+	return;
+    }
+
+    // Check / adjust page number
+    if (qd.page > m_pager.pageNumber()) {
+	int npages = qd.page - m_pager.pageNumber();
+	for (int i = 0; i < npages; i++)
+	    m_pager.resultPageNext();
+    } else if (qd.page < m_pager.pageNumber()) {
+	int npages = m_pager.pageNumber() - qd.page;
+	for (int i = 0; i < npages; i++) 
+	    m_pager.resultPageBack();
+    }
+    // Display
+    m_pager.displayPage(o_rclconfig);
+}
diff --git a/src/kde/kioslave/kio_recoll-kde4/kio_recoll.cpp b/src/kde/kioslave/kio_recoll-kde4/kio_recoll.cpp
new file mode 100644
index 00000000..281ff254
--- /dev/null
+++ b/src/kde/kioslave/kio_recoll-kde4/kio_recoll.cpp
@@ -0,0 +1,381 @@
+/* Copyright (C) 2005 J.F.Dockes
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the
+ *   Free Software Foundation, Inc.,
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include 
+#include 
+#include 
+#include  
+
+#include 
+using namespace std;
+
+#include 
+#include 
+#include 
+#include 
+
+#include 
+#include 
+#include 
+
+#include "rclconfig.h"
+#include "rcldb.h"
+#include "rclinit.h"
+#include "pathut.h"
+#include "searchdata.h"
+#include "rclquery.h"
+#include "wasatorcl.h"
+#include "kio_recoll.h"
+#include "docseqdb.h"
+#include "readfile.h"
+#include "smallut.h"
+#include "textsplit.h"
+#include "guiutils.h"
+
+using namespace KIO;
+
+RclConfig *RecollProtocol::o_rclconfig;
+
+RecollProtocol::RecollProtocol(const QByteArray &pool, const QByteArray &app) 
+    : SlaveBase("recoll", pool, app), m_initok(false), m_rcldb(0),
+      m_alwaysdir(false)
+{
+    kDebug() << endl;
+    if (o_rclconfig == 0) {
+	o_rclconfig = recollinit(0, 0, m_reason);
+	if (!o_rclconfig || !o_rclconfig->ok()) {
+	    m_reason = string("Configuration problem: ") + m_reason;
+	    return;
+	}
+    }
+    if (o_rclconfig->getDbDir().empty()) {
+	// Note: this will have to be replaced by a call to a
+	// configuration building dialog for initial configuration? Or
+	// do we assume that the QT GUO is always used for this ?
+	m_reason = "No db directory in configuration ??";
+	return;
+    }
+    rwSettings(false);
+
+    m_rcldb = new Rcl::Db(o_rclconfig);
+    if (!m_rcldb) {
+	m_reason = "Could not build database object. (out of memory ?)";
+	return;
+    }
+
+    // Decide if we allow switching between html and file manager
+    // presentation by using an end slash or not. Can also be done dynamically 
+    // by switching proto names.
+    const char *cp = getenv("RECOLL_KIO_ALWAYS_DIR");
+    if (cp) {
+	m_alwaysdir = stringToBool(cp);
+    } else {
+	o_rclconfig->getConfParam("kio_always_dir", &m_alwaysdir);
+    }
+
+    cp = getenv("RECOLL_KIO_STEMLANG");
+    if (cp) {
+        m_stemlang = cp;
+    } else {
+        m_stemlang = "english";
+    }
+    m_pager.setParent(this);
+    m_initok = true;
+    return;
+}
+
+// There should be an object counter somewhere to delete the config when done.
+// Doesn't seem needed in the kio context.
+RecollProtocol::~RecollProtocol()
+{
+    kDebug();
+    delete m_rcldb;
+}
+
+bool RecollProtocol::maybeOpenDb(string &reason)
+{
+    if (!m_rcldb) {
+	reason = "Internal error: initialization error";
+	return false;
+    }
+    if (!m_rcldb->isopen() && !m_rcldb->open(Rcl::Db::DbRO)) {
+	reason = "Could not open database in " + o_rclconfig->getDbDir();
+	return false;
+    }
+    return true;
+}
+
+// This is never called afaik
+void RecollProtocol::mimetype(const KUrl &url)
+{
+    kDebug() << url << endl;
+    mimeType("text/html");
+    finished();
+}
+
+UrlIngester::UrlIngester(RecollProtocol *p, const KUrl& url)
+    : m_parent(p), m_slashend(false), m_alwaysdir(false),
+      m_retType(UIRET_NONE), m_resnum(0), m_type(UIMT_NONE)
+{
+    kDebug() << "Url" << url;
+    m_alwaysdir = !url.protocol().compare("recollf");
+    QString path = url.path();
+    if (url.host().isEmpty()) {
+	if (path.isEmpty() || !path.compare("/")) {
+	    m_type = UIMT_ROOTENTRY;
+	    m_retType = UIRET_ROOT;
+	    return;
+	} else if (!path.compare("/help.html")) {
+	    m_type = UIMT_ROOTENTRY;
+	    m_retType = UIRET_HELP;
+	    return;
+	} else if (!path.compare("/search.html")) {
+	    m_type = UIMT_ROOTENTRY;
+	    m_retType = UIRET_SEARCH;
+	    // Retrieve the query value for preloading the form
+	    m_query.query = url.queryItem("q");
+	    return;
+	} else if (m_parent->isRecollResult(url, &m_resnum, &m_query.query)) {
+	    m_type = UIMT_QUERYRESULT;
+	    m_query.opt = "l";
+	    m_query.page = 0;
+	} else {
+	    // Have to think this is some search string
+	    m_type = UIMT_QUERY;
+	    m_query.query = url.path();
+	    m_query.opt = "l";
+	    m_query.page = 0;
+	}
+    } else {
+	// Non empty host, url must be something like :
+	//      //search/query?q=query¶m=value...
+	kDebug() << "host" << url.host() << "path" << url.path();
+	if (url.host().compare("search") || url.path().compare("/query")) {
+	    return;
+	}
+	m_type = UIMT_QUERY;
+	// Decode the forms' arguments
+	m_query.query = url.queryItem("q");
+
+	m_query.opt = url.queryItem("qtp");
+	if (m_query.opt.isEmpty()) {
+	    m_query.opt = "l";
+	} 
+	QString p = url.queryItem("p");
+	if (p.isEmpty()) {
+	    m_query.page = 0;
+	} else {
+	    sscanf(p.toAscii(), "%d", &m_query.page);
+	}
+	p = url.queryItem("det");
+	m_query.isDetReq = !p.isEmpty();
+	
+	p = url.queryItem("cmd");
+	if (!p.isEmpty() && !p.compare("pv")) {
+	    p = url.queryItem("dn");
+	    if (!p.isEmpty()) {
+		// Preview and no docnum ??
+		m_resnum = atoi((const char *)p.toUtf8());
+		// Result in page is 1+
+		m_resnum--;
+		m_type = UIMT_PREVIEW;
+	    }
+	}
+    }
+    if (m_query.query.startsWith("/"))
+	m_query.query.remove(0,1);
+    if (m_query.query.endsWith("/")) {
+	kDebug() << "Ends with /";
+	m_slashend = true;
+	m_query.query.chop(1);
+    } else {
+	m_slashend = false;
+    }
+    return;
+}
+
+bool RecollProtocol::syncSearch(const QueryDesc &qd)
+{
+    kDebug();
+    if (!m_initok || !maybeOpenDb(m_reason)) {
+	string reason = "RecollProtocol::listDir: Init error:" + m_reason;
+	error(KIO::ERR_SLAVE_DEFINED, reason.c_str());
+	return false;
+    }
+    if (qd.sameQuery(m_query)) {
+	return true;
+    }
+    // doSearch() calls error() if appropriate.
+    return doSearch(qd);
+}
+
+// This is used by the html interface, but also by the directory one
+// when doing file copies for exemple. This is the central dispatcher
+// for requests, it has to know a little about both models.
+void RecollProtocol::get(const KUrl& url)
+{
+    kDebug() << url << endl;
+
+    if (!m_initok || !maybeOpenDb(m_reason)) {
+	string reason = "Recoll: init error: " + m_reason;
+	error(KIO::ERR_SLAVE_DEFINED, reason.c_str());
+	return;
+    }
+
+    UrlIngester ingest(this, url);
+    UrlIngester::RootEntryType rettp;
+    QueryDesc qd;
+    int resnum;
+    if (ingest.isRootEntry(&rettp)) {
+	switch(rettp) {
+	case UrlIngester::UIRET_HELP: 
+	    {
+		QString location = 
+		    KStandardDirs::locate("data", "kio_recoll/help.html");
+		redirection(location);
+	    }
+	    goto out;
+	default:
+	    searchPage();
+	    goto out;
+	}
+    } else if (ingest.isResult(&qd, &resnum)) {
+	// Url matched one generated by konqueror/Dolphin out of a
+	// search directory listing: ie: 
+        // recoll:/some search string/recollResultxx
+	//
+	// This happens when the user drags/drop the result to another
+	// app, or with the "open-with" right-click. Does not happen
+	// if the entry itself is clicked (the UDS_URL is apparently
+	// used in this case
+	//
+	// Redirect to the result document URL
+	if (!syncSearch(qd)) {
+	    return;
+	}
+	Rcl::Doc doc;
+	if (resnum >= 0 && !m_source.isNull() && m_source->getDoc(resnum, doc)) {
+	    mimeType(doc.mimetype.c_str());
+	    redirection(KUrl::fromLocalFile((const char *)(doc.url.c_str()+7)));
+	    goto out;
+	}
+    } else if (ingest.isPreview(&qd, &resnum)) {
+	if (!syncSearch(qd)) {
+	    return;
+	}
+	Rcl::Doc doc;
+	if (resnum >= 0 && !m_source.isNull() && m_source->getDoc(resnum, doc)) {
+	    showPreview(doc);
+	    goto out;
+	}
+    } else if (ingest.isQuery(&qd)) {
+#if 0 
+// Do we need this ?
+	if (host.isEmpty()) {
+	    char cpage[20];sprintf(cpage, "%d", page);
+	    QString nurl = QString::fromAscii("recoll://search/query?q=") +
+		query + "&qtp=" + opt + "&p=" + cpage;
+	    redirection(KUrl(nurl));
+	    goto out;
+	}
+#endif
+	// htmlDoSearch does the search syncing (needs to know about changes).
+	htmlDoSearch(qd);
+	goto out;
+    }
+
+    error(KIO::ERR_SLAVE_DEFINED, "Unrecognized URL or internal error");
+ out:
+    finished();
+}
+
+// Execute Recoll search, and set the docsource
+bool RecollProtocol::doSearch(const QueryDesc& qd)
+{
+    kDebug() << "query" << qd.query << "opt" << qd.opt;
+    m_query = qd;
+
+    char opt = qd.opt.isEmpty() ? 'l' : qd.opt.toUtf8().at(0);
+    string qs = (const char *)qd.query.toUtf8();
+    Rcl::SearchData *sd = 0;
+    if (opt != 'l') {
+	Rcl::SearchDataClause *clp = 0;
+	if (opt == 'f') {
+	    clp = new Rcl::SearchDataClauseFilename(qs);
+	} else {
+            clp = new Rcl::SearchDataClauseSimple(opt == 'o' ? Rcl::SCLT_OR : 
+                                                  Rcl::SCLT_AND, qs);
+	}
+	sd = new Rcl::SearchData(Rcl::SCLT_OR, m_stemlang);
+	if (sd && clp)
+	    sd->addClause(clp);
+    } else {
+	sd = wasaStringToRcl(o_rclconfig, m_stemlang, qs, m_reason);
+    }
+    if (!sd) {
+	m_reason = "Internal Error: cant build search";
+	error(KIO::ERR_SLAVE_DEFINED, m_reason.c_str());
+	return false;
+    }
+
+    STD_SHARED_PTR sdata(sd);
+    STD_SHARED_PTRquery(new Rcl::Query(m_rcldb));
+    query->setCollapseDuplicates(prefs.collapseDuplicates);
+    if (!query->setQuery(sdata)) {
+	m_reason = "Query execute failed. Invalid query or syntax error?";
+	error(KIO::ERR_SLAVE_DEFINED, m_reason.c_str());
+	return false;
+    }
+
+    DocSequenceDb *src = 
+	new DocSequenceDb(STD_SHARED_PTR(query), "Query results", sdata);
+    if (src == 0) {
+	error(KIO::ERR_SLAVE_DEFINED, "Can't build result sequence");
+	return false;
+    }
+    m_source = STD_SHARED_PTR(src);
+    // Reset pager in all cases. Costs nothing, stays at page -1 initially
+    // htmldosearch will fetch the first page if needed.
+    m_pager.setDocSource(m_source);
+    return true;
+}
+
+// Note: KDE_EXPORT is actually needed on Unix when building with
+// cmake. Says something like __attribute__(visibility(defautl))
+// (cmake apparently sets all symbols to not exported)
+extern "C" {KDE_EXPORT int kdemain(int argc, char **argv);}
+
+int kdemain(int argc, char **argv)
+{
+#ifdef KDE_VERSION_3
+    KInstance instance("kio_recoll");
+#else
+    KComponentData instance("kio_recoll");
+#endif
+    kDebug() << "*** starting kio_recoll " << endl;
+
+    if (argc != 4)  {
+	kDebug() << "Usage: kio_recoll proto dom-socket1 dom-socket2\n" << endl;
+	exit(-1);
+    }
+
+    RecollProtocol slave(argv[2], argv[3]);
+    slave.dispatchLoop();
+
+    kDebug() << "kio_recoll Done" << endl;
+    return 0;
+}
diff --git a/src/kde/kioslave/kio_recoll-kde4/kio_recoll.h b/src/kde/kioslave/kio_recoll-kde4/kio_recoll.h
new file mode 100644
index 00000000..b4fac1a9
--- /dev/null
+++ b/src/kde/kioslave/kio_recoll-kde4/kio_recoll.h
@@ -0,0 +1,191 @@
+#ifndef _RECOLL_H
+#define _RECOLL_H
+/* Copyright (C) 2005 J.F.Dockes
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the
+ *   Free Software Foundation, Inc.,
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include 
+using std::string;
+
+#include 
+#include 
+
+#include 
+#include 
+#include 
+#include 
+
+#include "rclconfig.h"
+#include "rcldb.h"
+#include "reslistpager.h"
+#include "docseq.h"
+#include MEMORY_INCLUDE
+
+class RecollProtocol;
+
+/** Specialize the recoll html pager for the kind of links we use etc. */
+class RecollKioPager : public ResListPager {
+public:
+    RecollKioPager() : m_parent(0) {}
+    void setParent(RecollProtocol *proto) {m_parent = proto;}
+
+    virtual bool append(const string& data);
+    virtual bool append(const string& data, int, const Rcl::Doc&)
+    {return append(data);}
+    virtual string detailsLink();
+    virtual const string &parFormat();
+    virtual string nextUrl();
+    virtual string prevUrl();
+    virtual string pageTop();
+
+private:
+    RecollProtocol *m_parent;
+};
+
+class QueryDesc {
+public:
+    QueryDesc() : opt("l"), page(0), isDetReq(false) {}
+    QString query;
+    QString opt;
+    int page;
+    bool isDetReq;
+    bool sameQuery(const QueryDesc& o) const {
+	return !opt.compare(o.opt) && !query.compare(o.query);
+    }
+};
+
+// Our virtual tree is a bit complicated. We need a class to analyse an URL
+// and tell what we should do with it
+class UrlIngester {
+public:
+    UrlIngester(RecollProtocol *p, const KUrl& url);
+    enum RootEntryType {UIRET_NONE, UIRET_ROOT, UIRET_HELP, UIRET_SEARCH};
+    bool isRootEntry(RootEntryType *tp) {
+	if (m_type != UIMT_ROOTENTRY) return false;
+	*tp = m_retType;
+	return true;
+    }
+    bool isQuery(QueryDesc *q) {
+	if (m_type != UIMT_QUERY) return false;
+	*q = m_query;
+	return true;
+    }
+    bool isResult(QueryDesc *q, int *num) {
+	if (m_type != UIMT_QUERYRESULT) return false;
+	*q = m_query;
+	*num = m_resnum;
+	return true;
+    }
+    bool isPreview(QueryDesc *q, int *num) {
+	if (m_type != UIMT_PREVIEW) return false;
+	*q = m_query;
+	*num = m_resnum;
+	return true;
+    }
+    bool endSlashQuery() {return m_slashend;}
+    bool alwaysDir() {return m_alwaysdir;}
+
+private:
+    RecollProtocol *m_parent;
+    QueryDesc       m_query;
+    bool            m_slashend;
+    bool            m_alwaysdir;
+    RootEntryType   m_retType;
+    int             m_resnum;
+    enum MyType {UIMT_NONE, UIMT_ROOTENTRY, UIMT_QUERY, UIMT_QUERYRESULT,
+		 UIMT_PREVIEW};
+    MyType           m_type;
+};
+
+
+/**
+ * A KIO slave to execute and display Recoll searches.
+ *
+ * Things are made a little complicated because KIO slaves can't hope
+ * that their internal state will remain consistent with their user
+ * application state: slaves die, are restarted, reused, at random
+ * between requests. 
+ * In our case, this means that any request has to be processed
+ * without reference to the last operation performed. Ie, if the
+ * search parameters are not those from the last request, the search
+ * must be restarted anew. This happens for example with different
+ * searches in 2 konqueror screens: typically only one kio_slave will
+ * be used.
+ * The fact that we check if the search is the same as the last one,
+ * to avoid restarting is an optimization, not the base mechanism
+ * (contrary to what was initially assumed, and may have left a few
+ * crumbs around).
+ *
+ * We have two modes of operation, one based on html forms and result
+ * pages, which can potentially be developped to the full Recoll
+ * functionality, and one based on a directory listing model, which
+ * will always be more limited, but may be useful in some cases to
+ * allow easy copying of files etc. Which one is in use is decided by
+ * the form of the URL. 
+ */
+class RecollProtocol : public KIO::SlaveBase {
+ public:
+    RecollProtocol(const QByteArray &pool, const QByteArray &app );
+    virtual ~RecollProtocol();
+    virtual void mimetype(const KUrl& url);
+    virtual void get(const KUrl& url);
+    // The directory mode is not available with KDE 4.0, I could find
+    // no way to avoid crashing kdirmodel
+#if KDE_IS_VERSION(4,1,0)
+    virtual void stat(const KUrl & url);
+    virtual void listDir(const KUrl& url);
+#endif
+
+    static RclConfig  *o_rclconfig;
+
+    friend class RecollKioPager;
+    friend class UrlIngester;
+
+ private:
+    bool maybeOpenDb(string& reason);
+    bool URLToQuery(const KUrl &url, QString& q, QString& opt, int *page=0);
+    bool doSearch(const QueryDesc& qd);
+
+    void searchPage();
+    void queryDetails();
+    string makeQueryUrl(int page, bool isdet = false);
+    bool syncSearch(const QueryDesc& qd);
+    void htmlDoSearch(const QueryDesc& qd);
+    void showPreview(const Rcl::Doc& doc);
+    bool isRecollResult(const KUrl &url, int *num, QString* q);
+
+    bool        m_initok;
+    Rcl::Db    *m_rcldb;
+    string      m_reason;
+    bool        m_alwaysdir;
+    string      m_stemlang; // english by default else env[RECOLL_KIO_STEMLANG]
+
+    // Search state: because of how the KIO slaves are used / reused,
+    // we can't be sure that the next request will be for the same
+    // search, and we need to check and restart one if the data
+    // changes. This is very wasteful but hopefully won't happen too
+    // much in actual use. One possible workaround for some scenarios
+    // (one slave several konqueror windows) would be to have a small
+    // cache of recent searches kept open.
+    RecollKioPager m_pager;
+    STD_SHARED_PTR m_source;
+    // Note: page here is not used, current page always comes from m_pager.
+    QueryDesc      m_query;
+};
+
+extern "C" {int kdemain(int, char**);}
+
+#endif // _RECOLL_H
diff --git a/src/kde/kioslave/kio_recoll-kde4/notes.txt b/src/kde/kioslave/kio_recoll-kde4/notes.txt
new file mode 100644
index 00000000..8b8127ff
--- /dev/null
+++ b/src/kde/kioslave/kio_recoll-kde4/notes.txt
@@ -0,0 +1,188 @@
+Recoll KIO Slave notes/todoes/etc.
+
+Goal: access Recoll search from other applications. 
+
+We want to allow URLs like recoll:/?? to be used inside
+Konqueror/Dolphin and open dialogs, to start a search with results
+displayed/used inside the application.
+
+
+Todoes
+======
+
+
+Implementation notes:
+====================
+
+- There are two main ways to do this: 
+  - a-la kio_beagle, using listDir() to list entries pointing to the
+    different operations or objects (help, status, search result
+    entries, bookmarks, whatever...). The nice thing is that the
+    results really look like file object in a directory (probably,
+    didn't try it actually), no need for look and feel, it's provided by
+    KDE 
+
+  - Or a la strigi: all interactions are through html pages and get()
+    operations.  Much simpler, but does not allow file management
+    operations (except by drag/drop from the result list), and can't
+    use it inside other application's Open dialogs.
+
+Recoll is currently doing both things on KDE4.1 (only html for KDE4.0). 
+
+
+Virtual tree:
+=============
+
+recoll: 
+recoll:/
+recoll:/help.html
+recoll:/search.html
+    Purely synthetic top level entries. Yes, given the following, this means
+    that you can't search for 'help.html' directly (but you can do it through 
+    the html interface). These have to exist in the top level directory
+
+recoll:/some search string
+recoll:/some search string/
+ We have a 'mode' determined by the protocol name:
+  	 recoll -> mixed
+	 recollf -> file manager
+  - html mode: these redirect to recoll://search/query?q="some search string"
+  - file manager mode: do the search and display results.
+  - mixed mode: what mode is entered depends on ending /
+
+recoll://search/query?q=...
+  html mode search  
+
+recoll:/some search string/recollResultxyz
+xyz is the index in result list.
+
+When generating a directory, with use bogus names for the result
+entries (the displayed ones are from UDS_DISPLAY_NAME and are the real
+names). When doing drag/drop or "open with" Konqueror/Dolphin builds
+an url by concatenating the current directory name and the UDS_NAME,
+instead of using UDS_TARGET_URL as when the entry is clicked. This
+forces us to use identifying names including the result number, which
+has many ennoying side effects (such as the target app not knowing the
+real file path...
+
+
+KIO notes:
+=========
+
+- Slaves are reused seemingly at random. Even with connection-oriented
+  ones (ie ftp), you can have 2 konqueror windows on different hosts
+  with only one slave which will have to close/reopen the connection at
+  each switch.
+   For slaves with really expensive internal state, this is very wasteful.
+
+- Use cases for father_url+name or target_url are ill defined.
+- Need a way to block autocompletion queries !
+- No way to display the target URL in konqueror
+
+Todoes
+======
+- Improve the html interface to support more functions
+ - Category filtering
+ - Sorting
+
+- Find a way to use the html form to enter the query and get the
+  results as a directory ?  - Would it be possible to use a redirect
+  to switch to the directory-oriented results for a query from the
+  html form ? 
+  -> No, even a redirect to a form that would initially trigger a
+     listDir() triggers a get() when performed from a get()
+
+KDE misc notes
+==================
+Debug areas: /usr/share/kde4/config/kdebug.areas
+kdebugdialog [--fullmode] pour configurer
+./.kde/share/config/kdebugrc
+Output to ~/.xession-errors by default. How to change ?
+
+kio_recoll misc notes:
+===========================
+Probleme quand l'url se termine par un / et qu'on edite le mot,
+konqueror lance une recherche a chaque lettre.
+
+Apparemment c'est l'entree "listing" du .protocol qui decide si le plugin
+est traité plutot comme un dirlister ou comme un htmlgetter. Curieusement,
+le changement ne s'opere pas toujours immediatement quand on change le
+fichier .proto, y compris apres avoir tue tous les process kde (changement
+ŕ la deuxieme execution de konqueror sur kde4.0). Sur kde4.0 il faut que le
+.proto soit sans entree "listing"
+
+Problemes de gestion de l'etat
+===============================
+Les KIO slaves ne sont pas associes a une fenetre ! ils sont
+reutilises au hasard, et leur etat n'a aucune raison de correspondre a
+celui de l'affichage. On peut tres bien avoir 1 fenetre 2 kio ou 1 kio
+deux fenetres, et le next d'un search peut arriver au kio qui a
+l'autre search, donc n'importenaouak. Il faudrait que l'etat soit
+partage et accede par un identifiant uniquement determine par l'url de
+la fenetre.
+
+Meme pour une fenetre unique, au bout d'un moment le kio timeout et exite.
+
+En fait les slaves ne peuvent pas stocker d'etat du tout. Donc:
+ - soit un serveur central auquel ils parlent
+ - soit ils relancent dynamiquement les recherches si pas de match
+C'est vrai aussi bien pour les dirlists que pour la version html. 
+
+J'ai essaye de mettre une boucle timeout callback callspecial() mais
+ca ne sert a rien, c'est gere dans le process kio_slave, ca ne
+maintient pas l'association avec un konqueror.
+
+KDE_FORK_SLAVES sort of solves the problem in a limited way: 
+ - It applies to an application instance, not a KIO slave type, so it
+   affects other KIO usages.  
+ - If the application has 2 sessions with the same KIO there are no 
+   warranties that 1 kio per session will be used ?
+
+
+
+Old KDE3 notes, 
+===============
+
+kio_recoll has not been checked or worked under KDE3 for eons, no
+reason to believe it works.
+
+- Not using libtool. Probably should. compilation flags in the Makefile
+  were copy-pasted from a kdebase compilation tree on FreeBSD (kio/man).
+- You MUST install a kio_recoll.la in lib/kde3 along with kio_recoll.so,
+  else kdeinit won't be able to load the lib (probably uses the libltdl
+  thingy?). The one in this directory was duplicated/adjusted from
+  kio_man.la. The contents don't seem too critical, just needs to exist.
+- If you want to try, compile, then install kio_recoll.la kio_recoll.so
+  wherever kde keeps its plugins (ie: lib/kde3), and recoll.protocol in the
+  services directory (share/services ? look for other .protocol file).
+- I saw after doing the build/config mockup that kdevelop can generate a
+  kio_slave project. This might be the next thing to do. otoh would need to
+  separate the kio from the main source to avoid having to distribute 2megs
+  of kde build config files.
+
+
+
+Connected mode
+==============
+Tried to add bogus connection status to see if this would prevent switching between apps/slaves, doesnt... checked that's the same for kio_ftp
+
+void RecollProtocol::openConnection() 
+{
+    kDebug();
+    connected();
+}
+void RecollProtocol::closeConnection() 
+{
+    kDebug();
+}
+void RecollProtocol::setHost(const QString& host, quint16, 
+			     const QString&, const QString&)
+{
+    kDebug() << host;
+}
+void RecollProtocol::slave_status()
+{
+    kDebug();
+    slaveStatus("search", true);
+}
++    connected(); call in maybeopendb()
diff --git a/src/kde/kioslave/kio_recoll-kde4/recoll.protocol b/src/kde/kioslave/kio_recoll-kde4/recoll.protocol
new file mode 100644
index 00000000..c69fc77d
--- /dev/null
+++ b/src/kde/kioslave/kio_recoll-kde4/recoll.protocol
@@ -0,0 +1,11 @@
+[Protocol]
+exec=kio_recoll
+protocol=recoll
+input=none
+output=filesystem
+listing=Name,Type, URL
+reading=true
+defaultMimeType=text/html
+Icon=recoll
+Class=:local
+URIMode=rawuri
diff --git a/src/kde/kioslave/kio_recoll-kde4/recollf.protocol b/src/kde/kioslave/kio_recoll-kde4/recollf.protocol
new file mode 100644
index 00000000..fb150ecf
--- /dev/null
+++ b/src/kde/kioslave/kio_recoll-kde4/recollf.protocol
@@ -0,0 +1,11 @@
+[Protocol]
+exec=kio_recoll
+protocol=recollf
+input=none
+output=filesystem
+listing=Name,Type, URL
+reading=true
+defaultMimeType=text/html
+Icon=recoll
+Class=:local
+URIMode=rawuri
diff --git a/src/kde/kioslave/kio_recoll-kde4/recollnolist.protocol b/src/kde/kioslave/kio_recoll-kde4/recollnolist.protocol
new file mode 100644
index 00000000..cb5d55e4
--- /dev/null
+++ b/src/kde/kioslave/kio_recoll-kde4/recollnolist.protocol
@@ -0,0 +1,11 @@
+[Protocol]
+exec=kio_recoll
+protocol=recoll
+input=none
+output=filesystem
+# Version for kde4.0: no "listing" entry
+reading=true
+defaultMimeType=text/html
+Icon=recoll
+Class=:local
+URIMode=rawuri