1 /*
2  *  Copyright 2007-2021 Fabrice Colin
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18 
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <iostream>
22 #include <sstream>
23 #include <typeinfo>
24 #include <utility>
25 
26 #include "Languages.h"
27 #include "DBusIndex.h"
28 
29 using std::clog;
30 using std::clog;
31 using std::endl;
32 using std::string;
33 using std::stringstream;
34 using std::vector;
35 using std::set;
36 using std::map;
37 using std::min;
38 using std::pair;
39 using std::tuple;
40 
41 using namespace Gio;
42 using namespace Glib;
43 
44 static const char *g_fieldNames[] = { "caption", "url", "type", "language", "modtime", "size", "extract", "score", NULL };
45 
DBusIndex(IndexInterface * pROIndex)46 DBusIndex::DBusIndex(IndexInterface *pROIndex) :
47 	IndexInterface(),
48 	m_refProxy(com::github::fabricecolin::PinotProxy::createForBus_sync(
49 		DBus::BUS_TYPE_SESSION,
50 		Gio::DBus::PROXY_FLAGS_NONE,
51 		PINOT_DBUS_SERVICE_NAME, PINOT_DBUS_OBJECT_PATH)),
52 	m_pROIndex(pROIndex)
53 {
54 }
55 
DBusIndex(const DBusIndex & other)56 DBusIndex::DBusIndex(const DBusIndex &other) :
57 	IndexInterface(other),
58 	m_pROIndex(other.m_pROIndex)
59 {
60 }
61 
~DBusIndex()62 DBusIndex::~DBusIndex()
63 {
64 	if (m_pROIndex != NULL)
65 	{
66 		// Noone else is going to delete this
67 		delete m_pROIndex;
68 	}
69 }
70 
operator =(const DBusIndex & other)71 DBusIndex &DBusIndex::operator=(const DBusIndex &other)
72 {
73 	if (this != &other)
74 	{
75 		IndexInterface::operator=(other);
76 		m_pROIndex = other.m_pROIndex;
77 	}
78 
79 	return *this;
80 }
81 
82 /// Extracts docInfo from tuples.
documentInfoFromTuples(const vector<tuple<ustring,ustring>> & tuples,DocumentInfo & docInfo)83 void DBusIndex::documentInfoFromTuples(const vector<tuple<ustring, ustring>> &tuples,
84 	DocumentInfo &docInfo)
85 {
86 	for (vector<tuple<ustring, ustring>>::const_iterator tupleIter = tuples.begin();
87 		tupleIter != tuples.end(); ++tupleIter)
88 	{
89 		ustring fieldName, fieldValue;
90 
91 		std::tie(fieldName, fieldValue) = *tupleIter;
92 
93 		// Populate docInfo
94 		if (fieldName == g_fieldNames[0])
95 		{
96 			docInfo.setTitle(fieldValue.c_str());
97 		}
98 		else if (fieldName == g_fieldNames[1])
99 		{
100 			docInfo.setLocation(fieldValue.c_str());
101 		}
102 		else if (fieldName == g_fieldNames[2])
103 		{
104 			docInfo.setType(fieldValue.c_str());
105 		}
106 		else if (fieldName == g_fieldNames[3])
107 		{
108 			docInfo.setLanguage(Languages::toLocale(fieldValue.c_str()));
109 		}
110 		else if (fieldName == g_fieldNames[4])
111 		{
112 			docInfo.setTimestamp(fieldValue.c_str());
113 		}
114 		else if (fieldName == g_fieldNames[5])
115 		{
116 			docInfo.setSize((off_t )atoi(fieldValue.c_str()));
117 		}
118 		else if (fieldName == g_fieldNames[6])
119 		{
120 			docInfo.setExtract(fieldValue.c_str());
121 		}
122 		else if (fieldName == g_fieldNames[7])
123 		{
124 			docInfo.setScore((float)atof(fieldValue.c_str()));
125 		}
126 	}
127 }
128 
129 /// Converts docInfo to tuples.
documentInfoToTuples(const DocumentInfo & docInfo,vector<tuple<ustring,ustring>> & tuples)130 void DBusIndex::documentInfoToTuples(const DocumentInfo &docInfo,
131 	vector<tuple<ustring, ustring>> &tuples)
132 {
133 	for (unsigned int fieldNum = 0; g_fieldNames[fieldNum] != NULL; ++fieldNum)
134 	{
135 		string value;
136 		stringstream numStr;
137 
138 		switch (fieldNum)
139 		{
140 			case 0:
141 				value = docInfo.getTitle();
142 				break;
143 			case 1:
144 				value = docInfo.getLocation(true);
145 				break;
146 			case 2:
147 				value = docInfo.getType();
148 				break;
149 			case 3:
150 				value = Languages::toEnglish(docInfo.getLanguage());
151 				break;
152 			case 4:
153 				value = docInfo.getTimestamp();
154 				break;
155 			case 5:
156 				numStr << docInfo.getSize();
157 				value = numStr.str();
158 				break;
159 			case 6:
160 				value = docInfo.getExtract();
161 				break;
162 			case 7:
163 				numStr << docInfo.getScore();
164 				value = numStr.str();
165 				break;
166 			default:
167 				break;
168 		}
169 
170 		if (value.empty() == true)
171 		{
172 			continue;
173 		}
174 
175 		tuples.push_back(make_tuple(g_fieldNames[fieldNum], value));
176 	}
177 }
178 
179 /// Asks the D-Bus service to reload its configuration.
reload(void)180 bool DBusIndex::reload(void)
181 {
182 	try
183 	{
184 		return m_refProxy->Reload_sync();
185 	}
186 	catch (const Glib::Error &ex)
187 	{
188 		clog << "DBusIndex::reload: " << ex.what() << endl;
189 	}
190 
191 	return false;
192 }
193 
194 /// Gets some statistics from the D-Bus service.
getStatistics(unsigned int & crawledCount,unsigned int & docsCount,bool & lowDiskSpace,bool & onBattery,bool & crawling)195 bool DBusIndex::getStatistics(unsigned int &crawledCount, unsigned int &docsCount,
196 	bool &lowDiskSpace, bool &onBattery, bool &crawling)
197 {
198 	try
199 	{
200 		std::tie(crawledCount, docsCount, lowDiskSpace, onBattery, crawling) = m_refProxy->GetStatistics_sync();
201 	}
202 	catch (const Glib::Error &ex)
203 	{
204 		clog << "DBusIndex::getStatistics: " << ex.what() << endl;
205 	}
206 
207 	return true;
208 }
209 
210 //
211 // Implementation of IndexInterface
212 //
213 
214 /// Returns false if the index couldn't be opened.
isGood(void) const215 bool DBusIndex::isGood(void) const
216 {
217 	if (m_pROIndex == NULL)
218 	{
219 		return false;
220 	}
221 
222 	return m_pROIndex->isGood();
223 }
224 
225 /// Gets metadata.
getMetadata(const string & name) const226 string DBusIndex::getMetadata(const string &name) const
227 {
228 	if (m_pROIndex == NULL)
229 	{
230 		return "";
231 	}
232 
233 	return m_pROIndex->getMetadata(name);
234 }
235 
236 /// Sets metadata.
setMetadata(const string & name,const string & value) const237 bool DBusIndex::setMetadata(const string &name, const string &value) const
238 {
239 	clog << "DBusIndex::setMetadata: not allowed" << endl;
240 	return false;
241 }
242 
243 /// Gets the index location.
getLocation(void) const244 string DBusIndex::getLocation(void) const
245 {
246 	if (m_pROIndex == NULL)
247 	{
248 		return "";
249 	}
250 
251 	return m_pROIndex->getLocation();
252 }
253 
254 /// Returns a document's properties.
getDocumentInfo(unsigned int docId,DocumentInfo & docInfo) const255 bool DBusIndex::getDocumentInfo(unsigned int docId, DocumentInfo &docInfo) const
256 {
257 	vector<tuple<ustring, ustring>> tuples;
258 
259 	try
260 	{
261 		tuples = m_refProxy->GetDocumentInfo_sync(docId);
262 	}
263 	catch (const Glib::Error &ex)
264 	{
265 		clog << "DBusIndex::getDocumentInfo: " << ex.what() << endl;
266 	}
267 
268 	if (tuples.empty() == true)
269 	{
270 		return false;
271 	}
272 
273 	documentInfoFromTuples(tuples, docInfo);
274 
275 	return true;
276 }
277 
278 /// Returns a document's terms count.
getDocumentTermsCount(unsigned int docId) const279 unsigned int DBusIndex::getDocumentTermsCount(unsigned int docId) const
280 {
281 	unsigned int termsCount = 0;
282 
283 	try
284 	{
285 		termsCount = m_refProxy->GetDocumentTermsCount_sync(docId);
286 	}
287 	catch (const Glib::Error &ex)
288 	{
289 		clog << "DBusIndex::getDocumentTermsCount: " << ex.what() << endl;
290 	}
291 
292 	return termsCount;
293 }
294 
295 /// Returns a document's terms.
getDocumentTerms(unsigned int docId,map<unsigned int,string> & wordsBuffer) const296 bool DBusIndex::getDocumentTerms(unsigned int docId,
297 	map<unsigned int, string> &wordsBuffer) const
298 {
299 	vector<ustring> termsList;
300 
301 	try
302 	{
303 		termsList = m_refProxy->GetDocumentTerms_sync(docId);
304 	}
305 	catch (const Glib::Error &ex)
306 	{
307 		clog << "DBusIndex::getDocumentTerms: " << ex.what() << endl;
308 	}
309 
310 	if (termsList.empty() == true)
311 	{
312 		return false;
313 	}
314 
315 	unsigned int termPos = 0;
316 
317 	for (vector<ustring>::const_iterator termIter = termsList.begin();
318 		termIter != termsList.end(); ++termIter)
319 	{
320 		wordsBuffer.insert(pair<unsigned int, string>(termPos, termIter->c_str()));
321 		++termPos;
322 	}
323 
324 	return true;
325 }
326 
327 /// Sets the list of known labels.
setLabels(const set<string> & labels,bool resetLabels)328 bool DBusIndex::setLabels(const set<string> &labels, bool resetLabels)
329 {
330 	// Not allowed here
331 	return false;
332 }
333 
334 /// Gets the list of known labels.
getLabels(set<string> & labels) const335 bool DBusIndex::getLabels(set<string> &labels) const
336 {
337 	vector<ustring> labelsList;
338 
339 	try
340 	{
341 		labelsList = m_refProxy->GetLabels_sync();
342 	}
343 	catch (const Glib::Error &ex)
344 	{
345 		clog << "DBusIndex::getLabels: " << ex.what() << endl;
346 	}
347 
348 	for (vector<ustring>::const_iterator labelIter = labelsList.begin();
349 		labelIter != labelsList.end(); ++labelIter)
350 	{
351 		labels.insert(labelIter->c_str());
352 	}
353 
354 	return true;
355 }
356 
357 /// Adds a label.
addLabel(const string & name)358 bool DBusIndex::addLabel(const string &name)
359 {
360 	ustring labelName(name.c_str());
361 	ustring newLabelName;
362 
363 	try
364 	{
365 		newLabelName = m_refProxy->AddLabel_sync(labelName);
366 	}
367 	catch (const Glib::Error &ex)
368 	{
369 		clog << "DBusIndex::addLabel: " << ex.what() << endl;
370 	}
371 
372 	return (newLabelName == labelName);
373 }
374 
375 /// Deletes all references to a label.
deleteLabel(const string & name)376 bool DBusIndex::deleteLabel(const string &name)
377 {
378 	ustring labelName(name.c_str());
379 	ustring deletedLabelName;
380 
381 	try
382 	{
383 		deletedLabelName = m_refProxy->DeleteLabel_sync(labelName);
384 	}
385 	catch (const Glib::Error &ex)
386 	{
387 		clog << "DBusIndex::deleteLabel: " << ex.what() << endl;
388 	}
389 
390 	return (deletedLabelName == labelName);
391 }
392 
393 /// Determines whether a document has a label.
hasLabel(unsigned int docId,const string & name) const394 bool DBusIndex::hasLabel(unsigned int docId, const string &name) const
395 {
396 	unsigned int foundDocId = 0;
397 
398 	try
399 	{
400 		foundDocId = m_refProxy->HasLabel_sync(docId, name.c_str());
401 	}
402 	catch (const Glib::Error &ex)
403 	{
404 		clog << "DBusIndex::hasDocument: " << ex.what() << endl;
405 	}
406 
407 	return (foundDocId == docId);
408 }
409 
410 /// Returns a document's labels.
getDocumentLabels(unsigned int docId,set<string> & labels) const411 bool DBusIndex::getDocumentLabels(unsigned int docId, set<string> &labels) const
412 {
413 	vector<ustring> labelsList;
414 
415 	try
416 	{
417 		labelsList = m_refProxy->GetDocumentLabels_sync(docId);
418 	}
419 	catch (const Glib::Error &ex)
420 	{
421 		clog << "DBusIndex::getDocumentLabels: " << ex.what() << endl;
422 	}
423 
424 	for (vector<ustring>::const_iterator labelIter = labelsList.begin();
425 		labelIter != labelsList.end(); ++labelIter)
426 	{
427 		labels.insert(labelIter->c_str());
428 	}
429 
430 	return true;
431 }
432 
433 /// Sets a document's labels.
setDocumentLabels(unsigned int docId,const set<string> & labels,bool resetLabels)434 bool DBusIndex::setDocumentLabels(unsigned int docId, const set<string> &labels,
435 	bool resetLabels)
436 {
437 	vector<ustring> labelsList;
438 
439 	labelsList.reserve(labels.size());
440 	for (set<string>::const_iterator labelIter = labels.begin();
441 		labelIter != labels.end(); ++labelIter)
442 	{
443 		labelsList.push_back(labelIter->c_str());
444 	}
445 	try
446 	{
447 		m_refProxy->SetDocumentLabels_sync(docId, labelsList, resetLabels);
448 	}
449 	catch (const Glib::Error &ex)
450 	{
451 		clog << "DBusIndex::setDocumentLabels: " << ex.what() << endl;
452 	}
453 
454 	return true;
455 }
456 
457 /// Sets documents' labels.
setDocumentsLabels(const set<unsigned int> & docIds,const set<string> & labels,bool resetLabels)458 bool DBusIndex::setDocumentsLabels(const set<unsigned int> &docIds,
459 	const set<string> &labels, bool resetLabels)
460 {
461 	vector<ustring> idsList;
462 	vector<ustring> labelsList;
463 
464 	idsList.reserve(docIds.size());
465 	for (set<unsigned int>::const_iterator idIter = docIds.begin();
466 		idIter != docIds.end(); ++idIter)
467 	{
468 		stringstream numStr;
469 
470 		numStr << *idIter;
471 		idsList.push_back(numStr.str().c_str());
472 	}
473 	labelsList.reserve(labels.size());
474 	for (set<string>::const_iterator labelIter = labels.begin();
475 		labelIter != labels.end(); ++labelIter)
476 	{
477 		labelsList.push_back(labelIter->c_str());
478 	}
479 
480 	try
481 	{
482 		return m_refProxy->SetDocumentsLabels_sync(idsList, labelsList, resetLabels);
483 	}
484 	catch (const Glib::Error &ex)
485 	{
486 		clog << "DBusIndex::setDocumentsLabels: " << ex.what() << endl;
487 	}
488 
489 	return false;
490 }
491 
492 /// Checks whether the given URL is in the index.
hasDocument(const string & url) const493 unsigned int DBusIndex::hasDocument(const string &url) const
494 {
495 	try
496 	{
497 		return m_refProxy->HasDocument_sync(url.c_str());
498 	}
499 	catch (const Glib::Error &ex)
500 	{
501 		clog << "DBusIndex::hasDocument: " << ex.what() << endl;
502 	}
503 
504 	return 0;
505 }
506 
507 /// Gets terms with the same root.
getCloseTerms(const string & term,set<string> & suggestions)508 unsigned int DBusIndex::getCloseTerms(const string &term, set<string> &suggestions)
509 {
510 	vector<ustring> termsList;
511 
512 	try
513 	{
514 		termsList = m_refProxy->GetCloseTerms_sync(term.c_str());
515 	}
516 	catch (const Glib::Error &ex)
517 	{
518 		clog << "DBusIndex::getCloseTerms: " << ex.what() << endl;
519 	}
520 
521 	if (termsList.empty() == true)
522 	{
523 		return 0;
524 	}
525 
526 	for (vector<ustring>::const_iterator termIter = termsList.begin();
527 		termIter != termsList.end(); ++termIter)
528 	{
529 		suggestions.insert(termIter->c_str());
530 	}
531 
532 	return termsList.size();
533 }
534 
535 /// Returns the ID of the last document.
getLastDocumentID(void) const536 unsigned int DBusIndex::getLastDocumentID(void) const
537 {
538 	return 0;
539 }
540 
541 /// Returns the number of documents.
getDocumentsCount(const string & labelName) const542 unsigned int DBusIndex::getDocumentsCount(const string &labelName) const
543 {
544 	unsigned int docsCount = 0;
545 
546 	try
547 	{
548 		docsCount = m_refProxy->GetDocumentsCount_sync(labelName);
549 	}
550 	catch (const Glib::Error &ex)
551 	{
552 		clog << "DBusIndex::getDocumentsCount: " << ex.what() << endl;
553 	}
554 
555 	return docsCount;
556 }
557 
558 /// Lists documents.
listDocuments(set<unsigned int> & docIds,unsigned int maxDocsCount,unsigned int startDoc) const559 unsigned int DBusIndex::listDocuments(set<unsigned int> &docIds,
560 	unsigned int maxDocsCount, unsigned int startDoc) const
561 {
562 	vector<ustring> docIdsList;
563 
564 	try
565 	{
566 		docIdsList = m_refProxy->ListDocuments_sync("",
567 			0, maxDocsCount, startDoc);
568 	}
569 	catch (const Glib::Error &ex)
570 	{
571 		clog << "DBusIndex::listDocuments: " << ex.what() << endl;
572 	}
573 
574 	for (vector<ustring>::const_iterator docIter = docIdsList.begin();
575 		docIter != docIdsList.end(); ++docIter)
576 	{
577 		docIds.insert((unsigned int)atoi(docIter->c_str()));
578 	}
579 
580 	return docIdsList.size();
581 }
582 
583 /// Lists documents.
listDocuments(const string & name,set<unsigned int> & docIds,NameType type,unsigned int maxDocsCount,unsigned int startDoc) const584 bool DBusIndex::listDocuments(const string &name, set<unsigned int> &docIds,
585 	NameType type, unsigned int maxDocsCount, unsigned int startDoc) const
586 {
587 	vector<ustring> docIdsList;
588 
589 	try
590 	{
591 		docIdsList = m_refProxy->ListDocuments_sync(name.c_str(),
592 			(unsigned int)type, maxDocsCount, startDoc);
593 	}
594 	catch (const Glib::Error &ex)
595 	{
596 		clog << "DBusIndex::listDocuments: " << ex.what() << endl;
597 	}
598 
599 	for (vector<ustring>::const_iterator docIter = docIdsList.begin();
600 		docIter != docIdsList.end(); ++docIter)
601 	{
602 		docIds.insert((unsigned int)atoi(docIter->c_str()));
603 	}
604 
605 	return !docIdsList.empty();
606 }
607 
608 /// Indexes the given data.
indexDocument(const Document & doc,const set<string> & labels,unsigned int & docId)609 bool DBusIndex::indexDocument(const Document &doc, const set<string> &labels,
610 	unsigned int &docId)
611 {
612 	clog << "DBusIndex::indexDocument: not allowed" << endl;
613 	return false;
614 }
615 
616 /// Updates the given document; true if success.
updateDocument(unsigned int docId,const Document & doc)617 bool DBusIndex::updateDocument(unsigned int docId, const Document &doc)
618 {
619 	unsigned updatedDocId = 0;
620 
621 	try
622 	{
623 		updatedDocId = m_refProxy->UpdateDocument_sync(docId);
624 	}
625 	catch (const Glib::Error &ex)
626 	{
627 		clog << "DBusIndex::updateDocument: " << ex.what() << endl;
628 	}
629 
630 	return (updatedDocId == docId);
631 }
632 
633 /// Updates a document's properties.
updateDocumentInfo(unsigned int docId,const DocumentInfo & docInfo)634 bool DBusIndex::updateDocumentInfo(unsigned int docId, const DocumentInfo &docInfo)
635 {
636 	vector<tuple<ustring, ustring>> tuples;
637 	unsigned updatedDocId = 0;
638 
639 	documentInfoToTuples(docInfo, tuples);
640 
641 	try
642 	{
643 		updatedDocId = m_refProxy->SetDocumentInfo_sync(docId, tuples);
644 	}
645 	catch (const Glib::Error &ex)
646 	{
647 		clog << "DBusIndex::updateDocument: " << ex.what() << endl;
648 	}
649 
650 	return (updatedDocId == docId);
651 }
652 
653 /// Unindexes the given document; true if success.
unindexDocument(unsigned int docId)654 bool DBusIndex::unindexDocument(unsigned int docId)
655 {
656 	clog << "DBusIndex::unindexDocument: not allowed" << endl;
657 	return false;
658 }
659 
660 /// Unindexes the given document.
unindexDocument(const string & location)661 bool DBusIndex::unindexDocument(const string &location)
662 {
663 	clog << "DBusIndex::unindexDocument: not allowed" << endl;
664 	return false;
665 }
666 
667 /// Unindexes documents.
unindexDocuments(const string & name,NameType type)668 bool DBusIndex::unindexDocuments(const string &name, NameType type)
669 {
670 	clog << "DBusIndex::unindexDocuments: not allowed" << endl;
671 	return false;
672 }
673 
674 /// Unindexes all documents.
unindexAllDocuments(void)675 bool DBusIndex::unindexAllDocuments(void)
676 {
677 	clog << "DBusIndex::unindexDocuments: not allowed" << endl;
678 	return false;
679 }
680 
681 /// Flushes recent changes to the disk.
flush(void)682 bool DBusIndex::flush(void)
683 {
684 	// The daemon knows best when to flush
685 	return true;
686 }
687 
688 /// Reopens the index.
reopen(void) const689 bool DBusIndex::reopen(void) const
690 {
691 	return true;
692 }
693 
694 /// Resets the index.
reset(void)695 bool DBusIndex::reset(void)
696 {
697 	// This can't be done here
698 	return false;
699 }
700 
701