Fix crash if key bindings specified in profile cannot be found. Improve
[personal-kdebase.git] / apps / kfind / kquery.cpp
blobf7f11eb0d4d76528ccf58b454371b0f1aa09f033
1 #include "kquery.h"
3 #include <stdlib.h>
5 #include <QtCore/QFileInfo>
6 #include <QtCore/QTextStream>
7 #include <QtCore/QList>
8 #include <kdebug.h>
9 #include <kfileitem.h>
10 #include <kfilemetainfo.h>
11 #include <kapplication.h>
12 #include <kmessagebox.h>
13 #include <klocale.h>
14 #include <kstandarddirs.h>
15 #include <kzip.h>
17 KQuery::KQuery(QObject *parent)
18 : QObject(parent),
19 m_sizemode(0), m_sizeboundary1(0), m_sizeboundary2(0),
20 m_timeFrom(0), m_timeTo(0),
21 job(0), m_insideCheckEntries(false), m_result(0)
23 processLocate = new KProcess(this);
24 connect(processLocate,SIGNAL(readyReadStandardOutput()),this,SLOT(slotreadyReadStandardOutput()));
25 connect(processLocate,SIGNAL(readyReadStandardError()),this,SLOT(slotreadyReadStandardError()));
26 connect(processLocate,SIGNAL(finished(int, QProcess::ExitStatus)),this,SLOT(slotendProcessLocate(int, QProcess::ExitStatus)));
28 // Files with these mime types can be ignored, even if
29 // findFormatByFileContent() in some cases may claim that
30 // these are text files:
31 ignore_mimetypes.append("application/pdf");
32 ignore_mimetypes.append("application/postscript");
34 // PLEASE update the documentation when you add another
35 // file type here:
36 ooo_mimetypes.append("application/vnd.sun.xml.writer");
37 ooo_mimetypes.append("application/vnd.sun.xml.calc");
38 ooo_mimetypes.append("application/vnd.sun.xml.impress");
39 // OASIS mimetypes, used by OOo-2.x and KOffice >= 1.4
40 //ooo_mimetypes.append("application/vnd.oasis.opendocument.chart");
41 //ooo_mimetypes.append("application/vnd.oasis.opendocument.graphics");
42 //ooo_mimetypes.append("application/vnd.oasis.opendocument.graphics-template");
43 //ooo_mimetypes.append("application/vnd.oasis.opendocument.formula");
44 //ooo_mimetypes.append("application/vnd.oasis.opendocument.image");
45 ooo_mimetypes.append("application/vnd.oasis.opendocument.presentation-template");
46 ooo_mimetypes.append("application/vnd.oasis.opendocument.presentation");
47 ooo_mimetypes.append("application/vnd.oasis.opendocument.spreadsheet-template");
48 ooo_mimetypes.append("application/vnd.oasis.opendocument.spreadsheet");
49 ooo_mimetypes.append("application/vnd.oasis.opendocument.text-template");
50 ooo_mimetypes.append("application/vnd.oasis.opendocument.text");
51 // KOffice-1.3 mimetypes
52 koffice_mimetypes.append("application/x-kword");
53 koffice_mimetypes.append("application/x-kspread");
54 koffice_mimetypes.append("application/x-kpresenter");
57 KQuery::~KQuery()
59 while (!m_regexps.isEmpty())
60 delete m_regexps.takeFirst();
61 while (!m_fileItems.isEmpty())
62 m_fileItems.dequeue();
65 void KQuery::kill()
67 if (job)
68 job->kill(KJob::EmitResult);
69 if (processLocate->state() == QProcess::Running)
70 processLocate->kill();
71 while (!m_fileItems.isEmpty())
72 m_fileItems.dequeue();
75 void KQuery::start()
77 while (!m_fileItems.isEmpty())
78 m_fileItems.dequeue();
79 if(m_useLocate) //use "locate" instead of the internal search method
81 m_url.cleanPath();
82 processLocate->clearProgram();
83 *processLocate << "locate";
84 *processLocate << m_url.path( KUrl::AddTrailingSlash ).toLatin1();
85 bufferLocate.clear();
86 processLocate->setNextOpenMode(QIODevice::Text);
87 processLocate->setOutputChannelMode(KProcess::SeparateChannels);
88 processLocate->start();
89 return;
92 if (m_recursive)
93 job = KIO::listRecursive( m_url, KIO::HideProgressInfo );
94 else
95 job = KIO::listDir( m_url, KIO::HideProgressInfo );
97 connect(job, SIGNAL(entries(KIO::Job *, const KIO::UDSEntryList &)),
98 SLOT(slotListEntries(KIO::Job *, const KIO::UDSEntryList &)));
99 connect(job, SIGNAL(result(KJob *)), SLOT(slotResult(KJob *)));
100 connect(job, SIGNAL(canceled(KJob *)), SLOT(slotCanceled(KJob *)));
103 void KQuery::slotResult( KJob * _job )
105 if (job != _job) return;
106 job = 0;
108 m_result=_job->error();
109 checkEntries();
112 void KQuery::slotCanceled( KJob * _job )
114 if (job != _job) return;
115 job = 0;
117 while (!m_fileItems.isEmpty())
118 m_fileItems.dequeue();
120 m_result=KIO::ERR_USER_CANCELED;
121 checkEntries();
124 void KQuery::slotListEntries(KIO::Job*, const KIO::UDSEntryList& list)
126 const KIO::UDSEntryList::ConstIterator end = list.constEnd();
127 for (KIO::UDSEntryList::ConstIterator it = list.constBegin(); it != end; ++it)
129 m_fileItems.enqueue(KFileItem(*it, m_url, true, true));
131 checkEntries();
134 void KQuery::checkEntries()
136 if (m_insideCheckEntries)
137 return;
138 m_insideCheckEntries=true;
139 metaKeyRx=new QRegExp(m_metainfokey);
140 metaKeyRx->setPatternSyntax( QRegExp::Wildcard );
141 while ( !m_fileItems.isEmpty() )
143 processQuery( m_fileItems.dequeue() );
146 delete metaKeyRx;
147 m_insideCheckEntries=false;
148 if (job==0)
149 emit result(m_result);
152 /* List of files found using slocate */
153 void KQuery::slotListEntries( QStringList list )
155 metaKeyRx=new QRegExp(m_metainfokey);
156 metaKeyRx->setPatternSyntax( QRegExp::Wildcard );
158 QStringList::const_iterator it = list.constBegin();
159 QStringList::const_iterator end = list.constEnd();
161 for (; it != end; ++it)
163 processQuery( KFileItem( KFileItem::Unknown, KFileItem::Unknown, KUrl(*it)) );
166 delete metaKeyRx;
169 /* Check if file meets the find's requirements*/
170 void KQuery::processQuery( const KFileItem &file)
173 if ( file.name() == "." || file.name() == ".." )
174 return;
176 bool matched=false;
178 QListIterator<QRegExp *> nextItem( m_regexps );
179 while ( nextItem.hasNext() )
181 QRegExp *reg = nextItem.next();
182 matched = matched || ( reg == 0L ) || ( reg->exactMatch( file.url().fileName( KUrl::IgnoreTrailingSlash ) ) ) ;
185 if (!matched)
186 return;
188 // make sure the files are in the correct range
189 switch( m_sizemode )
191 case 1: // "at least"
192 if ( file.size() < m_sizeboundary1 ) return;
193 break;
194 case 2: // "at most"
195 if ( file.size() > m_sizeboundary1 ) return;
196 break;
197 case 3: // "equal"
198 if ( file.size() != m_sizeboundary1 ) return;
199 break;
200 case 4: // "between"
201 if ( (file.size() < m_sizeboundary1) ||
202 (file.size() > m_sizeboundary2) ) return;
203 break;
204 case 0: // "none" -> Fall to default
205 default:
206 break;
209 // make sure it's in the correct date range
210 // what about 0 times?
211 if ( m_timeFrom && m_timeFrom > file.time(KFileItem::ModificationTime).toTime_t() )
212 return;
213 if ( m_timeTo && m_timeTo < file.time(KFileItem::ModificationTime).toTime_t() )
214 return;
216 // username / group match
217 if ( (!m_username.isEmpty()) && (m_username != file.user()) )
218 return;
219 if ( (!m_groupname.isEmpty()) && (m_groupname != file.group()) )
220 return;
222 // file type
223 switch (m_filetype)
225 case 0:
226 break;
227 case 1: // plain file
228 if ( !S_ISREG( file.mode() ) )
229 return;
230 break;
231 case 2:
232 if ( !file.isDir() )
233 return;
234 break;
235 case 3:
236 if ( !file.isLink() )
237 return;
238 break;
239 case 4:
240 if ( !S_ISCHR ( file.mode() ) && !S_ISBLK ( file.mode() ) &&
241 !S_ISFIFO( file.mode() ) && !S_ISSOCK( file.mode() ) )
242 return;
243 break;
244 case 5: // binary
245 if ( (file.permissions() & 0111) != 0111 || file.isDir() )
246 return;
247 break;
248 case 6: // suid
249 if ( (file.permissions() & 04000) != 04000 ) // fixme
250 return;
251 break;
252 default:
253 if (!m_mimetype.isEmpty() && !m_mimetype.contains(file.mimetype()))
254 return;
257 // match data in metainfo...
258 if ((!m_metainfo.isEmpty()) && (!m_metainfokey.isEmpty()))
260 bool foundmeta=false;
261 QString filename = file.url().path();
263 if(filename.startsWith("/dev/"))
264 return;
266 KFileMetaInfo metadatas(filename);
267 QStringList metakeys;
268 QString strmetakeycontent;
270 metakeys = metadatas.supportedKeys();
271 for (QStringList::const_iterator it = metakeys.constBegin(); it != metakeys.constEnd(); ++it )
273 if (!metaKeyRx->exactMatch(*it))
274 continue;
275 strmetakeycontent=metadatas.item(*it).value().toString();
276 if(strmetakeycontent.indexOf(m_metainfo)!=-1)
278 foundmeta=true;
279 break;
282 if (!foundmeta)
283 return;
286 // match contents...
287 QString matchingLine;
288 if (!m_context.isEmpty())
291 if( !m_search_binary && ignore_mimetypes.indexOf(file.mimetype()) != -1 ) {
292 kDebug() << "ignoring, mime type is in exclusion list: " << file.url();
293 return;
296 bool found = false;
297 bool isZippedOfficeDocument=false;
298 int matchingLineNumber=0;
300 // FIXME: doesn't work with non local files
302 QString filename;
303 QTextStream* stream=0;
304 QFile qf;
305 QRegExp xmlTags;
306 QByteArray zippedXmlFileContent;
308 // KWord's and OpenOffice.org's files are zipped...
309 if( ooo_mimetypes.indexOf(file.mimetype()) != -1 ||
310 koffice_mimetypes.indexOf(file.mimetype()) != -1 )
312 KZip zipfile(file.url().path());
313 KZipFileEntry *zipfileEntry;
315 if(zipfile.open(QIODevice::ReadOnly))
317 const KArchiveDirectory *zipfileContent = zipfile.directory();
319 if( koffice_mimetypes.indexOf(file.mimetype()) != -1 )
320 zipfileEntry = (KZipFileEntry*)zipfileContent->entry("maindoc.xml");
321 else
322 zipfileEntry = (KZipFileEntry*)zipfileContent->entry("content.xml"); //for OpenOffice.org
324 if(!zipfileEntry) {
325 kWarning() << "Expected XML file not found in ZIP archive " << file.url() ;
326 return;
329 zippedXmlFileContent = zipfileEntry->data();
330 xmlTags.setPattern("<.*>");
331 xmlTags.setMinimal(true);
332 stream = new QTextStream(zippedXmlFileContent, QIODevice::ReadOnly);
333 stream->setCodec("UTF-8");
334 isZippedOfficeDocument = true;
335 } else {
336 kWarning() << "Cannot open supposed ZIP file " << file.url() ;
338 } else if( !m_search_binary && !file.mimetype().startsWith("text/") &&
339 file.url().isLocalFile() ) {
340 if ( KMimeType::isBinaryData(file.url().path()) ) {
341 kDebug() << "ignoring, not a text file: " << file.url();
342 return;
346 if(!isZippedOfficeDocument) //any other file or non-compressed KWord
348 filename = file.url().path();
349 if(filename.startsWith("/dev/"))
350 return;
351 qf.setFileName(filename);
352 qf.open(QIODevice::ReadOnly);
353 stream=new QTextStream(&qf);
354 stream->setCodec(QTextCodec::codecForLocale());
357 while ( ! stream->atEnd() )
359 QString str = stream->readLine();
360 matchingLineNumber++;
362 if (str.isNull()) break;
363 if(isZippedOfficeDocument)
364 str.replace(xmlTags, "");
366 if (m_regexpForContent)
368 if (m_regexp.indexIn(str)>=0)
370 matchingLine=QString::number(matchingLineNumber)+": "+str;
371 found = true;
372 break;
375 else
377 if (str.indexOf(m_context, 0, m_casesensitive?Qt::CaseSensitive:Qt::CaseInsensitive) != -1)
379 matchingLine=QString::number(matchingLineNumber)+": "+str;
380 found = true;
381 break;
384 kapp->processEvents();
386 delete stream;
388 if (!found)
389 return;
391 emit addFile(file,matchingLine);
394 void KQuery::setContext(const QString & context, bool casesensitive,
395 bool search_binary, bool useRegexp)
397 m_context = context;
398 m_casesensitive = casesensitive;
399 m_search_binary = search_binary;
400 m_regexpForContent=useRegexp;
401 if ( !m_regexpForContent )
402 m_regexp.setPatternSyntax(QRegExp::Wildcard);
403 else
404 m_regexp.setPatternSyntax(QRegExp::RegExp);
405 if ( casesensitive )
406 m_regexp.setCaseSensitivity(Qt::CaseSensitive);
407 else
408 m_regexp.setCaseSensitivity(Qt::CaseInsensitive);
409 if (m_regexpForContent)
410 m_regexp.setPattern(m_context);
413 void KQuery::setMetaInfo(const QString &metainfo, const QString &metainfokey)
415 m_metainfo=metainfo;
416 m_metainfokey=metainfokey;
419 void KQuery::setMimeType(const QStringList &mimetype)
421 m_mimetype = mimetype;
424 void KQuery::setFileType(int filetype)
426 m_filetype = filetype;
429 void KQuery::setSizeRange(int mode, KIO::filesize_t value1, KIO::filesize_t value2)
431 m_sizemode = mode;
432 m_sizeboundary1 = value1;
433 m_sizeboundary2 = value2;
436 void KQuery::setTimeRange(time_t from, time_t to)
438 m_timeFrom = from;
439 m_timeTo = to;
442 void KQuery::setUsername(const QString &username)
444 m_username = username;
447 void KQuery::setGroupname(const QString &groupname)
449 m_groupname = groupname;
452 void KQuery::setRegExp(const QString &regexp, bool caseSensitive)
454 QRegExp *regExp;
455 QRegExp sep(";");
456 const QStringList strList=regexp.split( sep, QString::SkipEmptyParts);
457 // QRegExp globChars ("[\\*\\?\\[\\]]", TRUE, FALSE);
458 while (!m_regexps.isEmpty())
459 delete m_regexps.takeFirst();
461 // m_regexpsContainsGlobs.clear();
462 for ( QStringList::ConstIterator it = strList.constBegin(); it != strList.constEnd(); ++it ) {
463 regExp = new QRegExp((*it),( caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive ), QRegExp::Wildcard);
464 // m_regexpsContainsGlobs.append(regExp->pattern().contains(globChars));
465 m_regexps.append(regExp);
469 void KQuery::setRecursive(bool recursive)
471 m_recursive = recursive;
474 void KQuery::setPath(const KUrl &url)
476 m_url = url;
479 void KQuery::setUseFileIndex(bool useLocate)
481 m_useLocate=useLocate;
484 void KQuery::slotreadyReadStandardError()
486 KMessageBox::error(NULL, QString::fromLocal8Bit(processLocate->readAllStandardOutput()), i18n("Error while using locate"));
489 void KQuery::slotreadyReadStandardOutput()
491 bufferLocate += processLocate->readAllStandardOutput();
494 void KQuery::slotendProcessLocate(int, QProcess::ExitStatus)
496 if(bufferLocate.isEmpty())
498 emit result(0);
499 return;
502 QString str = QString::fromLocal8Bit(bufferLocate);
503 bufferLocate.clear();
504 slotListEntries(str.split('\n'));
505 emit result(0);
508 #include "kquery.moc"