diff --git a/kmymoney/plugins/ofx/import/dialogs/konlinebankingsetupwizard.cpp b/kmymoney/plugins/ofx/import/dialogs/konlinebankingsetupwizard.cpp index 48cb3ca8d..899366cbc 100644 --- a/kmymoney/plugins/ofx/import/dialogs/konlinebankingsetupwizard.cpp +++ b/kmymoney/plugins/ofx/import/dialogs/konlinebankingsetupwizard.cpp @@ -1,571 +1,588 @@ /*************************************************************************** konlinebankingsetupwizard.cpp ------------------- begin : Sat Jan 7 2006 copyright : (C) 2006 by Ace Jones email : acejones@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "konlinebankingsetupwizard.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "../ofxpartner.h" #include "mymoneyofxconnector.h" using KWallet::Wallet; class KOnlineBankingSetupWizard::Private { public: Private() : m_prevPage(-1), m_wallet(0), m_walletIsOpen(false) {} QFile m_fpTrace; QTextStream m_trace; int m_prevPage; Wallet *m_wallet; bool m_walletIsOpen; }; KOnlineBankingSetupWizard::KOnlineBankingSetupWizard(QWidget *parent): QWizard(parent), d(new Private), m_fDone(false), m_fInit(false), m_appId(0) { setupUi(this); m_applicationEdit->hide(); m_headerVersionEdit->hide(); #ifndef LIBOFX_HAVE_CLIENTUID m_editClientUid->setEnabled(false); m_clientUidLabel->setEnabled(false); #endif m_appId = new OfxAppVersion(m_applicationCombo, m_applicationEdit, ""); m_headerVersion = new OfxHeaderVersion(m_headerVersionCombo, ""); // fill the list view with banks QProgressDialog* dlg = new QProgressDialog(this); dlg->setWindowTitle(i18n("Loading banklist")); dlg->setLabelText(i18n("Getting list of banks from http://www.ofxhome.com/\nThis may take some time depending on the available bandwidth.")); dlg->setModal(true); dlg->setCancelButton(0); // force to show immediately as the call to OfxPartner::BankNames() // does not call the processEvents() loop dlg->setMinimumDuration(0); QCoreApplication::processEvents(); //set password field according to KDE preferences m_editPassword->setPasswordMode(true); // make sure to not exceed data fields m_editUsername->setMaxLength(OFX_USERID_LENGTH-1); m_editPassword->setMaxLength(OFX_USERPASS_LENGTH-1); KListWidgetSearchLine* searchLine = new KListWidgetSearchLine(autoTab, m_listFi); vboxLayout1->insertWidget(0, searchLine); QTimer::singleShot(20, searchLine, SLOT(setFocus())); OfxPartner::setDirectory(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1Char('/') + ""); m_listFi->addItems(OfxPartner::BankNames()); m_fInit = true; delete dlg; checkNextButton(); connect(this, SIGNAL(currentIdChanged(int)), this, SLOT(checkNextButton())); connect(this, SIGNAL(currentIdChanged(int)), this, SLOT(newPage(int))); connect(m_listFi, SIGNAL(itemSelectionChanged()), this, SLOT(checkNextButton())); connect(m_listAccount, SIGNAL(itemSelectionChanged()), this, SLOT(checkNextButton())); connect(m_selectionTab, SIGNAL(currentChanged(int)), this, SLOT(checkNextButton())); connect(m_fid, SIGNAL(userTextChanged(QString)), this, SLOT(checkNextButton())); connect(m_bankName, SIGNAL(userTextChanged(QString)), this, SLOT(checkNextButton())); connect(m_url, SIGNAL(textChanged(QString)), this, SLOT(checkNextButton())); connect(m_editUsername, SIGNAL(userTextChanged(QString)), this, SLOT(checkNextButton())); connect(m_editPassword, SIGNAL(userTextChanged(QString)), this, SLOT(checkNextButton())); connect(m_applicationEdit, SIGNAL(userTextChanged(QString)), this, SLOT(checkNextButton())); connect(m_applicationCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(applicationSelectionChanged())); // setup text on buttons setButtonText(QWizard::NextButton, i18nc("Go to next page of the wizard", "&Next")); setButtonText(QWizard::BackButton, KStandardGuiItem::back().text()); // setup icons button(QWizard::FinishButton)->setIcon(KStandardGuiItem::ok().icon()); button(QWizard::CancelButton)->setIcon(KStandardGuiItem::cancel().icon()); button(QWizard::NextButton)->setIcon(KStandardGuiItem::forward(KStandardGuiItem::UseRTL).icon()); button(QWizard::BackButton)->setIcon(KStandardGuiItem::back(KStandardGuiItem::UseRTL).icon()); + + m_problemMessages->setHidden(true); + m_problemMessages->setWordWrap(true); } KOnlineBankingSetupWizard::~KOnlineBankingSetupWizard() { delete m_appId; delete d; } void KOnlineBankingSetupWizard::applicationSelectionChanged() { m_applicationEdit->setVisible(m_appId->appId().endsWith(':')); checkNextButton(); } void KOnlineBankingSetupWizard::walletOpened(bool ok) { if (ok && (d->m_wallet->hasFolder(KWallet::Wallet::PasswordFolder()) || d->m_wallet->createFolder(KWallet::Wallet::PasswordFolder())) && d->m_wallet->setFolder(KWallet::Wallet::PasswordFolder())) { d->m_walletIsOpen = true; } else { qDebug("Wallet was not opened"); } m_storePassword->setEnabled(d->m_walletIsOpen); } void KOnlineBankingSetupWizard::checkNextButton() { bool enableButton = false; switch (currentId()) { case 0: if (m_selectionTab->currentIndex() == 0) { enableButton = (m_listFi->currentItem() != 0) && m_listFi->currentItem()->isSelected(); } else { enableButton = !(m_url->url().isEmpty() || m_bankName->text().isEmpty()); } break; case 1: enableButton = !(m_editUsername->text().isEmpty() || m_editPassword->text().isEmpty() || !m_appId->isValid()); break; case 2: enableButton = (m_listAccount->currentItem() != 0) && m_listAccount->currentItem()->isSelected(); break; } button(QWizard::NextButton)->setEnabled(enableButton); } void KOnlineBankingSetupWizard::newPage(int id) { QWidget* focus = 0; + m_problemMessages->setHidden(true); bool ok = true; if ((id - d->m_prevPage) == 1) { // one page forward? switch (d->m_prevPage) { case 0: ok = finishFiPage(); // open the KDE wallet if not already opened if (ok && !d->m_wallet) { d->m_wallet = Wallet::openWallet(Wallet::NetworkWallet(), winId(), Wallet::Asynchronous); connect(d->m_wallet, SIGNAL(walletOpened(bool)), SLOT(walletOpened(bool))); } focus = m_editUsername; break; case 1: ok = finishLoginPage(); focus = m_listAccount; break; case 2: m_fDone = ok = finishAccountPage(); break; } if (ok) { if (focus) { focus->setFocus(); } } else { // force to go back to prev page back(); } } else { // going backwards, we're never done m_fDone = false; } button(QWizard::FinishButton)->setEnabled(m_fDone); // hide cancel and back button on last page button(QWizard::CancelButton)->setVisible(!m_fDone); button(QWizard::BackButton)->setVisible(!m_fDone); if (ok) d->m_prevPage = id; } bool KOnlineBankingSetupWizard::finishFiPage() { bool result = false; m_bankInfo.clear(); - OfxFiServiceInfo info; + OfxHomeServiceInfo info; if (m_selectionTab->currentIndex() == 0) { // Get the fipids for the selected bank QListWidgetItem* item = m_listFi->currentItem(); if (item && item->isSelected()) { QString bank = item->text(); m_textDetails->clear(); m_textDetails->append(QString("

Details for %1:

").arg(bank)); QStringList fipids = OfxPartner::FipidForBank(bank); QStringList::const_iterator it_fipid = fipids.constBegin(); while (it_fipid != fipids.constEnd()) { // For each fipid, get the connection details info = OfxPartner::ServiceInfo(*it_fipid); // Print them to the text browser QString message = QString("

Fipid: %1
").arg(*it_fipid); // If the bank supports retrieving statements - if (info.accountlist) { - m_bankInfo.push_back(info); + if (info.ofxInfo.accountlist) { + m_bankInfo.push_back(info.ofxInfo); + + message += QString("URL: %1
Org: %2
Fid: %3
").arg(info.ofxInfo.url, info.ofxInfo.org, info.ofxInfo.fid); - message += QString("URL: %1
Org: %2
Fid: %3
").arg(info.url, info.org, info.fid); - if (info.statements) + if (info.ofxInfo.statements) message += i18n("Supports online statements
"); - if (info.investments) + if (info.ofxInfo.investments) message += i18n("Supports investments
"); - if (info.billpay) + if (info.ofxInfo.billpay) message += i18n("Supports bill payment (but not supported by KMyMoney yet)
"); + + QString problemMessage; + if (!info.ofxValidated) + problemMessage += i18n("OFX host failed. Last successful access was on '%1'. ").arg(info.lastOfxValidated); + if (!info.sslValidated) + problemMessage += i18n("Certificate verification of OFX host failed. Last successful verification was on '%1'.").arg(info.lastSslValidated); + + if (!problemMessage.isEmpty()) { + m_problemMessages->setText(problemMessage); + m_problemMessages->animatedShow(); + } + } else { message += i18n("Does not support online banking"); } message += "

"; m_textDetails->append(message); ++it_fipid; } result = true; } else // error! No current item KMessageBox::sorry(this, i18n("Please choose a bank.")); } else { // manual entry of values if (m_fid->text().isEmpty() || m_url->url().isEmpty() || m_bankName->text().isEmpty()) { KMessageBox::sorry(this, i18n("Please fill all fields with values.")); } m_textDetails->clear(); m_textDetails->append(i18n("

Details for %1:

", m_bankName->text())); - memset(&info, 0, sizeof(OfxFiServiceInfo)); - strncpy(info.fid, m_fid->text().toLatin1(), OFX_FID_LENGTH - 1); - strncpy(info.org, m_bankName->text().toLatin1(), OFX_ORG_LENGTH - 1); - strncpy(info.url, m_url->url().url().toLatin1(), OFX_URL_LENGTH - 1); - info.accountlist = 1; - info.statements = 1; - info.billpay = 1; - info.investments = 1; + memset(&info.ofxInfo, 0, sizeof(OfxFiServiceInfo)); + strncpy(info.ofxInfo.fid, m_fid->text().toLatin1(), OFX_FID_LENGTH - 1); + strncpy(info.ofxInfo.org, m_bankName->text().toLatin1(), OFX_ORG_LENGTH - 1); + strncpy(info.ofxInfo.url, m_url->url().url().toLatin1(), OFX_URL_LENGTH - 1); + info.ofxInfo.accountlist = 1; + info.ofxInfo.statements = 1; + info.ofxInfo.billpay = 1; + info.ofxInfo.investments = 1; - m_bankInfo.push_back(info); + m_bankInfo.push_back(info.ofxInfo); QString message; - message += QString("

URL: %1
Org: %2
Fid: %3
").arg(info.url, info.org, info.fid); - if (info.statements) + message += QString("

URL: %1
Org: %2
Fid: %3
").arg(info.ofxInfo.url, info.ofxInfo.org, info.ofxInfo.fid); + if (info.ofxInfo.statements) message += i18n("Supports online statements
"); - if (info.investments) + if (info.ofxInfo.investments) message += i18n("Supports investments
"); - if (info.billpay) + if (info.ofxInfo.billpay) message += i18n("Supports bill payment (but not supported by KMyMoney yet)
"); message += "

"; m_textDetails->append(message); result = true; } // make sure to display the beginning of the collected information m_textDetails->moveCursor(QTextCursor::Start); return result; } bool KOnlineBankingSetupWizard::finishLoginPage() { bool result = true; QString username = m_editUsername->text(); QString password = m_editPassword->text(); QString clientUid = m_editClientUid->text(); m_listAccount->clear(); // Process an account request for each fipid m_it_info = m_bankInfo.constBegin(); while (m_it_info != m_bankInfo.constEnd()) { OfxFiLogin fi; memset(&fi, 0, sizeof(OfxFiLogin)); Q_ASSERT(sizeof(fi.fid) == sizeof((*m_it_info).fid)); Q_ASSERT(sizeof(fi.org) == sizeof((*m_it_info).org)); memcpy(fi.fid, (*m_it_info).fid, OFX_FID_LENGTH - 1); memcpy(fi.org, (*m_it_info).org, OFX_ORG_LENGTH - 1); strncpy(fi.userid, username.toLatin1().left(OFX_USERID_LENGTH - 1), OFX_USERID_LENGTH - 1); strncpy(fi.userpass, password.toLatin1(), OFX_USERPASS_LENGTH - 1); #ifdef LIBOFX_HAVE_CLIENTUID strncpy(fi.clientuid, clientUid.toLatin1(), OFX_CLIENTUID_LENGTH - 1); #endif // pretend we're Quicken 2008 // http://ofxblog.wordpress.com/2007/06/06/ofx-appid-and-appver-for-intuit-products/ // http://ofxblog.wordpress.com/2007/06/06/ofx-appid-and-appver-for-microsoft-money/ QString appId = m_appId->appId(); QRegExp exp("(.*):(.*)"); if (exp.indexIn(appId) != -1) { strncpy(fi.appid, exp.cap(1).toLatin1(), OFX_APPID_LENGTH - 1); if (exp.cap(2).isEmpty()) { strncpy(fi.appver, m_applicationEdit->text().toLatin1(), OFX_APPVER_LENGTH - 1); } else { strncpy(fi.appver, exp.cap(2).toLatin1(), OFX_APPVER_LENGTH - 1); } } else { strncpy(fi.appid, "QWIN", OFX_APPID_LENGTH - 1); strncpy(fi.appver, "1700", OFX_APPVER_LENGTH - 1); } QString hver = m_headerVersion->headerVersion(); strncpy(fi.header_version, hver.toLatin1(), OFX_HEADERVERSION_LENGTH - 1); QUrl filename(QString("file://%1response.ofx").arg(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1Char('/'))); QByteArray req(libofx_request_accountinfo(&fi)); // because the event loop is running while the request is performed disable the back button // (this function is not reentrant so the application might crash when back/next are used) QAbstractButton *backButton = button(QWizard::BackButton); bool backButtonState = backButton->isEnabled(); backButton->setEnabled(false); OfxHttpRequest(QString("POST"), QUrl((*m_it_info).url), req, QMap(), filename, false); backButton->setEnabled(backButtonState); LibofxContextPtr ctx = libofx_get_new_context(); Q_CHECK_PTR(ctx); ofx_set_account_cb(ctx, ofxAccountCallback, this); ofx_set_status_cb(ctx, ofxStatusCallback, this); // Add resulting accounts to the account list libofx_proc_file(ctx, filename.toLocalFile().toLatin1(), AUTODETECT); libofx_free_context(ctx); ++m_it_info; } if (! m_listAccount->topLevelItem(0)) { KMessageBox::sorry(this, i18n("No suitable accounts were found at this bank.")); result = false; } else { m_listAccount->resizeColumnToContents(0); m_listAccount->resizeColumnToContents(1); m_listAccount->resizeColumnToContents(2); m_listAccount->resizeColumnToContents(3); } return result; } bool KOnlineBankingSetupWizard::finishAccountPage() { bool result = true; if (! m_listAccount->currentItem()) { KMessageBox::sorry(this, i18n("Please choose an account")); result = false; } return result; } int KOnlineBankingSetupWizard::ofxAccountCallback(struct OfxAccountData data, void * pv) { KOnlineBankingSetupWizard* pthis = reinterpret_cast(pv); // Put the account info in the view MyMoneyKeyValueContainer kvps; if (data.account_type_valid) { QString type; switch (data.account_type) { case OfxAccountData::OFX_CHECKING: /**< A standard checking account */ type = "CHECKING"; break; case OfxAccountData::OFX_SAVINGS: /**< A standard savings account */ type = "SAVINGS"; break; case OfxAccountData::OFX_MONEYMRKT: /**< A money market account */ type = "MONEY MARKET"; break; case OfxAccountData::OFX_CREDITLINE: /**< A line of credit */ type = "CREDIT LINE"; break; case OfxAccountData::OFX_CMA: /**< Cash Management Account */ type = "CMA"; break; case OfxAccountData::OFX_CREDITCARD: /**< A credit card account */ type = "CREDIT CARD"; break; case OfxAccountData::OFX_INVESTMENT: /**< An investment account */ type = "INVESTMENT"; break; default: break; } kvps.setValue("type", type); } if (data.bank_id_valid) kvps.setValue("bankid", data.bank_id); if (data.broker_id_valid) kvps.setValue("bankid", data.broker_id); if (data.branch_id_valid) kvps.setValue("branchid", data.branch_id); if (data.account_number_valid) kvps.setValue("accountid", data.account_number); if (data.account_id_valid) kvps.setValue("uniqueId", data.account_id); kvps.setValue("username", pthis->m_editUsername->text()); kvps.setValue("password", pthis->m_editPassword->text()); #ifdef LIBOFX_HAVE_CLIENTUID kvps.setValue("clientUid", pthis->m_editClientUid->text()); #endif kvps.setValue("url", (*(pthis->m_it_info)).url); kvps.setValue("fid", (*(pthis->m_it_info)).fid); kvps.setValue("org", (*(pthis->m_it_info)).org); kvps.setValue("fipid", ""); QListWidgetItem* item = pthis->m_listFi->currentItem(); if (item) kvps.setValue("bankname", item->text()); // I removed the bankid here, because for some users it // was not possible to setup the automatic account matching // because the bankid was left empty here as well during // the statement download. In case we don't have it, we // simply use it blank. (ipwizard 2009-06-21) if (/* !kvps.value("bankid").isEmpty() && */ !kvps.value("uniqueId").isEmpty()) { kvps.setValue("kmmofx-acc-ref", QString("%1-%2").arg(kvps.value("bankid"), kvps.value("uniqueId"))); } else { qDebug("Cannot setup kmmofx-acc-ref for '%s'", qPrintable(kvps.value("bankname"))); } kvps.setValue("protocol", "OFX"); new ListViewItem(pthis->m_listAccount, kvps); return 0; } int KOnlineBankingSetupWizard::ofxStatusCallback(struct OfxStatusData data, void * pv) { KOnlineBankingSetupWizard* pthis = reinterpret_cast(pv); QString message; if (data.code_valid) { message += QString("#%1 %2: \"%3\"\n").arg(data.code).arg(data.name, data.description); } if (data.server_message_valid) { message += i18n("Server message: %1\n", data.server_message); } if (data.severity_valid) { switch (data.severity) { case OfxStatusData::INFO : break; case OfxStatusData::WARN : KMessageBox::detailedError(pthis, i18n("Your bank returned warnings when signing on"), i18nc("Warning 'message'", "WARNING %1", message)); break; case OfxStatusData::ERROR : KMessageBox::detailedError(pthis, i18n("Error signing onto your bank"), i18n("ERROR %1", message)); break; default: break; } } return 0; } bool KOnlineBankingSetupWizard::chosenSettings(MyMoneyKeyValueContainer& settings) { bool result = false;; if (m_fDone) { QTreeWidgetItem* qitem = m_listAccount->currentItem(); ListViewItem* item = dynamic_cast(qitem); if (item && item->isSelected()) { settings = *item; settings.deletePair("appId"); settings.deletePair("kmmofx-headerVersion"); QString appId = m_appId->appId(); if (!appId.isEmpty()) { if (appId.endsWith(':')) { appId += m_applicationEdit->text(); } settings.setValue("appId", appId); } QString hVer = m_headerVersion->headerVersion(); if (!hVer.isEmpty()) settings.setValue("kmmofx-headerVersion", hVer); if (m_storePassword->isChecked()) { if (d->m_walletIsOpen) { QString key = OFX_PASSWORD_KEY(settings.value("url"), settings.value("uniqueId")); d->m_wallet->writePassword(key, settings.value("password")); settings.deletePair("password"); } } else { settings.deletePair("password"); } result = true; } } return result; } KOnlineBankingSetupWizard::ListViewItem::ListViewItem(QTreeWidget* parent, const MyMoneyKeyValueContainer& kvps): MyMoneyKeyValueContainer(kvps), QTreeWidgetItem(parent) { setText(0, value("accountid")); setText(1, value("type")); setText(2, value("bankid")); setText(3, value("branchid")); } diff --git a/kmymoney/plugins/ofx/import/dialogs/konlinebankingsetupwizard.ui b/kmymoney/plugins/ofx/import/dialogs/konlinebankingsetupwizard.ui index 72f845b5d..03eebf7fb 100644 --- a/kmymoney/plugins/ofx/import/dialogs/konlinebankingsetupwizard.ui +++ b/kmymoney/plugins/ofx/import/dialogs/konlinebankingsetupwizard.ui @@ -1,416 +1,453 @@ KOnlineBankingSetupWizard 0 0 595 560 Online Banking Account Setup Select Financial Institution - + + 0 + + + 0 + + + 0 + + 0 Please select your financial institution from the list below... Qt::AlignVCenter true 0 Automatic - + + 0 + + + 0 + + + 0 + + 0 Manual - + + 0 + + + 0 + + + 0 + + 0 Org false FID false URL false Qt::Vertical QSizePolicy::Expanding 20 90 Enter Login Details - + + 0 + + + 0 + + + 0 + + 0 Qt::NoFocus Please enter the username and password you use to log into this bank for online banking. Please note that many banks require a separate signup, and assign a separate PIN or password just for online banking from home. true Username false Password false Store password Identify as false Expert option to enter the four digit version information of the application Qt::Horizontal QSizePolicy::Expanding 40 20 Header Version false Qt::Horizontal 40 20 - - - - Connection Details - - - false - - - - + true Qt::NoFocus Client UID + + + + Connection Details + + + false + + + + + + Select Account - + + 0 + + + 0 + + + 0 + + 0 Please select the account from your financial institution from the list below which matches this account. Qt::AlignVCenter true Number Type Bank Branch Finish Congratulations: you have successfully set up your bank for online banking via OFX. Qt::AlignVCenter true - - QTextBrowser - QTextBrowser -
ktextbrowser.h
-
- - KComboBox - QComboBox -
kcombobox.h
-
KLineEdit QLineEdit
klineedit.h
- QListWidget - QListWidget -
klistwidget.h
+ KUrlRequester + QWidget +
kurlrequester.h
- KUrlRequester + KMessageWidget QFrame -
kurlrequester.h
+
kmessagewidget.h
- QTabWidget - QTabWidget -
ktabwidget.h
- 1 + KComboBox + QComboBox +
kcombobox.h
m_selectionTab m_listFi m_bankName m_fid m_url m_editUsername m_editPassword m_storePassword m_applicationCombo m_headerVersionCombo m_listAccount
diff --git a/kmymoney/plugins/ofx/import/ofxpartner.cpp b/kmymoney/plugins/ofx/import/ofxpartner.cpp index 745432c35..f402a3bad 100644 --- a/kmymoney/plugins/ofx/import/ofxpartner.cpp +++ b/kmymoney/plugins/ofx/import/ofxpartner.cpp @@ -1,395 +1,409 @@ /*************************************************************************** ofxpartner.cpp ---------- begin : Fri Jan 23 2009 copyright : (C) 2009 by Thomas Baumgart email : Thomas Baumgart ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include #include "ofxpartner.h" #include "kmymoneysettings.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include #include #include #include // ---------------------------------------------------------------------------- // Some standard defined stuff collides with libofx.h #ifdef Q_CC_MSVC #undef ERROR #undef DELETE #endif // ---------------------------------------------------------------------------- // Project Includes namespace OfxPartner { bool post(const QString& request, const QMap& attr, const QUrl &url, const QUrl& filename); bool get(const QString& request, const QMap& attr, const QUrl &url, const QUrl& filename); const QString kBankFilename = "ofx-bank-index.xml"; const QString kCcFilename = "ofx-cc-index.xml"; const QString kInvFilename = "ofx-inv-index.xml"; #define VER "9" static QString directory; void setDirectory(const QString& dir) { directory = dir; } bool needReload(const QFileInfo& i) { return ((!i.isReadable()) || (i.lastModified().addDays(7) < QDateTime::currentDateTime()) || (i.size() < 1024)); } void ValidateIndexCache() { // TODO (Ace) Check whether these files exist and are recent enough before getting them again QUrl fname; QMap attr; fname = QUrl("file://" + directory + kBankFilename); QDir dir; dir.mkpath(directory); QFileInfo i(fname.toLocalFile()); if (needReload(i)) get("", attr, QUrl(QStringLiteral("http://www.ofxhome.com/api.php?all=yes")), fname); } static void ParseFile(QMap& result, const QString& fileName, const QString& bankName) { QFile f(fileName); if (f.open(QIODevice::ReadOnly)) { QTextStream stream(&f); stream.setCodec("UTF-8"); QString msg; int errl, errc; QDomDocument doc; if (doc.setContent(stream.readAll(), &msg, &errl, &errc)) { QDomNodeList olist = doc.elementsByTagName("institutionid"); for (int i = 0; i < olist.count(); ++i) { QDomNode onode = olist.item(i); if (onode.isElement()) { QDomElement elo = onode.toElement(); QString name = elo.attribute("name"); if (bankName.isEmpty()) result[name].clear(); else if (name == bankName) { result[elo.attribute("id")].clear(); } } } } f.close(); } } QStringList BankNames() { QMap result; // Make sure the index files are up to date ValidateIndexCache(); ParseFile(result, directory + kBankFilename, QString()); // Add Innovision result["Innovision"].clear(); return QStringList() << result.keys(); } QStringList FipidForBank(const QString& bank) { QMap result; ParseFile(result, directory + kBankFilename, bank); // the fipid for Innovision is 1. if (bank == "Innovision") result["1"].clear(); return QStringList() << result.keys(); } QString extractNodeText(QDomElement& node, const QString& name) { QString res; QRegExp exp("([^/]+)/?([^/].*)?"); if (exp.indexIn(name) != -1) { QDomNodeList olist = node.elementsByTagName(exp.cap(1)); if (olist.count()) { QDomNode onode = olist.item(0); if (onode.isElement()) { QDomElement elo = onode.toElement(); if (exp.cap(2).isEmpty()) { res = elo.text(); } else { res = extractNodeText(elo, exp.cap(2)); } } } } return res; } QString extractNodeText(QDomDocument& doc, const QString& name) { QString res; QRegExp exp("([^/]+)/?([^/].*)?"); if (exp.indexIn(name) != -1) { QDomNodeList olist = doc.elementsByTagName(exp.cap(1)); if (olist.count()) { QDomNode onode = olist.item(0); if (onode.isElement()) { QDomElement elo = onode.toElement(); if (exp.cap(2).isEmpty()) { res = elo.text(); } else { res = extractNodeText(elo, exp.cap(2)); } } } } return res; } -OfxFiServiceInfo ServiceInfo(const QString& fipid) +OfxHomeServiceInfo ServiceInfo(const QString& fipid) { - OfxFiServiceInfo result; - memset(&result, 0, sizeof(OfxFiServiceInfo)); + OfxHomeServiceInfo result; + memset(&result.ofxInfo, 0, sizeof(result.ofxInfo)); + result.ofxValidated = true; + result.sslValidated = true; + result.lastOfxValidated = QDate::currentDate().toString(); + result.lastSslValidated = result.lastOfxValidated; // Hard-coded values for Innovision test server if (fipid == "1") { - strncpy(result.fid, "00000", OFX_FID_LENGTH - 1); - strncpy(result.org, "ReferenceFI", OFX_ORG_LENGTH - 1); - strncpy(result.url, "http://ofx.innovision.com", OFX_URL_LENGTH - 1); - result.accountlist = 1; - result.statements = 1; - result.billpay = 1; - result.investments = 1; + strncpy(result.ofxInfo.fid, "00000", OFX_FID_LENGTH - 1); + strncpy(result.ofxInfo.org, "ReferenceFI", OFX_ORG_LENGTH - 1); + strncpy(result.ofxInfo.url, "http://ofx.innovision.com", OFX_URL_LENGTH - 1); + result.ofxInfo.accountlist = 1; + result.ofxInfo.statements = 1; + result.ofxInfo.billpay = 1; + result.ofxInfo.investments = 1; return result; } QMap attr; QUrl guidFile(QString("file://%1fipid-%2.xml").arg(directory).arg(fipid)); QFileInfo i(guidFile.toLocalFile()); if (!i.isReadable() || i.lastModified().addDays(7) < QDateTime::currentDateTime()) get("", attr, QUrl(QString("http://www.ofxhome.com/api.php?lookup=%1").arg(fipid)), guidFile); QFile f(guidFile.toLocalFile()); if (f.open(QIODevice::ReadOnly)) { QTextStream stream(&f); stream.setCodec("UTF-8"); QString msg; int errl, errc; QDomDocument doc; if (doc.setContent(stream.readAll(), &msg, &errl, &errc)) { - QString fid = extractNodeText(doc, "institution/fid"); - QString org = extractNodeText(doc, "institution/org"); - QString url = extractNodeText(doc, "institution/url"); - strncpy(result.fid, fid.toLatin1(), OFX_FID_LENGTH - 1); - strncpy(result.org, org.toLatin1(), OFX_ORG_LENGTH - 1); - strncpy(result.url, url.toLatin1(), OFX_URL_LENGTH - 1); - - result.accountlist = true; - result.statements = true; - result.billpay = false; - result.investments = true; + const auto fid = extractNodeText(doc, "institution/fid"); + const auto org = extractNodeText(doc, "institution/org"); + const auto url = extractNodeText(doc, "institution/url"); + result.ofxValidated = (extractNodeText(doc, "institution/ofxfail").toUInt() == 0); + result.sslValidated = (extractNodeText(doc, "institution/sslfail").toUInt() == 0); + result.lastOfxValidated = extractNodeText(doc, "institution/lastofxvalidation"); + result.lastSslValidated = extractNodeText(doc, "institution/lastsslvalidation"); + + strncpy(result.ofxInfo.fid, fid.toLatin1(), OFX_FID_LENGTH - 1); + strncpy(result.ofxInfo.org, org.toLatin1(), OFX_ORG_LENGTH - 1); + strncpy(result.ofxInfo.url, url.toLatin1(), OFX_URL_LENGTH - 1); + + result.ofxInfo.accountlist = true; + result.ofxInfo.statements = true; + result.ofxInfo.billpay = false; + result.ofxInfo.investments = true; } } else { + memset(&result.ofxInfo, 0, sizeof(result.ofxInfo)); + result.ofxValidated = false; + result.sslValidated = false; + result.lastOfxValidated.clear(); + result.lastSslValidated.clear(); qDebug() << "OFX ServiceInfo:" << f.errorString(); } return result; } bool get(const QString& request, const QMap& attr, const QUrl &url, const QUrl& filename) { Q_UNUSED(request); QByteArray req; OfxHttpRequest job("GET", url, req, attr, filename, false); return job.error() == 0; } bool post(const QString& request, const QMap& attr, const QUrl &url, const QUrl& filename) { QByteArray req(request.toUtf8()); OfxHttpRequest job("POST", url, req, attr, filename, false); return job.error() == 0; } } // namespace OfxPartner class OfxHttpRequest::Private { public: QFile m_fpTrace; }; OfxHttpRequest::OfxHttpRequest(const QString& type, const QUrl &url, const QByteArray &postData, const QMap& metaData, const QUrl& dst, bool showProgressInfo) : d(new Private) , m_dst(dst) , m_error(-1) , m_postJob(0) , m_getJob(0) { m_eventLoop = new QEventLoop(qApp->activeWindow()); if (KMyMoneySettings::logOfxTransactions()) { QString logPath = KMyMoneySettings::logPath(); d->m_fpTrace.setFileName(QString("%1/ofxlog.txt").arg(logPath)); d->m_fpTrace.open(QIODevice::WriteOnly | QIODevice::Append); } KIO::JobFlag jobFlags = KIO::DefaultFlags; if (!showProgressInfo) jobFlags = KIO::HideProgressInfo; KIO::Job* job; if(type.toLower() == QStringLiteral("get")) { job = m_getJob = KIO::copy(url, dst, jobFlags); } else { job = m_postJob = KIO::http_post(url, postData, jobFlags); m_postJob->addMetaData("content-type", "Content-type: application/x-ofx"); m_postJob->addMetaData(metaData); connect(job, SIGNAL(data(KIO::Job*,QByteArray)), this, SLOT(slotOfxData(KIO::Job*,QByteArray))); connect(job, SIGNAL(connected(KIO::Job*)), this, SLOT(slotOfxConnected(KIO::Job*))); } if (d->m_fpTrace.isOpen()) { QTextStream ts(&d->m_fpTrace); ts << "url: " << url.toDisplayString() << "\n"; ts << "request:\n" << QString(postData) << "\n" << "response:\n"; } connect(job, SIGNAL(result(KJob*)), this, SLOT(slotOfxFinished(KJob*))); job->start(); qDebug("Starting eventloop"); if (m_eventLoop) m_eventLoop->exec(); qDebug("Ending eventloop"); } OfxHttpRequest::~OfxHttpRequest() { delete m_eventLoop; if (d->m_fpTrace.isOpen()) { d->m_fpTrace.close(); } delete d; } void OfxHttpRequest::slotOfxConnected(KIO::Job*) { qDebug() << "OfxHttpRequest::slotOfxConnected" << m_dst.toLocalFile(); m_file.setFileName(m_dst.toLocalFile()); m_file.open(QIODevice::WriteOnly); } void OfxHttpRequest::slotOfxData(KIO::Job*, const QByteArray& _ba) { if (m_file.isOpen()) { m_file.write(_ba); if (d->m_fpTrace.isOpen()) { d->m_fpTrace.write(_ba); } } } void OfxHttpRequest::slotOfxFinished(KJob* /* e */) { if (m_file.isOpen()) { m_file.close(); if (d->m_fpTrace.isOpen()) { d->m_fpTrace.write("\nCompleted\n\n\n\n", 14); } } if(m_postJob) { m_error = m_postJob->error(); if (m_error) { m_postJob->uiDelegate()->showErrorMessage(); QFile::remove(m_dst.toLocalFile()); } else if (m_postJob->isErrorPage()) { QString details; QFile f(m_dst.toLocalFile()); if (f.open(QIODevice::ReadOnly)) { QTextStream stream(&f); QString line; while (!stream.atEnd()) { details += stream.readLine(); // line of text excluding '\n' } f.close(); } KMessageBox::detailedSorry(0, i18n("The HTTP request failed."), details, i18nc("The HTTP request failed", "Failed")); QFile::remove(m_dst.toLocalFile()); } } else if(m_getJob) { m_error = m_getJob->error(); if (m_error) { m_getJob->uiDelegate()->showErrorMessage(); QFile::remove(m_dst.toLocalFile()); } } qDebug("Finishing eventloop"); if (m_eventLoop) m_eventLoop->exit(); } diff --git a/kmymoney/plugins/ofx/import/ofxpartner.h b/kmymoney/plugins/ofx/import/ofxpartner.h index 63f00d172..c964d2d17 100644 --- a/kmymoney/plugins/ofx/import/ofxpartner.h +++ b/kmymoney/plugins/ofx/import/ofxpartner.h @@ -1,94 +1,102 @@ /*************************************************************************** ofxpartner.h ---------- begin : Fri Jan 23 2009 copyright : (C) 2009 by Thomas Baumgart email : Thomas Baumgart ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef OFXPARTNER_H #define OFXPARTNER_H // ---------------------------------------------------------------------------- // QT Includes #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include class KJob; class QEventLoop; namespace KIO { class Job; class TransferJob; } // ---------------------------------------------------------------------------- // Project Includes #include +struct OfxHomeServiceInfo { + OfxFiServiceInfo ofxInfo; + bool ofxValidated; + bool sslValidated; + QString lastOfxValidated; + QString lastSslValidated; +}; + namespace OfxPartner { /** * setup the directory where the files will be stored. * @a dir must end with a '/' and must exist. Call this * before any other of the functions of OfxPartner. The * default will be to store the files in the current * directory. */ void setDirectory(const QString& dir); void ValidateIndexCache(); -OfxFiServiceInfo ServiceInfo(const QString& fipid); +OfxHomeServiceInfo ServiceInfo(const QString& fipid); QStringList BankNames(); QStringList FipidForBank(const QString& bank); } class OfxHttpRequest : public QObject { Q_OBJECT public: OfxHttpRequest(const QString& method, const QUrl &url, const QByteArray &postData, const QMap& metaData, const QUrl& dst, bool showProgressInfo = true); virtual ~OfxHttpRequest(); /** * returns the error code provided by KIO::TransferJob or * KIO::Job depending on post() or get() operation. If it * is not set by the actual operation, it returns -1. */ int error() const { return m_error; } protected Q_SLOTS: void slotOfxFinished(KJob*); void slotOfxData(KIO::Job*, const QByteArray&); void slotOfxConnected(KIO::Job*); private: class Private; Private* d; QUrl m_dst; QFile m_file; int m_error; KIO::TransferJob* m_postJob; KIO::Job* m_getJob; QPointer m_eventLoop; }; #endif // OFXPARTNER_H