/* 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")) { 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 numentries = -1; if (numentries == -1) { if (o_rclconfig) o_rclconfig->getConfParam("kio_max_direntries", &numentries); if (numentries == -1) numentries = 100; } vector page; int pagelen = m_source->getSeqSlice(0, numentries, page); if (pagelen < 0) { error(ERR_SLAVE_DEFINED, "Internal error"); return; } UDSEntryList entries; for (int i = 0; i < pagelen; i++) { entries.append(resultToUDSEntry(page[i].doc, i)); } listEntries(entries); finished(); } #else // <--- KDE 4.1+ #include #include "kio_recoll.h" bool RecollProtocol::isRecollResult(const KUrl &, int *, QString *) { return false; } #endif