File indexing completed on 2024-05-12 05:26:19

0001 /*
0002  * Copyright (C) 2015 Aaron Seigo <aseigo@kolabsystems.com>
0003  * Copyright (C) 2015 Christian Mollekopf <mollekopf@kolabsystems.com>
0004  *
0005  *   This program is free software; you can redistribute it and/or modify
0006  *   it under the terms of the GNU General Public License as published by
0007  *   the Free Software Foundation; either version 2 of the License, or
0008  *   (at your option) any later version.
0009  *
0010  *   This program is distributed in the hope that it will be useful,
0011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013  *   GNU General Public License for more details.
0014  *
0015  *   You should have received a copy of the GNU General Public License
0016  *   along with this program; if not, write to the
0017  *   Free Software Foundation, Inc.,
0018  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
0019  */
0020 
0021 #include "sinksh_utils.h"
0022 
0023 #include "common/store.h"
0024 #include "common/log.h"
0025 #include "common/propertyparser.h"
0026 
0027 #include "utils.h"
0028 
0029 namespace SinkshUtils {
0030 
0031 bool isValidStoreType(const QString &type)
0032 {
0033     return Sink::ApplicationDomain::getTypeNames().contains(type.toLatin1());
0034 }
0035 
0036 StoreBase &getStore(const QString &type)
0037 {
0038     using namespace Sink::ApplicationDomain;
0039 
0040 #define REGISTER_TYPE(TYPE) \
0041         if (type == getTypeName<TYPE>()) { static Store<TYPE> store; return store; } else
0042 SINK_REGISTER_TYPES()
0043 #undef REGISTER_TYPE
0044         {
0045             SinkWarning_("", "") << "Trying to get a store that doesn't exist: " << type;
0046             Q_ASSERT(false);
0047         }
0048 
0049     static DummyStore store;
0050     return store;
0051 }
0052 
0053 QList<QByteArray> requestedProperties(const QString &type)
0054 {
0055     using namespace Sink::ApplicationDomain;
0056     if (type == getTypeName<Folder>()) {
0057         return QList<QByteArray>() << Folder::Name::name
0058                                   << Folder::Parent::name
0059                                   << Folder::SpecialPurpose::name;
0060     } else if (type == getTypeName<Mail>()) {
0061         return QList<QByteArray>() << Mail::Subject::name
0062                                   << Mail::Folder::name
0063                                   << Mail::Date::name;
0064     } else if (type == getTypeName<Event>()) {
0065         return QList<QByteArray>() << Event::Summary::name;
0066     } else if (type == getTypeName<Todo>()) {
0067         return QList<QByteArray>() << Todo::Summary::name << Todo::Status::name;
0068     } else if (type == getTypeName<Contact>()) {
0069         return QList<QByteArray>() << Contact::Fn::name << Contact::Emails::name << Contact::Addressbook::name;
0070     } else if (type == getTypeName<Addressbook>()) {
0071         return QList<QByteArray>() << Addressbook::Name::name << Addressbook::Parent::name;
0072     } else if (type == getTypeName<SinkResource>()) {
0073         return QList<QByteArray>() << SinkResource::ResourceType::name << SinkResource::Account::name << SinkResource::Server::name;
0074     } else if (type == getTypeName<SinkAccount>()) {
0075         return QList<QByteArray>() << SinkAccount::AccountType::name << SinkAccount::Name::name;
0076     } else if (type == getTypeName<Identity>()) {
0077         return QList<QByteArray>() << Identity::Name::name << Identity::Address::name << Identity::Account::name;
0078     }
0079     return QList<QByteArray>();
0080 }
0081 
0082 QSharedPointer<QAbstractItemModel> loadModel(const QString &type, Sink::Query query)
0083 {
0084     query.requestedProperties = requestedProperties(type);
0085     auto model = getStore(type).loadModel(query);
0086     Q_ASSERT(model);
0087     return model;
0088 }
0089 
0090 QStringList resourceIds()
0091 {
0092     Sink::Query query;
0093     QStringList resources;
0094     for (const auto &r : getStore("resource").read(query)) {
0095         resources << r.identifier();
0096     }
0097     return resources;
0098 }
0099 
0100 QStringList debugareaCompleter(const QStringList &, const QString &fragment, State &state)
0101 {
0102     return Utils::filteredCompletions(Sink::Log::debugAreas().toList(), fragment);
0103 }
0104 
0105 QStringList resourceCompleter(const QStringList &, const QString &fragment, State &state)
0106 {
0107     return Utils::filteredCompletions(resourceIds(), fragment);
0108 }
0109 
0110 static QStringList toStringList(const QByteArrayList &l)
0111 {
0112     QStringList list;
0113     for (const auto &s : l) {
0114         list << s;
0115     }
0116     return list;
0117 }
0118 
0119 QStringList resourceOrTypeCompleter(const QStringList &commands, const QString &fragment, State &state)
0120 {
0121     if (commands.count() == 1) {
0122         return Utils::filteredCompletions(toStringList(Sink::ApplicationDomain::getTypeNames()), fragment);
0123     }
0124 
0125     return Utils::filteredCompletions(resourceIds(), fragment);
0126 }
0127 
0128 QStringList typeCompleter(const QStringList &commands, const QString &fragment, State &state)
0129 {
0130     return Utils::filteredCompletions(toStringList(Sink::ApplicationDomain::getTypeNames()), fragment);
0131 }
0132 
0133 QMap<QString, QString> keyValueMapFromArgs(const QStringList &args)
0134 {
0135     QMap<QString, QString> map;
0136     for (int i = 0; i + 2 <= args.size(); i += 2) {
0137         map.insert(args.at(i), args.at(i + 1));
0138     }
0139     return map;
0140 }
0141 
0142 bool isId(const QByteArray &value)
0143 {
0144     return value.startsWith("{");
0145 }
0146 
0147 bool applyFilter(Sink::Query &query, const QStringList &args_)
0148 {
0149     if (args_.isEmpty()) {
0150         return false;
0151     }
0152     auto args = args_;
0153 
0154     auto type = args.takeFirst();
0155 
0156     if ((type.isEmpty() || !SinkshUtils::isValidStoreType(type)) && type != "*") {
0157         qWarning() << "Unknown type: " << type;
0158         return false;
0159     }
0160     if (type != "*") {
0161         query.setType(type.toUtf8());
0162     }
0163     if (!args.isEmpty()) {
0164         auto resource = args.takeFirst().toLatin1();
0165 
0166         if (resource.contains('/')) {
0167             //The resource isn't an id but a path
0168             auto list = resource.split('/');
0169             const auto resourceId = parseUid(list.takeFirst());
0170             query.resourceFilter(resourceId);
0171             if (type == Sink::ApplicationDomain::getTypeName<Sink::ApplicationDomain::Mail>() && !list.isEmpty()) {
0172                 auto value = list.takeFirst();
0173                 if (isId(value)) {
0174                     query.filter<Sink::ApplicationDomain::Mail::Folder>(value);
0175                 } else {
0176                     Sink::Query folderQuery;
0177                     folderQuery.resourceFilter(resourceId);
0178                     folderQuery.filter<Sink::ApplicationDomain::Folder::Name>(value);
0179                     folderQuery.filter<Sink::ApplicationDomain::Folder::Parent>(QVariant());
0180                     auto folders = Sink::Store::read<Sink::ApplicationDomain::Folder>(folderQuery);
0181                     if (folders.size() == 1) {
0182                         query.filter<Sink::ApplicationDomain::Mail::Folder>(folders.first());
0183                     } else {
0184                         qWarning() << "Folder name did not match uniquely: " << folders.size();
0185                         for (const auto &f : folders) {
0186                             qWarning() << f.getName();
0187                         }
0188                         return false;
0189                     }
0190                 }
0191             }
0192         } else {
0193             query.resourceFilter(parseUid(resource));
0194         }
0195     }
0196     return true;
0197 }
0198 
0199 bool applyFilter(Sink::Query &query, const SyntaxTree::Options &options)
0200 {
0201     bool ret = applyFilter(query, options.positionalArguments);
0202     if (options.options.contains("resource")) {
0203         for (const auto &f : options.options.value("resource")) {
0204             query.resourceFilter(parseUid(f.toLatin1()));
0205         }
0206     }
0207     if (options.options.contains("filter")) {
0208         for (const auto &f : options.options.value("filter")) {
0209             auto filter = f.split("=");
0210             const auto property = filter.value(0).toLatin1();
0211             const auto value = filter.value(1);
0212             query.filter(property, Sink::PropertyParser::parse(query.type(), property, QString::fromUtf8(parseUid(value.toUtf8()))));
0213         }
0214     }
0215     if (options.options.contains("fulltext")) {
0216         for (const auto &f : options.options.value("fulltext")) {
0217             query.filter({}, Sink::QueryBase::Comparator(f, Sink::QueryBase::Comparator::Fulltext));
0218         }
0219     }
0220     if (options.options.contains("id")) {
0221         for (const auto &f : options.options.value("id")) {
0222             query.filter(parseUid(f.toUtf8()));
0223         }
0224     }
0225     return ret;
0226 }
0227 
0228 QByteArray parseUid(const QByteArray &uid)
0229 {
0230     if (uid.size() == 36 && uid.contains('-') && !uid.startsWith('{')) {
0231         return '{' + uid + '}';
0232     }
0233     return uid;
0234 }
0235 
0236 }