Created kio_recoll-kde4 as trying to maintain a kde4/5 merged version would need to many ifdefs
This commit is contained in:
@@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <string>
|
||||
using namespace std;
|
||||
|
||||
#include <qglobal.h>
|
||||
#include <qfile.h>
|
||||
#include <qtextstream.h>
|
||||
#include <qstring.h>
|
||||
|
||||
#include <kdebug.h>
|
||||
#include <kcomponentdata.h>
|
||||
#include <kstandarddirs.h>
|
||||
|
||||
#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<Rcl::SearchData> sdata(sd);
|
||||
STD_SHARED_PTR<Rcl::Query>query(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<Rcl::Query>(query), "Query results", sdata);
|
||||
if (src == 0) {
|
||||
error(KIO::ERR_SLAVE_DEFINED, "Can't build result sequence");
|
||||
return false;
|
||||
}
|
||||
m_source = STD_SHARED_PTR<DocSequence>(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;
|
||||
}
|
||||
Reference in New Issue
Block a user