File indexing completed on 2024-05-19 05:11:52

0001 /*
0002  * This file is part of the KDE Akonadi Search Project
0003  * SPDX-FileCopyrightText: 2013 Vishesh Handa <me@vhanda.in>
0004  *
0005  * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0006  *
0007  */
0008 
0009 #include <xapian.h>
0010 
0011 #include "akonadi_search_pim_debug.h"
0012 #include "contactquery.h"
0013 #include "resultiterator_p.h"
0014 
0015 #include <QFile>
0016 #include <QList>
0017 #include <QStandardPaths>
0018 
0019 using namespace Akonadi::Search::PIM;
0020 
0021 class Akonadi::Search::PIM::ContactQueryPrivate
0022 {
0023 public:
0024     QString name;
0025     QString nick;
0026     QString email;
0027     QString uid;
0028     QString any;
0029 
0030     int limit;
0031     ContactQuery::MatchCriteria criteria;
0032 };
0033 
0034 ContactQuery::ContactQuery()
0035     : Query()
0036     , d(new ContactQueryPrivate)
0037 {
0038     d->criteria = StartsWithMatch;
0039 }
0040 
0041 ContactQuery::~ContactQuery() = default;
0042 
0043 void ContactQuery::matchName(const QString &name)
0044 {
0045     d->name = name;
0046 }
0047 
0048 void ContactQuery::matchNickname(const QString &nick)
0049 {
0050     d->nick = nick;
0051 }
0052 
0053 void ContactQuery::matchEmail(const QString &email)
0054 {
0055     d->email = email;
0056 }
0057 
0058 void ContactQuery::matchUID(const QString &uid)
0059 {
0060     d->uid = uid;
0061 }
0062 
0063 void ContactQuery::match(const QString &str)
0064 {
0065     d->any = str;
0066 }
0067 
0068 int ContactQuery::limit() const
0069 {
0070     return d->limit;
0071 }
0072 
0073 void ContactQuery::setLimit(int limit)
0074 {
0075     d->limit = limit;
0076 }
0077 
0078 ContactQuery::MatchCriteria ContactQuery::matchCriteria() const
0079 {
0080     return d->criteria;
0081 }
0082 
0083 void ContactQuery::setMatchCriteria(ContactQuery::MatchCriteria m)
0084 {
0085     d->criteria = m;
0086 }
0087 
0088 ResultIterator ContactQuery::exec()
0089 {
0090     const QString dir = defaultLocation(QStringLiteral("contacts"));
0091     Xapian::Database db;
0092 
0093     try {
0094         db = Xapian::Database(QFile::encodeName(dir).toStdString());
0095     } catch (const Xapian::DatabaseOpeningError &) {
0096         qCWarning(AKONADI_SEARCH_PIM_LOG) << "Xapian Database does not exist at " << dir;
0097         return {};
0098     } catch (const Xapian::DatabaseCorruptError &) {
0099         qCWarning(AKONADI_SEARCH_PIM_LOG) << "Xapian Database corrupted";
0100         return {};
0101     } catch (const Xapian::DatabaseError &e) {
0102         qCWarning(AKONADI_SEARCH_PIM_LOG) << "Failed to open Xapian database:" << QString::fromStdString(e.get_error_string());
0103         return {};
0104     } catch (...) {
0105         qCWarning(AKONADI_SEARCH_PIM_LOG) << "Random exception, but we do not want to crash";
0106         return {};
0107     }
0108 
0109     QList<Xapian::Query> m_queries;
0110 
0111     if (d->criteria == ExactMatch) {
0112         if (!d->any.isEmpty()) {
0113             const QByteArray ba = d->any.toUtf8();
0114             m_queries << Xapian::Query(ba.constData());
0115         }
0116 
0117         if (!d->name.isEmpty()) {
0118             const QByteArray ba = "NA" + d->name.toUtf8();
0119             m_queries << Xapian::Query(ba.constData());
0120         }
0121 
0122         if (!d->nick.isEmpty()) {
0123             const QByteArray ba = "NI" + d->nick.toUtf8();
0124             m_queries << Xapian::Query(ba.constData());
0125         }
0126 
0127         if (!d->email.isEmpty()) {
0128             const QByteArray ba = d->email.toUtf8();
0129             m_queries << Xapian::Query(ba.constData());
0130         }
0131 
0132         if (!d->uid.isEmpty()) {
0133             const QByteArray ba = "UID" + d->uid.toUtf8();
0134             m_queries << Xapian::Query(ba.constData());
0135         }
0136     } else if (d->criteria == StartsWithMatch) {
0137         if (!d->any.isEmpty()) {
0138             Xapian::QueryParser parser;
0139             parser.set_database(db);
0140             const QByteArray ba = d->any.toUtf8();
0141             m_queries << parser.parse_query(ba.constData(), Xapian::QueryParser::FLAG_PARTIAL);
0142         }
0143 
0144         if (!d->name.isEmpty()) {
0145             Xapian::QueryParser parser;
0146             parser.set_database(db);
0147             parser.add_prefix("", "NA");
0148             const QByteArray ba = d->name.toUtf8();
0149             m_queries << parser.parse_query(ba.constData(), Xapian::QueryParser::FLAG_PARTIAL);
0150         }
0151 
0152         if (!d->nick.isEmpty()) {
0153             Xapian::QueryParser parser;
0154             parser.set_database(db);
0155             parser.add_prefix("", "NI");
0156             const QByteArray ba = d->nick.toUtf8();
0157             m_queries << parser.parse_query(ba.constData(), Xapian::QueryParser::FLAG_PARTIAL);
0158         }
0159 
0160         // FIXME: Check for exact match?
0161         if (!d->email.isEmpty()) {
0162             Xapian::QueryParser parser;
0163             parser.set_database(db);
0164             const QByteArray ba = d->email.toUtf8();
0165             m_queries << parser.parse_query(ba.constData(), Xapian::QueryParser::FLAG_PARTIAL);
0166         }
0167 
0168         if (!d->uid.isEmpty()) {
0169             Xapian::QueryParser parser;
0170             parser.set_database(db);
0171             parser.add_prefix("", "UID");
0172             const QByteArray ba = d->uid.toUtf8();
0173             m_queries << parser.parse_query(ba.constData(), Xapian::QueryParser::FLAG_PARTIAL);
0174         }
0175     }
0176     try {
0177         Xapian::Query query(Xapian::Query::OP_OR, m_queries.begin(), m_queries.end());
0178         qCDebug(AKONADI_SEARCH_PIM_LOG) << query.get_description().c_str();
0179 
0180         Xapian::Enquire enquire(db);
0181         enquire.set_query(query);
0182 
0183         if (d->limit == 0) {
0184             d->limit = 10000;
0185         }
0186 
0187         Xapian::MSet matches = enquire.get_mset(0, d->limit);
0188 
0189         ResultIterator iter;
0190         iter.d->init(matches);
0191         return iter;
0192     } catch (const Xapian::Error &e) {
0193         qCWarning(AKONADI_SEARCH_PIM_LOG) << QString::fromStdString(e.get_type()) << QString::fromStdString(e.get_description());
0194         return {};
0195     }
0196 }