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