File indexing completed on 2024-04-28 05:02:32

0001 /*
0002     SPDX-FileCopyrightText: 2019 Thomas Baumgart tbaumgart @kde.org
0003     SPDX-FileCopyrightText: 2024 Ralf Habacker ralf.habacker @freenet.de
0004 
0005     This file is part of libalkimia.
0006 
0007     SPDX-License-Identifier: LGPL-2.1-or-later
0008 */
0009 
0010 #include "alkonlinequotesprofile_p.h"
0011 
0012 #include "alkutils.h"
0013 
0014 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
0015 #include <QDebug>
0016 #include <QRegularExpression>
0017 #else
0018 #include <KConfig>
0019 #include <KDebug>
0020 #endif
0021 
0022 // define static members
0023 QString AlkOnlineQuotesProfile::Private::m_financeQuoteScriptPath;
0024 QStringList AlkOnlineQuotesProfile::Private::m_financeQuoteSources;
0025 
0026 bool AlkOnlineQuotesProfile::Private::setupFinanceQuoteScriptPath()
0027 {
0028     if (m_financeQuoteScriptPath.isEmpty()) {
0029         m_financeQuoteScriptPath = AlkUtils::locateDataFile("misc/financequote.pl");
0030     }
0031     return !m_financeQuoteScriptPath.isEmpty();
0032 }
0033 
0034 AlkOnlineQuotesProfile::Private::Private(AlkOnlineQuotesProfile *p)
0035     : m_p(p)
0036     , m_profileManager(0)
0037     , m_engine(new AlkNewStuffEngine)
0038     , m_config(0)
0039     , m_type(Type::Undefined)
0040 {
0041 #ifdef ENABLE_FINANCEQUOTE
0042     setupFinanceQuoteScriptPath();
0043 #endif
0044 #if QT_VERSION < QT_VERSION_CHECK(5,0,0)
0045     connect(m_engine, SIGNAL(updatesAvailable(AlkNewStuffEntryList)), this,
0046             SLOT(slotUpdatesAvailable(AlkNewStuffEntryList)));
0047 #else
0048     connect(m_engine, &AlkNewStuffEngine::updatesAvailable, this,
0049             &AlkOnlineQuotesProfile::Private::slotUpdatesAvailable);
0050 #endif
0051 }
0052 
0053 AlkOnlineQuotesProfile::Private::~Private()
0054 {
0055     delete m_engine;
0056 }
0057 
0058 QString AlkOnlineQuotesProfile::Private::GHNSId(const QString &name) const
0059 {
0060     for (const AlkNewStuffEntry &entry : m_engine->installedEntries()) {
0061         if (entry.name == name)
0062             return entry.id;
0063     }
0064     return QString();
0065 }
0066 
0067 QString AlkOnlineQuotesProfile::Private::GHNSFilePath(const QString &name) const
0068 {
0069     for (const AlkNewStuffEntry &entry : m_engine->installedEntries()) {
0070         if (entry.name == name)
0071             return entry.installedFiles.size() > 0 ? entry.installedFiles[0] : QString();
0072     }
0073     return QString();
0074 }
0075 
0076 const QStringList AlkOnlineQuotesProfile::Private::quoteSourcesNative()
0077 {
0078     auto kconfig = KSharedConfig::openConfig(m_kconfigFile, KConfig::SimpleConfig);
0079     QStringList groups = kconfig->groupList();
0080 
0081     QStringList::Iterator it;
0082 #if QT_VERSION < QT_VERSION_CHECK(5,0,0)
0083     QRegExp onlineQuoteSource(QString("^Online-Quote-Source-(.*)$"));
0084 
0085     // get rid of all 'non online quote source' entries
0086     for (it = groups.begin(); it != groups.end(); it = groups.erase(it)) {
0087         if (onlineQuoteSource.indexIn(*it) >= 0) {
0088             // Insert the name part
0089             it = groups.insert(it, onlineQuoteSource.cap(1));
0090             ++it;
0091         }
0092     }
0093 #else
0094     QRegularExpression onlineQuoteSource(QLatin1String("^Online-Quote-Source-(.*)$"));
0095 
0096     // get rid of all 'non online quote source' entries
0097     for (it = groups.begin(); it != groups.end(); it = groups.erase(it)) {
0098         const auto match = onlineQuoteSource.match(*it);
0099         if (match.hasMatch()) {
0100             // Insert the name part
0101             it = groups.insert(it, match.captured(1));
0102             ++it;
0103         }
0104     }
0105 #endif
0106     // Set up each of the default sources.  These are done piecemeal so that
0107     // when we add a new source, it's automatically picked up. And any changes
0108     // are also picked up.
0109     QMap<QString, AlkOnlineQuoteSource> defaults = defaultQuoteSources();
0110     QMap<QString, AlkOnlineQuoteSource>::iterator it_source = defaults.begin();
0111     while (it_source != defaults.end()) {
0112         if (!groups.contains((*it_source).name())) {
0113             groups += (*it_source).name();
0114             (*it_source).write();
0115             kconfig->sync();
0116         }
0117         ++it_source;
0118     }
0119 
0120     return groups;
0121 }
0122 
0123 #ifdef ENABLE_FINANCEQUOTE
0124 const QStringList AlkOnlineQuotesProfile::Private::quoteSourcesFinanceQuote()
0125 {
0126     if (m_financeQuoteSources.empty()) { // run the process one time only
0127         // since this is a static function it can be called without constructing an object
0128         // so we need to make sure that m_financeQuoteScriptPath is properly initialized
0129         if (setupFinanceQuoteScriptPath()) {
0130             AlkFinanceQuoteProcess testList;
0131             testList.testLaunch(m_financeQuoteScriptPath);
0132             while (!testList.isFinished()) {
0133                 qApp->processEvents();
0134             }
0135 
0136             if (testList.exitCode() == 0) {
0137                 AlkFinanceQuoteProcess getList;
0138                 getList.launch(m_financeQuoteScriptPath);
0139                 while (!getList.isFinished()) {
0140                     qApp->processEvents();
0141                 }
0142                 m_financeQuoteSources = getList.getSourceList();
0143             }
0144         }
0145     }
0146     return m_financeQuoteSources;
0147 }
0148 #endif
0149 
0150 const QStringList AlkOnlineQuotesProfile::Private::quoteSourcesSkrooge()
0151 {
0152     return quoteSourcesGHNS();
0153 }
0154 
0155 const QStringList AlkOnlineQuotesProfile::Private::quoteSourcesGHNS()
0156 {
0157     QStringList sources;
0158     QStringList files = AlkUtils::getDataFiles(m_GHNSFilePath, QStringList() << QStringLiteral("*.txt"));
0159 
0160     // add installed remote sources
0161     for (const AlkNewStuffEntry &entry : m_engine->installedEntries()) {
0162         AlkOnlineQuoteSource source(entry.name, m_p);
0163         if (entry.installedFiles.size() > 0)
0164             files.removeAll(entry.installedFiles[0]);
0165         if (source.isEmpty()) {
0166             qDebug() << "skipping" << entry.name;
0167             continue;
0168         }
0169         if (!sources.contains(entry.id)) {
0170             qDebug() << "adding quote source" << entry.name;
0171             sources.push_back(entry.name);
0172         }
0173     }
0174 
0175     // add unpublished remote sources
0176     for (const QString &file : files) {
0177         QFileInfo f(file);
0178         QString name = f.completeBaseName();
0179         AlkOnlineQuoteSource source(name, m_p);
0180         if (source.isEmpty()) {
0181             qDebug() << "skipping" << name;
0182             continue;
0183         }
0184         if (!sources.contains(name)) {
0185             qDebug() << "adding quote source" << name;
0186             sources.push_back(name);
0187         }
0188     }
0189     return sources;
0190 }
0191 
0192 const AlkOnlineQuotesProfile::Map AlkOnlineQuotesProfile::Private::defaultQuoteSources()
0193 {
0194     QMap<QString, AlkOnlineQuoteSource> result;
0195 
0196     // Use fx-rate.net as the standard currency exchange rate source until
0197     // we have the capability to use more than one source. Use a neutral
0198     // name for the source.
0199 
0200     switch (m_p->type()) {
0201     case AlkOnlineQuotesProfile::Type::None:
0202     case AlkOnlineQuotesProfile::Type::Alkimia4:
0203     case AlkOnlineQuotesProfile::Type::Alkimia5: {
0204         AlkOnlineQuoteSource source(AlkOnlineQuoteSource::defaultCurrencyQuoteSource("Alkimia Currency"));
0205         source.setProfile(m_p);
0206         result[source.name()] = source;
0207 #if defined(BUILD_WITH_WEBKIT) || defined(BUILD_WITH_WEBENGINE)
0208         AlkOnlineQuoteSource source2(AlkOnlineQuoteSource::defaultCurrencyQuoteSource("Alkimia Currency.webkit"));
0209         source2.setProfile(m_p);
0210         result[source2.name()] = source2;
0211 #endif
0212         break;
0213     }
0214     default:
0215         break;
0216     }
0217     return result;
0218 }
0219 
0220 QString AlkOnlineQuotesProfile::Private::dataRootPath()
0221 {
0222     return QLibraryInfo::location(QLibraryInfo::PrefixPath) + "/share";
0223 }
0224 
0225 QString AlkOnlineQuotesProfile::Private::homeRootPath()
0226 {
0227     if (m_type == Type::KMyMoney5 || m_type == Type::Alkimia5 || m_type == Type::Skrooge5)
0228         return QDir::homePath();
0229     else if (m_type == Type::KMyMoney4 || m_type == Type::Alkimia4 || m_type == Type::Skrooge4) {
0230 #ifdef Q_OS_WIN
0231         return qgetenv("APPDATA");
0232 #else
0233         return QDir::homePath();
0234 #endif
0235     } else {
0236         return QString();
0237     }
0238 }
0239 
0240 QString AlkOnlineQuotesProfile::Private::configPath()
0241 {
0242 #if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
0243     if (m_type == Type::KMyMoney5)
0244         return QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
0245     else if(m_type == Type::Alkimia5 || m_type == Type::Skrooge5)
0246         return QString("%1/.config").arg(homeRootPath());
0247     else
0248 #endif
0249         if (m_type == Type::KMyMoney4 || m_type == Type::Alkimia4 || m_type == Type::Skrooge4)
0250             return QString("%1/.kde4/share/config").arg(homeRootPath());
0251     return
0252             QString();
0253 }
0254 
0255 QString AlkOnlineQuotesProfile::Private::dataReadPath()
0256 {
0257     if (m_type == Type::KMyMoney5 || m_type == Type::Alkimia5 || m_type == Type::Skrooge5)
0258         return dataRootPath();
0259     else if (m_type == Type::KMyMoney4 || m_type == Type::Alkimia4 || m_type == Type::Skrooge4)
0260         return QString("%1/kde4/apps").arg(dataRootPath());
0261     return
0262             QString();
0263 }
0264 
0265 QString AlkOnlineQuotesProfile::Private::dataWritePath()
0266 {
0267     if (m_type == Type::KMyMoney5 || m_type == Type::Alkimia5 || m_type == Type::Skrooge5)
0268         return QString("%1/.local/share").arg(homeRootPath());
0269     else if (m_type == Type::KMyMoney4 || m_type == Type::Alkimia4 || m_type == Type::Skrooge4)
0270         return QString("%1/.kde4/share/apps").arg(homeRootPath());
0271     return
0272             QString();
0273 }
0274 
0275 void AlkOnlineQuotesProfile::Private::slotUpdatesAvailable(const AlkNewStuffEntryList &updates)
0276 {
0277     for (auto &entry : updates) {
0278         qDebug() << "update available in profile" << m_p->name() << "for"
0279                  << entry.name << entry.version << entry.id << entry.category
0280                  << entry.providerId;
0281         Q_EMIT m_p->updateAvailable(m_p->name(), entry.name);
0282     }
0283 }