File indexing completed on 2024-04-28 16:13:26

0001 /*
0002     SPDX-FileCopyrightText: 2018 Ralf Habacker ralf.habacker @freenet.de
0003     SPDX-FileCopyrightText: 2019 Thomas Baumgart tbaumgart @kde.org
0004 
0005     This file is part of libalkimia.
0006 
0007     SPDX-License-Identifier: LGPL-2.1-or-later
0008 */
0009 
0010 #ifdef ENABLE_FINANCEQUOTE
0011 #include "alkfinancequoteprocess.h"
0012 #endif
0013 #include "alkonlinequotesprofile.h"
0014 #include "alkonlinequotesprofilemanager.h"
0015 
0016 #include "alkonlinequotesource.h"
0017 
0018 #include <QApplication>
0019 #include <QDir>
0020 #include <QLibraryInfo>
0021 #include <QString>
0022 #include <QtDebug>
0023 #include <QFileInfo>
0024 
0025 #include <KConfig>
0026 #include <KConfigGroup>
0027 
0028 #if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
0029     #include <QStandardPaths>
0030     #include <knscore/downloadmanager.h>
0031     namespace KNS = KNSCore;
0032 #else
0033     #include <KGlobal>
0034     #include <KStandardDirs>
0035     #include <knewstuff3/downloadmanager.h>
0036     namespace KNS = KNS3;
0037 #endif
0038 
0039 class AlkOnlineQuotesProfile::Private : public QObject
0040 {
0041     Q_OBJECT
0042 public:
0043     AlkOnlineQuotesProfile *m_p;
0044     QString m_name;
0045     QString m_GHNSFile;
0046     QString m_GHNSFilePath;
0047     QString m_kconfigFile;
0048     AlkOnlineQuotesProfileManager *m_profileManager;
0049     KNS::DownloadManager *m_manager;
0050     KConfig *m_config;
0051     Type m_type;
0052     static QString m_financeQuoteScriptPath;
0053     static QStringList m_financeQuoteSources;
0054 
0055     bool setupFinanceQuoteScriptPath()
0056     {
0057         if (m_financeQuoteScriptPath.isEmpty()) {
0058 #if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
0059             m_financeQuoteScriptPath = QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("misc/financequote.pl"));
0060 #else
0061             m_financeQuoteScriptPath = KGlobal::dirs()->findResource("appdata",
0062                                                                      QString("misc/financequote.pl"));
0063 #endif
0064         }
0065         return !m_financeQuoteScriptPath.isEmpty();
0066     }
0067 
0068     Private(AlkOnlineQuotesProfile *p)
0069         : m_p(p)
0070         , m_profileManager(0)
0071         , m_manager(0)
0072         , m_config(0)
0073         , m_type(Type::Undefined)
0074     {
0075 #ifdef ENABLE_FINANCEQUOTE
0076         setupFinanceQuoteScriptPath();
0077 #endif
0078     }
0079 
0080     ~Private()
0081     {
0082         delete m_manager;
0083         delete m_config;
0084     }
0085 
0086     void checkUpdates()
0087     {
0088         m_manager = new KNS::DownloadManager(m_p->hotNewStuffConfigFile(), this);
0089         // to know when checking for updates is done
0090         connect(m_manager, SIGNAL(searchResult(KNS3::Entry::List)), this,
0091                 SLOT(slotUpdatesFound(KNS3::Entry::List)));
0092         // to know about finished installations
0093         connect(m_manager, SIGNAL(entryStatusChanged(KNS3::Entry)), this,
0094                 SLOT(entryStatusChanged(KNS3::Entry)));
0095         // start checking for updates
0096         m_manager->checkForUpdates();
0097     }
0098 
0099 public Q_SLOTS:
0100     void slotUpdatesFound(const KNS3::Entry::List &updates)
0101     {
0102         foreach (const KNS3::Entry &entry, updates) {
0103             qDebug() << "update available in profile" << m_p->name() << "for" << entry.name() << entry.version() << entry.id() << entry.category() << entry.providerId();
0104             emit m_p->updateAvailable(m_p->name(), entry.name());
0105         }
0106     }
0107 
0108     // to know about finished installations
0109     void entryStatusChanged(const KNS3::Entry &entry)
0110     {
0111         qDebug() << __FUNCTION__ << entry.name() << entry.status() << entry.summary();
0112     }
0113 
0114     const QStringList quoteSourcesNative()
0115     {
0116         //KSharedConfigPtr kconfig = KGlobal::config();
0117         KConfig config(m_kconfigFile);
0118         KConfig *kconfig = &config;
0119         QStringList groups = kconfig->groupList();
0120 
0121         QStringList::Iterator it;
0122         QRegExp onlineQuoteSource(QString("^Online-Quote-Source-(.*)$"));
0123 
0124         // get rid of all 'non online quote source' entries
0125         for (it = groups.begin(); it != groups.end(); it = groups.erase(it)) {
0126             if (onlineQuoteSource.indexIn(*it) >= 0) {
0127                 // Insert the name part
0128                 it = groups.insert(it, onlineQuoteSource.cap(1));
0129                 ++it;
0130             }
0131         }
0132 
0133         // Set up each of the default sources.  These are done piecemeal so that
0134         // when we add a new source, it's automatically picked up. And any changes
0135         // are also picked up.
0136         QMap<QString, AlkOnlineQuoteSource> defaults = defaultQuoteSources();
0137         QMap<QString, AlkOnlineQuoteSource>::iterator it_source = defaults.begin();
0138         while (it_source != defaults.end()) {
0139             if (!groups.contains((*it_source).name())) {
0140                 groups += (*it_source).name();
0141                 (*it_source).write();
0142                 kconfig->sync();
0143             }
0144             ++it_source;
0145         }
0146 
0147         return groups;
0148     }
0149 
0150 #ifdef ENABLE_FINANCEQUOTE
0151     const QStringList quoteSourcesFinanceQuote()
0152     {
0153         if (m_financeQuoteSources.empty()) { // run the process one time only
0154             // since this is a static function it can be called without constructing an object
0155             // so we need to make sure that m_financeQuoteScriptPath is properly initialized
0156             if (setupFinanceQuoteScriptPath()) {
0157                 AlkFinanceQuoteProcess getList;
0158                 getList.launch(m_financeQuoteScriptPath);
0159                 while (!getList.isFinished()) {
0160                     qApp->processEvents();
0161                 }
0162                 m_financeQuoteSources = getList.getSourceList();
0163             }
0164         }
0165         return m_financeQuoteSources;
0166     }
0167 #endif
0168 
0169     const QStringList quoteSourcesSkrooge()
0170     {
0171         return quoteSourcesGHNS();
0172     }
0173 
0174     const QStringList quoteSourcesGHNS()
0175     {
0176         QStringList sources;
0177 
0178 #if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
0179         QStringList resources;
0180         const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, m_GHNSFilePath, QStandardPaths::LocateDirectory);
0181         Q_FOREACH (const QString& dir, dirs) {
0182             const QStringList fileNames = QDir(dir).entryList(QStringList() << QStringLiteral("*.txt"));
0183             Q_FOREACH (const QString& file, fileNames) {
0184                 resources.append(dir + '/' + file);
0185             }
0186         }
0187 #else
0188         const QString filename = QString("%1/*.txt").arg(m_GHNSFilePath);
0189         const QStringList resources = KStandardDirs().findAllResources("data", filename);
0190 #endif
0191         foreach (const QString &file, resources) {
0192             QFileInfo f(file);
0193             QString file2 = f.completeBaseName();
0194             AlkOnlineQuoteSource source(file2, m_p);
0195             if (source.isEmpty()) {
0196                 qDebug() << "skipping" << file;
0197                 continue;
0198             }
0199             if (!sources.contains(file2)) {
0200                 qDebug() << "adding quote source" << file;
0201                 sources.push_back(file2);
0202             }
0203         }
0204 
0205         return sources;
0206     }
0207 
0208     const AlkOnlineQuotesProfile::Map defaultQuoteSources()
0209     {
0210         QMap<QString, AlkOnlineQuoteSource> result;
0211 
0212         // Use fx-rate.net as the standard currency exchange rate source until
0213         // we have the capability to use more than one source. Use a neutral
0214         // name for the source.
0215 
0216         switch (m_p->type()) {
0217         case AlkOnlineQuotesProfile::Type::None:
0218         case AlkOnlineQuotesProfile::Type::Alkimia4:
0219         case AlkOnlineQuotesProfile::Type::Alkimia5: {
0220             AlkOnlineQuoteSource source("Alkimia Currency",
0221                                         "https://fx-rate.net/%1/%2",
0222                                         QString(), // symbolregexp
0223                                         "Today\\s+=\\s+([^<]+)",
0224                                         ",\\s*(\\d+\\s*[a-zA-Z]{3}\\s*\\d{4})",
0225                                         "%d %m %y",
0226                                         true // skip HTML stripping
0227                                         );
0228             source.setProfile(m_p);
0229             result[source.name()] = source;
0230 #if defined(BUILD_WITH_WEBKIT) || defined(BUILD_WITH_WEBENGINE)
0231             AlkOnlineQuoteSource source2("Alkimia Currency.webkit",
0232                                         "https://fx-rate.net/%1/%2",
0233                                         QString(), // symbolregexp
0234                                         "Today\\s+=\\s+([^<]+)",
0235                                         ",\\s*(\\d+\\s*[a-zA-Z]{3}\\s*\\d{4})",
0236                                         "%d %m %y",
0237                                         true // skip HTML stripping
0238                                         );
0239             source2.setProfile(m_p);
0240             result[source2.name()] = source2;
0241 #endif
0242             break;
0243         }
0244         default:
0245             break;
0246         }
0247         return result;
0248     }
0249 
0250     /**
0251      * @brief return data root path
0252      * @return path
0253      */
0254     QString dataRootPath()
0255     {
0256         return QLibraryInfo::location(QLibraryInfo::PrefixPath) + "/share";
0257     }
0258 
0259     /**
0260      * @brief return home root path
0261      * @return path
0262      */
0263     QString homeRootPath()
0264     {
0265         if (m_type == Type::KMyMoney5 || m_type == Type::Alkimia5 || m_type == Type::Skrooge5)
0266             return QDir::homePath();
0267         else if (m_type == Type::KMyMoney4 || m_type == Type::Alkimia4 || m_type == Type::Skrooge4) {
0268 #ifdef Q_OS_WIN
0269             return qgetenv("APPDATA");
0270 #else
0271             return QDir::homePath();
0272 #endif
0273         } else {
0274             return QString();
0275         }
0276     }
0277 
0278     QString configPath()
0279     {
0280 #if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
0281         if (m_type == Type::KMyMoney5)
0282             return QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
0283         else if(m_type == Type::Alkimia5 || m_type == Type::Skrooge5)
0284             return QString("%1/.config").arg(homeRootPath());
0285         else
0286 #endif
0287         if (m_type == Type::KMyMoney4 || m_type == Type::Alkimia4 || m_type == Type::Skrooge4)
0288             return QString("%1/.kde4/share/config").arg(homeRootPath());
0289         return
0290             QString();
0291     }
0292 
0293     QString dataReadPath()
0294     {
0295         if (m_type == Type::KMyMoney5 || m_type == Type::Alkimia5 || m_type == Type::Skrooge5)
0296             return dataRootPath();
0297         else if (m_type == Type::KMyMoney4 || m_type == Type::Alkimia4 || m_type == Type::Skrooge4)
0298             return QString("%1/kde4/apps").arg(dataRootPath());
0299         return
0300             QString();
0301     }
0302 
0303     QString dataWritePath()
0304     {
0305         if (m_type == Type::KMyMoney5 || m_type == Type::Alkimia5 || m_type == Type::Skrooge5)
0306             return QString("%1/.local/share").arg(homeRootPath());
0307         else if (m_type == Type::KMyMoney4 || m_type == Type::Alkimia4 || m_type == Type::Skrooge4)
0308             return QString("%1/.kde4/share/apps").arg(homeRootPath());
0309         return
0310             QString();
0311     }
0312 };
0313 
0314 // define static members
0315 QString AlkOnlineQuotesProfile::Private::m_financeQuoteScriptPath;
0316 QStringList AlkOnlineQuotesProfile::Private::m_financeQuoteSources;
0317 
0318 
0319 AlkOnlineQuotesProfile::AlkOnlineQuotesProfile(const QString &name, Type type,
0320                                                const QString &ghnsConfigFile)
0321     : d(new Private(this))
0322 {
0323     d->m_name = name;
0324     d->m_GHNSFile = ghnsConfigFile;
0325     d->m_type = type;
0326     if (type == Type::KMyMoney5)
0327         d->m_kconfigFile = QString("%1/kmymoney/kmymoneyrc").arg(d->configPath());
0328     else if (type == Type::KMyMoney4)
0329         d->m_kconfigFile = QString("%1/kmymoneyrc").arg(d->configPath());
0330     else if (type == Type::Alkimia5 || type == Type::Alkimia4)
0331         d->m_kconfigFile = QString("%1/alkimiarc").arg(d->configPath());
0332     else
0333         d->m_kconfigFile = "";
0334     if (!d->m_kconfigFile.isEmpty())
0335         d->m_config = new KConfig(d->m_kconfigFile);
0336     if (!d->m_GHNSFile.isEmpty()) {
0337         KConfig ghnsFile(hotNewStuffConfigFile());
0338         KConfigGroup group = ghnsFile.group("KNewStuff3");
0339         d->m_GHNSFilePath = group.readEntry("TargetDir");
0340         d->checkUpdates();
0341     }
0342 }
0343 
0344 AlkOnlineQuotesProfile::~AlkOnlineQuotesProfile()
0345 {
0346     delete d;
0347 }
0348 
0349 QString AlkOnlineQuotesProfile::name() const
0350 {
0351     return d->m_name;
0352 }
0353 
0354 QString AlkOnlineQuotesProfile::hotNewStuffConfigFile() const
0355 {
0356 #if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
0357     QString configFile = QStandardPaths::locate(QStandardPaths::ConfigLocation, d->m_GHNSFile);
0358     if (configFile.isEmpty()) {
0359         configFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "knsrcfiles/" + d->m_GHNSFile);
0360     }
0361 #else
0362     QString configFile = KStandardDirs::locate("config", d->m_GHNSFile);
0363     if (configFile.isEmpty()) {
0364          configFile = KStandardDirs::locate("data", "knsrcfiles/" + d->m_GHNSFile);
0365     }
0366 #endif
0367     if (configFile.isEmpty()) {
0368         configFile = QString("%1/%2").arg(KNSRC_DIR, d->m_GHNSFile);
0369     }
0370 
0371     return configFile;
0372 }
0373 
0374 QString AlkOnlineQuotesProfile::hotNewStuffReadFilePath(const QString &fileName) const
0375 {
0376     foreach(const QString &path, hotNewStuffReadPath()) {
0377         QFileInfo f(path + fileName);
0378         if (f.exists())
0379             return f.absoluteFilePath();
0380     }
0381     return QString();
0382 }
0383 
0384 QString AlkOnlineQuotesProfile::hotNewStuffWriteFilePath(const QString &fileName) const
0385 {
0386     return QString("%1%2").arg(hotNewStuffWriteDir(), fileName);
0387 }
0388 
0389 QStringList AlkOnlineQuotesProfile::hotNewStuffReadPath() const
0390 {
0391     return QStringList()
0392         << QString("%1/%2/").arg(d->dataReadPath(), d->m_GHNSFilePath)
0393         << hotNewStuffWriteDir();
0394 }
0395 
0396 QString AlkOnlineQuotesProfile::hotNewStuffWriteDir() const
0397 {
0398     return QString("%1/%2/").arg(d->dataWritePath(), d->m_GHNSFilePath);
0399 }
0400 
0401 QString AlkOnlineQuotesProfile::hotNewStuffRelPath() const
0402 {
0403     return d->m_GHNSFilePath;
0404 }
0405 
0406 QString AlkOnlineQuotesProfile::kConfigFile() const
0407 {
0408     return d->m_kconfigFile;
0409 }
0410 
0411 KConfig *AlkOnlineQuotesProfile::kConfig() const
0412 {
0413     return d->m_config;
0414 }
0415 
0416 AlkOnlineQuotesProfile::Type AlkOnlineQuotesProfile::type()
0417 {
0418     return d->m_type;
0419 }
0420 
0421 bool AlkOnlineQuotesProfile::hasGHNSSupport()
0422 {
0423     return !d->m_GHNSFile.isEmpty();
0424 }
0425 
0426 const AlkOnlineQuotesProfile::Map AlkOnlineQuotesProfile::defaultQuoteSources()
0427 {
0428     return d->defaultQuoteSources();
0429 }
0430 
0431 const QStringList AlkOnlineQuotesProfile::quoteSources()
0432 {
0433     QStringList result;
0434     switch(d->m_type) {
0435     case AlkOnlineQuotesProfile::Type::Alkimia4:
0436     case AlkOnlineQuotesProfile::Type::Alkimia5:
0437     case AlkOnlineQuotesProfile::Type::KMyMoney4:
0438     case AlkOnlineQuotesProfile::Type::KMyMoney5:
0439         result << d->quoteSourcesNative();
0440         break;
0441 #ifdef ENABLE_FINANCEQUOTE
0442     case AlkOnlineQuotesProfile::Type::Script:
0443         result << d->quoteSourcesFinanceQuote();
0444         break;
0445 #endif
0446     case AlkOnlineQuotesProfile::Type::None:
0447         result << d->defaultQuoteSources().keys();
0448         break;
0449     default:
0450         break;
0451     }
0452     if (hasGHNSSupport())
0453         result << d->quoteSourcesGHNS();
0454     return result;
0455 }
0456 
0457 void AlkOnlineQuotesProfile::setManager(AlkOnlineQuotesProfileManager *manager)
0458 {
0459     d->m_profileManager = manager;
0460 }
0461 
0462 AlkOnlineQuotesProfileManager *AlkOnlineQuotesProfile::manager()
0463 {
0464     return d->m_profileManager;
0465 }
0466 
0467 QString AlkOnlineQuotesProfile::scriptPath()
0468 {
0469     return d->m_financeQuoteScriptPath;
0470 }
0471 
0472 #include "alkonlinequotesprofile.moc"