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"