327 lines
12 KiB
C++
327 lines
12 KiB
C++
/* 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 <dbera.web@gmail.com>
|
|
*
|
|
* KDE4 port:
|
|
* Stephan Binner <binner@kde.org>
|
|
*/
|
|
|
|
#include "autoconfig.h"
|
|
|
|
// 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 <sys/stat.h>
|
|
|
|
#include <QDebug>
|
|
#include <QUrl>
|
|
#include <QStandardPaths>
|
|
|
|
#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 QUrl &url, int *num, QString *q)
|
|
{
|
|
*num = -1;
|
|
qDebug() << "RecollProtocol::isRecollResult: url: " << url;
|
|
|
|
// Basic checks
|
|
if (!url.host().isEmpty() || url.path().isEmpty() ||
|
|
(url.scheme().compare("recoll") && url.scheme().compare("recollf"))) {
|
|
qDebug() << "RecollProtocol::isRecollResult: no: url.host " <<
|
|
url.host() << " path " << url.path() << " scheme " << url.scheme();
|
|
return false;
|
|
}
|
|
|
|
QString path = url.path();
|
|
qDebug() << "RecollProtocol::isRecollResult: path: " << 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++;
|
|
//qDebug() << "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.toUtf8(), "%d", num);
|
|
if (*num == -1)
|
|
return false;
|
|
|
|
//qDebug() << "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;
|
|
|
|
QUrl url(doc.url.c_str());
|
|
// qDebug() << 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().toUtf8(), &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 =
|
|
QStandardPaths::locate(QStandardPaths::GenericDataLocation,
|
|
"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 QUrl& url)
|
|
{
|
|
qDebug() << "RecollProtocol::stat:" << url;
|
|
|
|
UrlIngester ingest(this, url);
|
|
|
|
KIO::UDSEntry entry;
|
|
entry.insert(KIO::UDSEntry::UDS_TARGET_URL, url.url());
|
|
entry.insert(KIO::UDSEntry::UDS_URL, url.url());
|
|
UrlIngester::RootEntryType rettp;
|
|
QueryDesc qd;
|
|
int num;
|
|
if (ingest.isRootEntry(&rettp)) {
|
|
qDebug() << "RecollProtocol::stat: root entry";
|
|
switch(rettp) {
|
|
case UrlIngester::UIRET_ROOT:
|
|
qDebug() << "RecollProtocol::stat: root";
|
|
createRootEntry(entry);
|
|
break;
|
|
case UrlIngester::UIRET_HELP:
|
|
qDebug() << "RecollProtocol::stat: root help";
|
|
createGoHelpEntry(entry);
|
|
break;
|
|
case UrlIngester::UIRET_SEARCH:
|
|
qDebug() << "RecollProtocol::stat: root search";
|
|
createGoHomeEntry(entry);
|
|
break;
|
|
default:
|
|
qDebug() << "RecollProtocol::stat: ??";
|
|
error(ERR_DOES_NOT_EXIST, QString());
|
|
break;
|
|
}
|
|
} else if (ingest.isResult(&qd, &num)) {
|
|
qDebug() << "RecollProtocol::stat: isresult";
|
|
if (syncSearch(qd)) {
|
|
Rcl::Doc doc;
|
|
if (num >= 0 && m_source && m_source->getDoc(num, doc)) {
|
|
entry = resultToUDSEntry(doc, num);
|
|
} else {
|
|
error(ERR_DOES_NOT_EXIST, QString());
|
|
}
|
|
} else {
|
|
// hopefully syncSearch() set the error?
|
|
}
|
|
} else if (ingest.isQuery(&qd)) {
|
|
qDebug() << "RecollProtocol::stat: isquery";
|
|
// 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 (true || m_alwaysdir || ingest.alwaysDir() || ingest.endSlashQuery()) {
|
|
qDebug() << "RecollProtocol::stat: Directory type:";
|
|
// Need to check no / in there
|
|
#if 0
|
|
entry.insert(KIO::UDSEntry::UDS_NAME, "dockes bla"/*qd.query*/);
|
|
entry.insert(KIO::UDSEntry::UDS_URL, "recoll:other query");
|
|
entry.insert(KIO::UDSEntry::UDS_DISPLAY_NAME, qd.query);
|
|
entry.insert(KIO::UDSEntry::UDS_ACCESS, 0777);
|
|
entry.insert(KIO::UDSEntry::UDS_SIZE, 20480);
|
|
entry.insert(KIO::UDSEntry::UDS_MODIFICATION_TIME, time(0));
|
|
entry.insert(KIO::UDSEntry::UDS_CREATION_TIME, time(0));
|
|
entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
|
|
entry.insert(KIO::UDSEntry::UDS_MIME_TYPE, "inode/directory");
|
|
#endif
|
|
}
|
|
} else {
|
|
qDebug() << "RecollProtocol::stat: none of the above ??";
|
|
}
|
|
statEntry(entry);
|
|
finished();
|
|
}
|
|
|
|
void RecollProtocol::listDir(const QUrl& url)
|
|
{
|
|
qDebug() << "RecollProtocol::listDir: url: " << url;
|
|
|
|
UrlIngester ingest(this, url);
|
|
UrlIngester::RootEntryType rettp;
|
|
QueryDesc qd;
|
|
|
|
if (ingest.isRootEntry(&rettp)) {
|
|
switch(rettp) {
|
|
case UrlIngester::UIRET_ROOT:
|
|
{
|
|
qDebug() << "RecollProtocol::listDir:list /";
|
|
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, QString());
|
|
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()) {
|
|
qDebug() << "RecollProtocol::listDir: Ends With /";
|
|
error(ERR_SLAVE_DEFINED, u8s2qs("Autocompletion search aborted"));
|
|
return;
|
|
}
|
|
if (!syncSearch(qd)) {
|
|
// syncSearch did the error thing
|
|
return;
|
|
}
|
|
// Fallthrough to actually listing the directory
|
|
} else {
|
|
qDebug() << "RecollProtocol::listDir: Cant grok input url";
|
|
error(ERR_CANNOT_ENTER_DIRECTORY, QString());
|
|
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<ResListEntry> page;
|
|
int pagelen = m_source->getSeqSlice(pagebase, pagesize, page);
|
|
UDSEntry entry;
|
|
if (pagelen < 0) {
|
|
error(ERR_SLAVE_DEFINED, u8s2qs("Internal error"));
|
|
break;
|
|
}
|
|
UDSEntryList entries;
|
|
for (int i = 0; i < pagelen; i++) {
|
|
entries.push_back(resultToUDSEntry(page[i].doc, i));
|
|
}
|
|
listEntries(entries);
|
|
if (pagelen != pagesize) {
|
|
break;
|
|
}
|
|
pagebase += pagelen;
|
|
}
|
|
finished();
|
|
}
|