File indexing completed on 2024-05-12 05:09:31

0001 /***************************************************************************
0002     Copyright (C) 2005-2022 Robby Stephenson <robby@periapsis.org>
0003  ***************************************************************************/
0004 
0005 /***************************************************************************
0006  *                                                                         *
0007  *   This program is free software; you can redistribute it and/or         *
0008  *   modify it under the terms of the GNU General Public License as        *
0009  *   published by the Free Software Foundation; either version 2 of        *
0010  *   the License or (at your option) version 3 or any later version        *
0011  *   accepted by the membership of KDE e.V. (or its successor approved     *
0012  *   by the membership of KDE e.V.), which shall act as a proxy            *
0013  *   defined in Section 14 of version 3 of the license.                    *
0014  *                                                                         *
0015  *   This program is distributed in the hope that it will be useful,       *
0016  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0017  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0018  *   GNU General Public License for more details.                          *
0019  *                                                                         *
0020  *   You should have received a copy of the GNU General Public License     *
0021  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
0022  *                                                                         *
0023  ***************************************************************************/
0024 
0025 #include "fetcher.h"
0026 #include "fetchmanager.h" // for calling static optional fields
0027 #include "../collection.h"
0028 #include "../entry.h"
0029 #include "../tellico_debug.h"
0030 
0031 #include <KLocalizedString>
0032 #include <KSharedConfig>
0033 #include <KConfigGroup>
0034 #include <KIO/Global>
0035 #include <kio_version.h>
0036 #if KIO_VERSION >= QT_VERSION_CHECK(5,19,0)
0037 #include <KIO/FavIconRequestJob>
0038 #endif
0039 
0040 #include <QUrl>
0041 #include <QUuid>
0042 #include <QPointer>
0043 
0044 using namespace Tellico::Fetch;
0045 using Tellico::Fetch::Fetcher;
0046 
0047 Fetcher::Fetcher(QObject* parent) : QObject(parent)
0048     , m_updateOverwrite(false)
0049     , m_hasMoreResults(false)
0050     , m_messager(nullptr) {
0051   Q_ASSERT(parent);
0052 }
0053 
0054 Fetcher::~Fetcher() {
0055   saveConfig();
0056 }
0057 
0058 int Fetcher::collectionType() const {
0059   return m_request.collectionType();
0060 }
0061 
0062 /// virtual, overridden by subclasses
0063 bool Fetcher::canUpdate() const {
0064   return true;
0065 }
0066 
0067 bool Fetcher::updateOverwrite() const {
0068   return m_updateOverwrite;
0069 }
0070 
0071 const Tellico::Fetch::FetchRequest& Fetcher::request() const {
0072   return m_request;
0073 }
0074 
0075 void Fetcher::startSearch(const FetchRequest& request_) {
0076   m_request = request_;
0077   if(!canFetch(m_request.collectionType())) {
0078     myDebug() << "Bad collection request type for search:" << source() << m_request.collectionType();
0079     message(i18n("%1 does not allow searching for this collection type.", source()),
0080             MessageHandler::Warning);
0081     emit signalDone(this);
0082     return;
0083   }
0084 
0085   m_entries.clear();
0086   search();
0087 }
0088 
0089 void Fetcher::startUpdate(Tellico::Data::EntryPtr entry_) {
0090   Q_ASSERT(entry_);
0091   Q_ASSERT(entry_->collection());
0092   m_request = updateRequest(entry_);
0093   m_request.setCollectionType(entry_->collection()->type());
0094   if(m_request.isNull()) {
0095     myLog() << "Insufficient info from" << source() << "to update" << entry_->title();
0096     emit signalDone(this); // always need to emit this if not continuing with the search
0097     return;
0098   } else {
0099     myLog() << "Starting update from" << source() << "for" << entry_->title();
0100   }
0101   search();
0102 }
0103 
0104 void Fetcher::readConfig(const KConfigGroup& config_) {
0105   m_configGroup = config_;
0106 
0107   QString s = config_.readEntry("Name");
0108   if(!s.isEmpty()) {
0109     m_name = s;
0110   }
0111   m_updateOverwrite = config_.readEntry("UpdateOverwrite", false);
0112   // it's called custom fields here, but it's really optional lists
0113   m_fields = config_.readEntry("Custom Fields", QStringList());
0114   s = config_.readEntry("Uuid");
0115   if(s.isEmpty()) {
0116     s = QUuid::createUuid().toString();
0117   }
0118   m_uuid = s;
0119   // be sure to read config for subclass
0120   readConfigHook(config_);
0121 }
0122 
0123 void Fetcher::saveConfig() {
0124   if(!m_configGroup.isValid() || m_configGroup.isImmutable()) {
0125     return;
0126   }
0127   m_configGroup.writeEntry("Uuid", m_uuid);
0128   saveConfigHook(m_configGroup);
0129   m_configGroup.sync();
0130 }
0131 
0132 void Fetcher::setConfigGroup(const KConfigGroup& group_) {
0133   m_configGroup = group_;
0134 }
0135 
0136 Tellico::Data::EntryPtr Fetcher::fetchEntry(uint uid_) {
0137   // check if already fetched this entry
0138   if(m_entries.contains(uid_)) {
0139     return m_entries[uid_];
0140   }
0141 
0142   QPointer<Fetcher> ptr(this);
0143   Data::EntryPtr entry = fetchEntryHook(uid_);
0144   // could be cancelled and killed after fetching entry, check ptr
0145   if(ptr && entry) {
0146     // iterate over list of possible optional fields
0147     // and if the field is not included in the user-configured list
0148     // remove the field from the entry
0149     QHashIterator<QString, QString> i(Manager::optionalFields(type()));
0150     while(i.hasNext()) {
0151       i.next();
0152       if(!m_fields.contains(i.key())) {
0153         entry->collection()->removeField(i.key());
0154       }
0155     }
0156   }
0157   m_entries.insert(uid_, entry);
0158   return entry;
0159 }
0160 
0161 void Fetcher::setMessageHandler(MessageHandler* handler) {
0162   m_messager = handler;
0163 }
0164 
0165 void Fetcher::message(const QString& message_, int type_) const {
0166   if(m_messager) {
0167     m_messager->send(message_, static_cast<MessageHandler::Type>(type_));
0168   }
0169 }
0170 
0171 QString Fetcher::favIcon(const char* url_) {
0172   return favIcon(QUrl(QString::fromLatin1(url_)));
0173 }
0174 
0175 QString Fetcher::favIcon(const QUrl& url_, const QUrl& iconUrl_) {
0176   if(!url_.isValid()) {
0177     return QString();
0178   }
0179 
0180 #if KIO_VERSION >= QT_VERSION_CHECK(5,19,0)
0181   KIO::FavIconRequestJob* job = new KIO::FavIconRequestJob(url_);
0182   // if the url has a meaningful path, then use it as the icon url
0183   if(!iconUrl_.isEmpty()) {
0184     job->setIconUrl(iconUrl_);
0185   } else if(url_.path().size() > 4 && url_.path().contains(QLatin1Char('.'))) {
0186     job->setIconUrl(url_);
0187   }
0188 
0189   connect(job, &KIO::FavIconRequestJob::result, [job](KJob *) {
0190          if(job->error()) {
0191            myDebug() << job->hostUrl().host() << "error:" << job->errorString();
0192          } else if(job->iconFile().isEmpty()) {
0193 //           myDebug() << "no favIcon found for" << job->hostUrl();
0194          }
0195      });
0196 #endif
0197 
0198   QString name = KIO::favIconForUrl(url_);
0199   // favIcons start with "/". being an absolute file path from FavIconFetchJob
0200   // but KIconLoader still expects them to start with "favicons/" and appends ".png"
0201   // since the rest of Tellico assumes KDE4 behavior, adjust here
0202   if(name.startsWith(QLatin1Char('/'))) {
0203     int pos = name.indexOf(QLatin1String("favicons/"));
0204     if(pos > -1) {
0205       name = name.mid(pos);
0206       name.chop(4); // remove ".png";
0207     }
0208   }
0209   return name;
0210 }