diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2011-07-04 22:38:03 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2011-07-04 22:38:03 +0000 |
commit | dadc34655c3ab961b0b0b94a10eaaba710f0b5e8 (patch) | |
tree | 99e72842fe687baea16376a147619b6048d7e441 /kmymoney2/plugins | |
download | kmymoney-dadc34655c3ab961b0b0b94a10eaaba710f0b5e8.tar.gz kmymoney-dadc34655c3ab961b0b0b94a10eaaba710f0b5e8.zip |
Added kmymoney
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/kmymoney@1239792 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kmymoney2/plugins')
39 files changed, 5724 insertions, 0 deletions
diff --git a/kmymoney2/plugins/Makefile.am b/kmymoney2/plugins/Makefile.am new file mode 100644 index 0000000..9b67654 --- /dev/null +++ b/kmymoney2/plugins/Makefile.am @@ -0,0 +1,24 @@ +KDE_OPTIONS = noautodist + +METASOURCES = AUTO +INCLUDES = $(all_includes) -I$(top_srcdir) -I. + +# since some of the subdirs are conditional, we need to define DIST_SUBDIRS +SUBDIRS = interfaces @OFX_IMPORTERPLUGIN@ +DIST_SUBDIRS = interfaces ofximport + +# The library containing the plugin base class +lib_LTLIBRARIES = libkmm_plugin.la +libkmm_plugin_la_SOURCES = kmymoneyplugin.cpp pluginloader.cpp viewinterface.cpp statementinterface.cpp importinterface.cpp +libkmm_plugin_la_LDFLAGS = $(all_libraries) -version-info 0:0:0 + +#definition of the service type +kde_servicetypes_DATA = kmymoneyplugin.desktop kmymoneyimporterplugin.desktop + +# make sure this lib is build before any subdirectory +BUILT_SOURCES = libkmm_plugin.la + +pluginsincludedir = $(includedir)/kmymoney +pluginsinclude_HEADERS = kmymoneyplugin.h pluginloader.h viewinterface.h statementinterface.h importinterface.h + +EXTRA_DIST = kmymoneyplugin.desktop kmymoneyimporterplugin.desktop diff --git a/kmymoney2/plugins/importinterface.cpp b/kmymoney2/plugins/importinterface.cpp new file mode 100644 index 0000000..ce867c8 --- /dev/null +++ b/kmymoney2/plugins/importinterface.cpp @@ -0,0 +1,34 @@ +/*************************************************************************** + importinterface.cpp + ------------------- + begin : Mon Apr 14 2008 + copyright : (C) 2008 Thomas Baumgart + email : ipwizard@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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "importinterface.h" + +KMyMoneyPlugin::ImportInterface::ImportInterface(QObject* parent, const char* name) : + QObject(parent, name) +{ +} + +#include "importinterface.moc" diff --git a/kmymoney2/plugins/importinterface.h b/kmymoney2/plugins/importinterface.h new file mode 100644 index 0000000..4b8a347 --- /dev/null +++ b/kmymoney2/plugins/importinterface.h @@ -0,0 +1,63 @@ +/*************************************************************************** + importinterface.h + ------------------- + begin : Mon Apr 14 2008 + copyright : (C) 2008 Thomas Baumgart + email : ipwizard@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. * + * * + ***************************************************************************/ + +#ifndef IMPORTINTERFACE_H +#define IMPORTINTERFACE_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qobject.h> +#include <qstring.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <kfile.h> +#include <kurl.h> +class KPopupMenu; + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/export.h> + +namespace KMyMoneyPlugin { + +/** + * This abstract class represents the ImportInterface to + * add new importers to KMyMoney. + */ +class KMYMONEY_EXPORT ImportInterface : public QObject +{ + Q_OBJECT + +public: + ImportInterface(QObject* parent, const char* name = 0); + ~ImportInterface() {} + + virtual KURL selectFile(const QString& title, const QString& path, const QString& mask, KFile::Mode mode) const = 0; + +signals: +}; + +}; // namespace +#endif diff --git a/kmymoney2/plugins/interfaces/Makefile.am b/kmymoney2/plugins/interfaces/Makefile.am new file mode 100644 index 0000000..2b3c761 --- /dev/null +++ b/kmymoney2/plugins/interfaces/Makefile.am @@ -0,0 +1,13 @@ +KDE_OPTIONS = noautodist + +METASOURCES = AUTO +INCLUDES = $(all_includes) -I$(top_srcdir) -I. -I$(top_srcdir)/kmymoney2/widgets + +SUBDIRS = + +noinst_LIBRARIES = libinterfaces.a + +libinterfaces_a_SOURCES = kmmviewinterface.cpp kmmstatementinterface.cpp kmmimportinterface.cpp + +noinst_HEADERS = kmmviewinterface.h kmmstatementinterface.h kmmimportinterface.h + diff --git a/kmymoney2/plugins/interfaces/kmmimportinterface.cpp b/kmymoney2/plugins/interfaces/kmmimportinterface.cpp new file mode 100644 index 0000000..b3c54d8 --- /dev/null +++ b/kmymoney2/plugins/interfaces/kmmimportinterface.cpp @@ -0,0 +1,41 @@ +/*************************************************************************** + kmmimportinterface.cpp + ------------------- + begin : Mon Apr 14 2008 + copyright : (C) 2008 Thomas Baumgart + email : ipwizard@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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "../../kmymoney2.h" +#include "kmmimportinterface.h" + +KMyMoneyPlugin::KMMImportInterface::KMMImportInterface(KMyMoney2App* app, QObject* parent, const char* name) : + ImportInterface(parent, name), + m_app(app) +{ +} + +KURL KMyMoneyPlugin::KMMImportInterface::selectFile(const QString& title, const QString& path, const QString& mask, KFile::Mode mode) const +{ + return m_app->selectFile(title, path, mask, mode); +} + +#include "kmmimportinterface.moc" diff --git a/kmymoney2/plugins/interfaces/kmmimportinterface.h b/kmymoney2/plugins/interfaces/kmmimportinterface.h new file mode 100644 index 0000000..484a228 --- /dev/null +++ b/kmymoney2/plugins/interfaces/kmmimportinterface.h @@ -0,0 +1,60 @@ +/*************************************************************************** + kmmimportinterface.h + ------------------- + begin : Mon Apr 14 2008 + copyright : (C) 2008 Thomas Baumgart + email : ipwizard@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. * + * * + ***************************************************************************/ + +#ifndef KMMIMPORTINTERFACE_H +#define KMMIMPORTINTERFACE_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +// ---------------------------------------------------------------------------- +// QT Includes + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <kfile.h> +#include <kurl.h> +class KMyMoney2App; + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "../importinterface.h" + +namespace KMyMoneyPlugin { + +/** + * This class represents the implementation of the + * ViewInterface. + */ +class KMMImportInterface : public ImportInterface { + Q_OBJECT + +public: + KMMImportInterface(KMyMoney2App* app, QObject* parent, const char* name = 0); + ~KMMImportInterface() {} + + KURL selectFile(const QString& title, const QString& path, const QString& mask, KFile::Mode mode) const; + +private: + KMyMoney2App* m_app; +}; + +}; // namespace +#endif diff --git a/kmymoney2/plugins/interfaces/kmmstatementinterface.cpp b/kmymoney2/plugins/interfaces/kmmstatementinterface.cpp new file mode 100644 index 0000000..9e09db2 --- /dev/null +++ b/kmymoney2/plugins/interfaces/kmmstatementinterface.cpp @@ -0,0 +1,56 @@ +/*************************************************************************** + kmmstatementinterface.cpp + ------------------- + begin : Wed Jan 5 2005 + copyright : (C) 2005 Thomas Baumgart + email : ipwizard@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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qstring.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "kmmstatementinterface.h" +#include "../../kmymoney2.h" +#include <kmymoney/mymoneyaccount.h> +#include <kmymoney/mymoneykeyvaluecontainer.h> + +KMyMoneyPlugin::KMMStatementInterface::KMMStatementInterface(KMyMoney2App* app, QObject* parent, const char* name) : + StatementInterface(parent, name), + m_app(app) +{ +} + +bool KMyMoneyPlugin::KMMStatementInterface::import(const MyMoneyStatement& s) +{ + qDebug("KMyMoneyPlugin::KMMStatementInterface::import start"); + return m_app->slotStatementImport(s); +} + +const MyMoneyAccount& KMyMoneyPlugin::KMMStatementInterface::account(const QString& key, const QString& value) const +{ + return m_app->account(key, value); +} + +void KMyMoneyPlugin::KMMStatementInterface::setAccountOnlineParameters(const MyMoneyAccount& acc, const MyMoneyKeyValueContainer& kvps) const +{ + m_app->setAccountOnlineParameters(acc, kvps); +} + +#include "kmmstatementinterface.moc" diff --git a/kmymoney2/plugins/interfaces/kmmstatementinterface.h b/kmymoney2/plugins/interfaces/kmmstatementinterface.h new file mode 100644 index 0000000..7890002 --- /dev/null +++ b/kmymoney2/plugins/interfaces/kmmstatementinterface.h @@ -0,0 +1,77 @@ +/*************************************************************************** + kmmstatementinterface.h + ------------------- + begin : Wed Jan 5 2005 + copyright : (C) 2005 Thomas Baumgart + email : ipwizard@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. * + * * + ***************************************************************************/ + +#ifndef KMMSTATEMENTINTERFACE_H +#define KMMSTATEMENTINTERFACE_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +// ---------------------------------------------------------------------------- +// QT Includes + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +class KMyMoney2App; +class MyMoneyAccount; +class MyMoneyKeyValueContainer; + +#include "../statementinterface.h" + +namespace KMyMoneyPlugin { + +/** + * This class represents the implementation of the + * StatementInterface. + */ +class KMMStatementInterface : public StatementInterface +{ + Q_OBJECT + +public: + KMMStatementInterface(KMyMoney2App* app, QObject* parent, const char* name = 0); + ~KMMStatementInterface() {} + + /** + * This method imports a MyMoneyStatement into the engine + */ + bool import(const MyMoneyStatement& s); + + /** + * This method returns the account for a given @a key - @a value pair. + * If the account is not found in the list of accounts, MyMoneyAccount() + * is returned. + */ + const MyMoneyAccount& account(const QString& key, const QString& value) const; + + /** + * This method stores the online parameters in @a kvps used by the plugin + * with the account @a acc. + */ + void setAccountOnlineParameters(const MyMoneyAccount&acc, const MyMoneyKeyValueContainer& kvps) const; + +private: + KMyMoney2App* m_app; +}; + +}; // namespace +#endif diff --git a/kmymoney2/plugins/interfaces/kmmviewinterface.cpp b/kmymoney2/plugins/interfaces/kmmviewinterface.cpp new file mode 100644 index 0000000..9779876 --- /dev/null +++ b/kmymoney2/plugins/interfaces/kmmviewinterface.cpp @@ -0,0 +1,60 @@ +/*************************************************************************** + viewinterface.cpp + ------------------- + begin : Wed Jan 5 2005 + copyright : (C) 2005 Thomas Baumgart + email : ipwizard@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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "../../kmymoney2.h" +#include "../../views/kmymoneyview.h" +#include "../../widgets/selectedtransaction.h" +#include "kmmviewinterface.h" + +KMyMoneyPlugin::KMMViewInterface::KMMViewInterface(KMyMoney2App* app, KMyMoneyView* view, QObject* parent, const char* name) : + ViewInterface(parent, name), + m_app(app), + m_view(view) +{ + connect(app, SIGNAL(accountSelected(const MyMoneyAccount&)), this, SIGNAL(accountSelected(const MyMoneyAccount&))); + connect(app, SIGNAL(transactionsSelected(const KMyMoneyRegister::SelectedTransactions&)), this, SIGNAL(transactionsSelected(const KMyMoneyRegister::SelectedTransactions&))); + connect(app, SIGNAL(accountReconciled(const MyMoneyAccount&, const QDate&, const MyMoneyMoney&, const MyMoneyMoney&, const QValueList<QPair<MyMoneyTransaction, MyMoneySplit> >&)), + this, SIGNAL(accountReconciled(const MyMoneyAccount&, const QDate&, const MyMoneyMoney&, const MyMoneyMoney&, const QValueList<QPair<MyMoneyTransaction, MyMoneySplit> >&))); + + connect(app, SIGNAL(institutionSelected(const MyMoneyInstitution&)), this, SIGNAL(institutionSelected(const MyMoneyInstitution&))); + + connect(m_view, SIGNAL(viewStateChanged(bool)), this, SIGNAL(viewStateChanged(bool))); + connect(m_view, SIGNAL(kmmFilePlugin(unsigned int)), this, SIGNAL(kmmFilePlugin(unsigned int))); +} + +KMyMoneyViewBase* KMyMoneyPlugin::KMMViewInterface::addPage(const QString& item, const QString& icon) +{ + return m_view->addPage(item, icon); +} + +void KMyMoneyPlugin::KMMViewInterface::addWidget(KMyMoneyViewBase* view, QWidget* w) +{ + if(view && w) + view->addWidget(w); +} + + +#include "kmmviewinterface.moc" diff --git a/kmymoney2/plugins/interfaces/kmmviewinterface.h b/kmymoney2/plugins/interfaces/kmmviewinterface.h new file mode 100644 index 0000000..d06b4f8 --- /dev/null +++ b/kmymoney2/plugins/interfaces/kmmviewinterface.h @@ -0,0 +1,79 @@ +/*************************************************************************** + kmmviewinterface.h + ------------------- + begin : Wed Jan 5 2005 + copyright : (C) 2005 Thomas Baumgart + email : ipwizard@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. * + * * + ***************************************************************************/ + +#ifndef KMMVIEWINTERFACE_H +#define KMMVIEWINTERFACE_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +// ---------------------------------------------------------------------------- +// QT Includes + +// ---------------------------------------------------------------------------- +// KDE Includes + +class KMyMoney2App; +class KMyMoneyView; +class KMyMoneyViewBase; + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "../viewinterface.h" + +namespace KMyMoneyPlugin { + +/** + * This class represents the implementation of the + * ViewInterface. + */ +class KMMViewInterface : public ViewInterface { + Q_OBJECT + +public: + KMMViewInterface(KMyMoney2App* app, KMyMoneyView* view, QObject* parent, const char* name = 0); + ~KMMViewInterface() {} + + /** + * This method returns a pointer to a newly created view + * with title @p item and icon @p pixmap. + * + * @param item Name of view + * @param icon name for the icon to be used for the view + * + * @return pointer to KMyMoneyViewBase object + */ + KMyMoneyViewBase* addPage(const QString& item, const QString& icon); + + /** + * This method allows to add a widget to the view + * created with addPage() + * + * @param view pointer to view object + * @param w pointer to widget + */ + void addWidget(KMyMoneyViewBase* view, QWidget* w); + +private: + KMyMoney2App* m_app; + KMyMoneyView* m_view; +}; + +}; // namespace +#endif diff --git a/kmymoney2/plugins/kmymoneyimporterplugin.desktop b/kmymoney2/plugins/kmymoneyimporterplugin.desktop new file mode 100644 index 0000000..d8277e6 --- /dev/null +++ b/kmymoney2/plugins/kmymoneyimporterplugin.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Type=ServiceType +Name=KMyMoney Importer Plugin +X-KDE-ServiceType=KMyMoneyImporterPlugin +Comment=A KMyMoney plugin + diff --git a/kmymoney2/plugins/kmymoneyplugin.cpp b/kmymoney2/plugins/kmymoneyplugin.cpp new file mode 100644 index 0000000..ddca52a --- /dev/null +++ b/kmymoney2/plugins/kmymoneyplugin.cpp @@ -0,0 +1,91 @@ +/*************************************************************************** + kmymoneyplugin.cpp + ------------------- + begin : Wed Jan 5 2005 + copyright : (C) 2005 Thomas Baumgart + email : ipwizard@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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <kinstance.h> +#include <kaboutdata.h> +#include <kaction.h> + +// ---------------------------------------------------------------------------- +// Project Includes + + +#include "kmymoneyplugin.h" + +KMyMoneyPlugin::Plugin::Plugin(QObject* o, const char* name) : + QObject(o, name) +{ +} + +KMyMoneyPlugin::Plugin::~Plugin() +{ +} + +KAction* KMyMoneyPlugin::Plugin::action(const QString& actionName) const +{ + static KShortcut shortcut(""); + static KAction dummyAction(QString("Dummy"), QString(), shortcut, static_cast<const QObject*>(this), 0, static_cast<KActionCollection*>(0), ""); + + KAction* p = actionCollection()->action(actionName.latin1()); + if(p) + return p; + + qWarning("Action with name '%s' not found!", actionName.latin1()); + return &dummyAction; +} + +KToggleAction* KMyMoneyPlugin::Plugin::toggleAction(const QString& actionName) const +{ + static KShortcut shortcut(""); + static KToggleAction dummyAction(QString("Dummy"), QString(), shortcut, static_cast<const QObject*>(this), 0, static_cast<KActionCollection*>(0), ""); + + KAction* q = actionCollection()->action(actionName.latin1()); + + if(q) { + KToggleAction* p = dynamic_cast<KToggleAction*>(q); + if(!p) { + qWarning("Action '%s' is not of type KToggleAction", actionName.latin1()); + p = &dummyAction; + } + return p; + } + + qWarning("Action with name '%s' not found!", actionName.latin1()); + return &dummyAction; +} + +KMyMoneyPlugin::ViewInterface* KMyMoneyPlugin::Plugin::viewInterface() const +{ + return static_cast<ViewInterface*>( parent()->child( 0, "KMyMoneyPlugin::ViewInterface" ) ); +} + +KMyMoneyPlugin::StatementInterface* KMyMoneyPlugin::Plugin::statementInterface() const +{ + return static_cast<StatementInterface*>( parent()->child( 0, "KMyMoneyPlugin::StatementInterface" ) ); +} + +KMyMoneyPlugin::ImportInterface* KMyMoneyPlugin::Plugin::importInterface() const +{ + return static_cast<ImportInterface*>( parent()->child( 0, "KMyMoneyPlugin::ImportInterface" ) ); +} + +#include "kmymoneyplugin.moc" diff --git a/kmymoney2/plugins/kmymoneyplugin.desktop b/kmymoney2/plugins/kmymoneyplugin.desktop new file mode 100644 index 0000000..000b84a --- /dev/null +++ b/kmymoney2/plugins/kmymoneyplugin.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Type=ServiceType +Name=KMyMoney Plugin +X-KDE-ServiceType=KMyMoneyPlugin +Comment=A KMyMoney plugin + diff --git a/kmymoney2/plugins/kmymoneyplugin.h b/kmymoney2/plugins/kmymoneyplugin.h new file mode 100644 index 0000000..2978fde --- /dev/null +++ b/kmymoney2/plugins/kmymoneyplugin.h @@ -0,0 +1,214 @@ +/*************************************************************************** + kmymoneyplugin.h + ------------------- + begin : Wed Jan 5 2005 + copyright : (C) 2005 Thomas Baumgart + email : ipwizard@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. * + * * + ***************************************************************************/ + +#ifndef KMYMONEYPLUGIN_H +#define KMYMONEYPLUGIN_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qobject.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <kxmlguiclient.h> +class KAboutData; +class KInstance; +class KAction; +class KToggleAction; + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/viewinterface.h> +#include <kmymoney/statementinterface.h> +#include <kmymoney/importinterface.h> +#include <kmymoney/export.h> + +namespace KMyMoneyPlugin { + +/** + * This class describes the interface between the KMyMoney + * application and it's plugins. All plugins must be derived + * from this class. + * + * A good tutorial on how to design and develop a plugin + * structure for a KDE application (e.g. KMyMoney) can be found at + * http://developer.kde.org/documentation/tutorials/developing-a-plugin-structure/index.html + * + */ + class KMYMONEY_EXPORT Plugin : public QObject, public KXMLGUIClient + { + Q_OBJECT + public: + Plugin(QObject* parent, const char* name); + virtual ~Plugin(); + + protected: + /** See KMyMoney2App::action() for a description */ + KAction* action(const QString& name) const; + + /** See KMyMoney2App::toggleAction() for a description */ + KToggleAction* toggleAction(const QString& name) const; + + // define interface classes here. The interface classes provide a mechanism + // for the plugin to interact with KMyMoney + // they are defined in the following form for an interface + // named Xxx: + // + // XxxInterface* xxxInterface(); + ViewInterface* viewInterface() const; + StatementInterface* statementInterface() const; + ImportInterface* importInterface() const; + }; + +/** + * This class describes the interface between the KMyMoney + * application and it's ONLINE-BANKING plugins. All online banking plugins + * must provide this interface. + * + * A good tutorial on how to design and develop a plugin + * structure for a KDE application (e.g. KMyMoney) can be found at + * http://developer.kde.org/documentation/tutorials/developing-a-plugin-structure/index.html + * + */ + class KMYMONEY_EXPORT OnlinePlugin + { + public: + OnlinePlugin() {} + virtual ~OnlinePlugin() {} + + virtual void protocols(QStringList& protocolList) const = 0; + + /** + * This method returns a pointer to a widget representing an additional + * tab that will be added to the KNewAccountDlg. The string referenced + * with @a tabName will be filled with the text that should be placed + * on the tab. It should return 0 if no additional tab is needed. + * + * Information about the account can be taken out of @a account. + * + * Once the pointer to the widget is returned to KMyMoney, it takes care + * of destruction of all included widgets when the dialog is closed. The plugin + * can access the widgets created after the call to storeConfigParameters() + * happened. + */ + virtual QWidget* accountConfigTab(const MyMoneyAccount& account, QString& tabName) = 0; + + /** + * This method is called by the framework whenever it is time to store + * the configuration data maintained by the plugin. The plugin should use + * the widgets created in accountConfigTab() to extract the current values. + * + * @param current The @a current container contains the current settings + */ + virtual MyMoneyKeyValueContainer onlineBankingSettings(const MyMoneyKeyValueContainer& current) = 0; + + /** + * This method is called by the framework when the user wants to map + * a KMyMoney account onto an online account. The KMyMoney account is identified + * by @a acc and the online provider should store its data in @a onlineBankingSettings + * upon success. + * + * @retval true if account is mapped + * @retval false if account is not mapped + */ + virtual bool mapAccount(const MyMoneyAccount& acc, MyMoneyKeyValueContainer& onlineBankingSettings) = 0; + + /** + * This method is called by the framework when the user wants to update + * a KMyMoney account with data from an online account. The KMyMoney account is identified + * by @a acc. The online provider should read its data from acc.onlineBankingSettings(). + * @a true is returned upon success. The plugin might consider to stack the requests + * in case @a moreAccounts is @p true. @a moreAccounts defaults to @p false. + * + * @retval true if account is updated + * @retval false if account is not updated + */ + virtual bool updateAccount(const MyMoneyAccount& acc, bool moreAccounts = false) = 0; + }; + +/** + * This class describes the interface between the KMyMoney + * application and it's IMPORTER plugins. All importer plugins + * must provide this interface. + * + * A good tutorial on how to design and develop a plugin + * structure for a KDE application (e.g. KMyMoney) can be found at + * http://developer.kde.org/documentation/tutorials/developing-a-plugin-structure/index.html + * + */ + class KMYMONEY_EXPORT ImporterPlugin + { + public: + ImporterPlugin() {} + virtual ~ImporterPlugin() {} + + /** + * This method returns the english-language name of the format + * this plugin imports, e.g. "OFX" + * + * @return QString Name of the format + */ + virtual QString formatName(void) const = 0; + + /** + * This method returns the filename filter suitable for passing to + * KFileDialog::setFilter(), e.g. "*.ofx *.qfx" which describes how + * files of this format are likely to be named in the file system + * + * @return QString Filename filter string + */ + virtual QString formatFilenameFilter(void) const = 0; + + /** + * This method returns whether this plugin is able to import + * a particular file. + * + * @param filename Fully-qualified pathname to a file + * + * @return bool Whether the indicated file is importable by this plugin + */ + virtual bool isMyFormat( const QString& filename ) const = 0; + + /** + * Import a file + * + * @param filename File to import + * + * @return bool Whether the import was successful. + */ + virtual bool import( const QString& filename) = 0; + + /** + * Returns the error result of the last import + * + * @return QString English-language name of the error encountered in the + * last import, or QString() if it was successful. + * + */ + virtual QString lastError(void) const = 0; + + }; + +}; // end of namespace +#endif diff --git a/kmymoney2/plugins/ofximport/Makefile.am b/kmymoney2/plugins/ofximport/Makefile.am new file mode 100644 index 0000000..d4729f4 --- /dev/null +++ b/kmymoney2/plugins/ofximport/Makefile.am @@ -0,0 +1,33 @@ +KDE_OPTIONS = noautodist + +INCLUDES = $(all_includes) -I.. -I. -I${srcdir}/dialogs -Idialogs +METASOURCES = AUTO + +# Install this plugin in the KDE modules directory +kde_module_LTLIBRARIES = kmm_ofximport.la + +# Srcs for the plugin +kmm_ofximport_la_SOURCES = ofximporterplugin.cpp ofxpartner.cpp +#nodeparser.cpp + +# Libs needed by the plugin +kmm_ofximport_la_LIBADD = @OFX_LIBS@ dialogs/libdialogs.la ../libkmm_plugin.la ../libkmm_plugin.la $(top_builddir)/kmymoney2/mymoney/libkmm_mymoney.la + +# LD flags for the plugin +# -module says: this is a module, i.e. something you're going to dlopen +# so e.g. it has no version number like a normal shared lib would have. +kmm_ofximport_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) $(LIB_KHTML) $(LIB_KDECORE) $(LIB_KDEUI) $(LIB_QT) -L../.libs + +# rc file containing the GUI for the plugin +pluginsdir = $(kde_datadir)/kmm_ofximport +plugins_DATA = kmm_ofximport.rc + +# Install the desktop file needed to detect the plugin +kde_services_DATA = kmm_ofximport.desktop + +noinst_HEADERS = ofximporterplugin.h ofxpartner.h +# nodeparser.h + +EXTRA_DIST = kmm_ofximport.desktop kmm_ofximport.rc + +SUBDIRS = dialogs diff --git a/kmymoney2/plugins/ofximport/dialogs/Makefile.am b/kmymoney2/plugins/ofximport/dialogs/Makefile.am new file mode 100644 index 0000000..5d79074 --- /dev/null +++ b/kmymoney2/plugins/ofximport/dialogs/Makefile.am @@ -0,0 +1,16 @@ +noinst_LTLIBRARIES = libdialogs.la +KDE_OPTIONS = noautodist + +INCLUDES = $(all_includes) -I$(top_srcdir) + +libdialogs_la_METASOURCES = AUTO + +libdialogs_la_SOURCES = konlinebankingsetupdecl.ui konlinebankingstatusdecl.ui konlinebankingsetupwizard.cpp konlinebankingstatus.cpp kofxdirectconnectdlgdecl.ui kofxdirectconnectdlg.cpp mymoneyofxconnector.cpp + +EXTRA_DIST = konlinebankingsetupdecl.ui konlinebankingstatusdecl.ui kofxdirectconnectdlgdecl.ui + +DISTCLEANFILES= konlinebankingsetupdecl.h konlinebankingsetupdecl.cpp konlinebankingstatusdecl.h konlinebankingstatusdecl.cpp kofxdirectconnectdlgdecl.h kofxdirectconnectdlgdecl.cpp + +noinst_HEADERS = konlinebankingsetupwizard.h konlinebankingstatus.h kofxdirectconnectdlg.h mymoneyofxconnector.h + +messages: rc.cpp diff --git a/kmymoney2/plugins/ofximport/dialogs/kofxdirectconnectdlg.cpp b/kmymoney2/plugins/ofximport/dialogs/kofxdirectconnectdlg.cpp new file mode 100644 index 0000000..3db8fbc --- /dev/null +++ b/kmymoney2/plugins/ofximport/dialogs/kofxdirectconnectdlg.cpp @@ -0,0 +1,223 @@ +/*************************************************************************** + kofxdirectconnectdlg.cpp + ------------------- + begin : Sat Nov 13 2004 + copyright : (C) 2002 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. * + * * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qlabel.h> +#include <qdir.h> +#include <qfile.h> +#include <qtextstream.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <kurl.h> +#include <kio/job.h> +#include <kio/jobclasses.h> +#include <kdebug.h> +#include <ktempfile.h> +#include <kprogress.h> +#include <kmessagebox.h> +#include <klocale.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/mymoneyinstitution.h> +#include <kmymoney/mymoneyfile.h> +#include "mymoneyofxconnector.h" +#include "kofxdirectconnectdlg.h" + + +class KOfxDirectConnectDlg::Private +{ +public: + QFile m_fpTrace; +}; + +KOfxDirectConnectDlg::KOfxDirectConnectDlg(const MyMoneyAccount& account, QWidget *parent, const char *name) : + KOfxDirectConnectDlgDecl(parent, name), + d(new Private), + m_tmpfile(NULL), + m_connector(account), + m_job(NULL) +{ +} + +KOfxDirectConnectDlg::~KOfxDirectConnectDlg() +{ + if(d->m_fpTrace.isOpen()) { + d->m_fpTrace.close(); + } + delete m_tmpfile; + delete d; +} + +void KOfxDirectConnectDlg::init(void) +{ + show(); + + QByteArray request = m_connector.statementRequest(); + + // For debugging, dump out the request +#if 0 + QFile g( "request.ofx" ); + g.open( IO_WriteOnly ); + QTextStream(&g) << m_connector.url() << "\n" << QString(request); + g.close(); +#endif + + QDir homeDir(QDir::home()); + if(homeDir.exists("ofxlog.txt")) { + d->m_fpTrace.setName(QString("%1/ofxlog.txt").arg(QDir::homeDirPath())); + d->m_fpTrace.open(IO_WriteOnly | IO_Append); + } + + m_job = KIO::http_post( + m_connector.url(), + request, + true + ); + if(d->m_fpTrace.isOpen()) { + QByteArray data = m_connector.url().utf8(); + d->m_fpTrace.writeBlock("url: ", 5); + d->m_fpTrace.writeBlock(data, strlen(data)); + d->m_fpTrace.writeBlock("\n", 1); + d->m_fpTrace.writeBlock("request:\n", 9); + d->m_fpTrace.writeBlock(request, request.size()); + d->m_fpTrace.writeBlock("\n", 1); + d->m_fpTrace.writeBlock("response:\n", 10); + } + + m_job->addMetaData("content-type", "Content-type: application/x-ofx" ); + connect(m_job,SIGNAL(result(KIO::Job*)),this,SLOT(slotOfxFinished(KIO::Job*))); + connect(m_job,SIGNAL(data(KIO::Job*, const QByteArray&)),this,SLOT(slotOfxData(KIO::Job*,const QByteArray&))); + connect(m_job,SIGNAL(connected(KIO::Job*)),this,SLOT(slotOfxConnected(KIO::Job*))); + + setStatus(QString("Contacting %1...").arg(m_connector.url())); + kProgress1->setTotalSteps(3); + kProgress1->setProgress(1); +} + +void KOfxDirectConnectDlg::setStatus(const QString& _status) +{ + textLabel1->setText(_status); + kdDebug(2) << "STATUS: " << _status << endl; +} + +void KOfxDirectConnectDlg::setDetails(const QString& _details) +{ + kdDebug(2) << "DETAILS: " << _details << endl; +} + +void KOfxDirectConnectDlg::slotOfxConnected(KIO::Job*) +{ + if ( m_tmpfile ) + { +// throw new MYMONEYEXCEPTION(QString("Already connected, using %1.").arg(m_tmpfile->name())); + kdDebug(2) << "Already connected, using " << m_tmpfile->name() << endl; + delete m_tmpfile; //delete otherwise we mem leak + } + m_tmpfile = new KTempFile(); + setStatus("Connection established, retrieving data..."); + setDetails(QString("Downloading data to %1...").arg(m_tmpfile->name())); + kProgress1->advance(1); +} + +void KOfxDirectConnectDlg::slotOfxData(KIO::Job*,const QByteArray& _ba) +{ + if ( !m_tmpfile ) +// throw new MYMONEYEXCEPTION("Not currently connected!!"); + kdDebug(2) << "void ofxdcon::slotOfxData():: Not currently connected!" << endl; + *(m_tmpfile->textStream()) << QString(_ba); + + if(d->m_fpTrace.isOpen()) { + d->m_fpTrace.writeBlock(_ba, _ba.size()); + } + + setDetails(QString("Got %1 bytes").arg(_ba.size())); +} + +void KOfxDirectConnectDlg::slotOfxFinished(KIO::Job* /* e */) +{ + kProgress1->advance(1); + setStatus("Completed."); + + if(d->m_fpTrace.isOpen()) { + d->m_fpTrace.writeBlock("\nCompleted\n\n\n\n", 14); + } + + int error = m_job->error(); + + if ( m_tmpfile ) + { + m_tmpfile->close(); + } + + if ( error ) + { + m_job->showErrorDialog(); + } + else if ( m_job->isErrorPage() ) + { + QString details; + QFile f( m_tmpfile->name() ); + if ( f.open( IO_ReadOnly ) ) + { + QTextStream stream( &f ); + QString line; + while ( !stream.atEnd() ) { + details += stream.readLine(); // line of text excluding '\n' + } + f.close(); + + kdDebug(2) << "The HTTP request failed: " << details << endl; + } + KMessageBox::detailedSorry( this, i18n("The HTTP request failed."), details, i18n("Failed") ); + } + else if ( m_tmpfile ) + { + + emit statementReady(m_tmpfile->name()); + +// TODO (Ace) unlink this file, when I'm sure this is all really working. +// in the meantime, I'll leave the file around to assist people in debugging. +// m_tmpfile->unlink(); + } + delete m_tmpfile; + m_tmpfile = 0; + hide(); +} + +void KOfxDirectConnectDlg::reject(void) +{ + m_job->kill(); + if ( m_tmpfile ) + { + m_tmpfile->close(); + delete m_tmpfile; + m_tmpfile = NULL; + } + QDialog::reject(); +} + +#include "kofxdirectconnectdlg.moc" diff --git a/kmymoney2/plugins/ofximport/dialogs/kofxdirectconnectdlg.h b/kmymoney2/plugins/ofximport/dialogs/kofxdirectconnectdlg.h new file mode 100644 index 0000000..e5d92cd --- /dev/null +++ b/kmymoney2/plugins/ofximport/dialogs/kofxdirectconnectdlg.h @@ -0,0 +1,86 @@ +/*************************************************************************** + kofxdirectconnectdlg.h + ------------------- + begin : Sat Nov 13 2004 + copyright : (C) 2002 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. * + * * + ***************************************************************************/ + +#ifndef KOFXDIRECTCONNECTDLG_H +#define KOFXDIRECTCONNECTDLG_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +// ---------------------------------------------------------------------------- +// QT Includes + +// ---------------------------------------------------------------------------- +// KDE Includes + +class KTempFile; +class KOfxDirectConnectDlgPrivate; + +namespace KIO +{ +class Job; +class TransferJob; +} + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "mymoneyofxconnector.h" +#include "kofxdirectconnectdlgdecl.h" + +/** +@author ace jones +*/ +class KOfxDirectConnectDlg : public KOfxDirectConnectDlgDecl +{ +Q_OBJECT +public: + KOfxDirectConnectDlg(const MyMoneyAccount&, QWidget *parent = 0, const char *name = 0); + ~KOfxDirectConnectDlg(); + + void init(void); + +signals: + /** + * This signal is emitted when the statement is downloaded + * and stored in file @a fname. + */ + void statementReady(const QString& fname); + +protected slots: + void slotOfxFinished(KIO::Job*); + void slotOfxData(KIO::Job*,const QByteArray&); + void slotOfxConnected(KIO::Job*); + virtual void reject(void); + +protected: + void setStatus(const QString& _status); + void setDetails(const QString& _details); + + KTempFile* m_tmpfile; + MyMoneyOfxConnector m_connector; + KIO::TransferJob* m_job; + +private: + /// \internal d-pointer class. + class Private; + /// \internal d-pointer instance. + Private* const d; +}; + +#endif // KOFXDIRECTCONNECTDLG_H diff --git a/kmymoney2/plugins/ofximport/dialogs/kofxdirectconnectdlgdecl.ui b/kmymoney2/plugins/ofximport/dialogs/kofxdirectconnectdlgdecl.ui new file mode 100644 index 0000000..3937012 --- /dev/null +++ b/kmymoney2/plugins/ofximport/dialogs/kofxdirectconnectdlgdecl.ui @@ -0,0 +1,109 @@ +<!DOCTYPE UI><UI version="3.0" stdsetdef="1"> +<class>KOfxDirectConnectDlgDecl</class> +<widget class="QDialog"> + <property name="name"> + <cstring>KOfxDirectConnectDlgDecl</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>511</width> + <height>108</height> + </rect> + </property> + <property name="caption"> + <string>OFX Direct Connect</string> + </property> + <property name="sizeGripEnabled"> + <bool>true</bool> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Contacting bank...</string> + </property> + </widget> + <widget class="KProgress"> + <property name="name"> + <cstring>kProgress1</cstring> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout2</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer4</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>51</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>buttonCancel</cstring> + </property> + <property name="text"> + <string>Cancel</string> + </property> + <property name="accel"> + <string></string> + </property> + <property name="autoDefault"> + <bool>true</bool> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>61</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + </vbox> +</widget> +<customwidgets> +</customwidgets> +<connections> + <connection> + <sender>buttonCancel</sender> + <signal>clicked()</signal> + <receiver>KOfxDirectConnectDlgDecl</receiver> + <slot>reject()</slot> + </connection> +</connections> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kmymoney2/plugins/ofximport/dialogs/konlinebankingsetupdecl.ui b/kmymoney2/plugins/ofximport/dialogs/konlinebankingsetupdecl.ui new file mode 100644 index 0000000..3ee9b6a --- /dev/null +++ b/kmymoney2/plugins/ofximport/dialogs/konlinebankingsetupdecl.ui @@ -0,0 +1,488 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KOnlineBankingSetupDecl</class> +<widget class="QWizard"> + <property name="name"> + <cstring>KOnlineBankingSetupDecl</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>653</width> + <height>502</height> + </rect> + </property> + <property name="caption"> + <string>Online Banking Account Setup</string> + </property> + <widget class="QWidget"> + <property name="name"> + <cstring>FIPage</cstring> + </property> + <attribute name="title"> + <string>Select Financial Institution</string> + </attribute> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="frameShape"> + <enum>WinPanel</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + <property name="text"> + <string></string> + </property> + <property name="pixmap"> + <pixmap>image0</pixmap> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>m_listLayout</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Please select your financial institution from the list below...</string> + </property> + <property name="alignment"> + <set>WordBreak|AlignVCenter</set> + </property> + </widget> + <widget class="QTabWidget"> + <property name="name"> + <cstring>m_selectionTab</cstring> + </property> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string>Automatic</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KListView"> + <column> + <property name="text"> + <string>Financial Institution</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>m_listFi</cstring> + </property> + <property name="allColumnsShowFocus"> + <bool>true</bool> + </property> + </widget> + </vbox> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string>Manual</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout8</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KURLRequester" row="2" column="1"> + <property name="name"> + <cstring>m_url</cstring> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="text"> + <string>Org</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel1_5</cstring> + </property> + <property name="text"> + <string>FID</string> + </property> + </widget> + <widget class="KLineEdit" row="0" column="1"> + <property name="name"> + <cstring>m_bankName</cstring> + </property> + </widget> + <widget class="KLineEdit" row="1" column="1"> + <property name="name"> + <cstring>m_fid</cstring> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel2_4</cstring> + </property> + <property name="text"> + <string>URL</string> + </property> + </widget> + </grid> + </widget> + <spacer> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>90</height> + </size> + </property> + </spacer> + </vbox> + </widget> + </widget> + </vbox> + </widget> + </hbox> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>LoginPage</cstring> + </property> + <attribute name="title"> + <string>Enter Login Details</string> + </attribute> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_3</cstring> + </property> + <property name="frameShape"> + <enum>WinPanel</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + <property name="text"> + <string></string> + </property> + <property name="pixmap"> + <pixmap>image0</pixmap> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout6</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer row="3" column="2" rowspan="2" colspan="1"> + <property name="name"> + <cstring>spacer4_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>120</width> + <height>21</height> + </size> + </property> + </spacer> + <widget class="KActiveLabel" row="0" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>kActiveLabel1</cstring> + </property> + <property name="focusPolicy"> + <enum>NoFocus</enum> + </property> + <property name="text"> + <string>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.</string> + </property> + </widget> + <widget class="KComboBox" row="4" column="1"> + <property name="name"> + <cstring>m_headerVersionCombo</cstring> + </property> + </widget> + <widget class="QLabel" row="5" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>Connection Details</string> + </property> + </widget> + <widget class="KComboBox" row="3" column="1"> + <property name="name"> + <cstring>m_applicationCombo</cstring> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel1_4</cstring> + </property> + <property name="text"> + <string>Username</string> + </property> + </widget> + <widget class="QLabel" row="4" column="0"> + <property name="name"> + <cstring>textLabel1_6</cstring> + </property> + <property name="text"> + <string>Header Version</string> + </property> + </widget> + <widget class="QLineEdit" row="2" column="1" rowspan="1" colspan="2"> + <property name="name"> + <cstring>m_editPassword</cstring> + </property> + <property name="echoMode"> + <enum>Password</enum> + </property> + </widget> + <widget class="QLineEdit" row="1" column="1" rowspan="1" colspan="2"> + <property name="name"> + <cstring>m_editUsername</cstring> + </property> + </widget> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>textLabel1_2_3</cstring> + </property> + <property name="text"> + <string>Identify as</string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel2_3</cstring> + </property> + <property name="text"> + <string>Password</string> + </property> + </widget> + <widget class="QTextBrowser" row="6" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>m_textDetails</cstring> + </property> + </widget> + </grid> + </widget> + </hbox> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>AccountPage</cstring> + </property> + <attribute name="title"> + <string>Select Account</string> + </attribute> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="frameShape"> + <enum>WinPanel</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + <property name="text"> + <string></string> + </property> + <property name="pixmap"> + <pixmap>image0</pixmap> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout29</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel2_2</cstring> + </property> + <property name="text"> + <string>Please select the account from your financial institution from the list below which matches this account.</string> + </property> + <property name="alignment"> + <set>WordBreak|AlignVCenter</set> + </property> + </widget> + <widget class="QListView"> + <column> + <property name="text"> + <string>Number</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>Type</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>Bank</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>Branch</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>m_listAccount</cstring> + </property> + </widget> + </vbox> + </widget> + </hbox> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>WizardPage</cstring> + </property> + <attribute name="title"> + <string>WizardPage</string> + </attribute> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_2_2</cstring> + </property> + <property name="frameShape"> + <enum>WinPanel</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + <property name="text"> + <string></string> + </property> + <property name="pixmap"> + <pixmap>image0</pixmap> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout6</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>m_labelFinal</cstring> + </property> + <property name="text"> + <string>Congratulations! You have successfully set up your bank for online banking via OFX.</string> + </property> + <property name="alignment"> + <set>WordBreak|AlignVCenter</set> + </property> + </widget> + </vbox> + </widget> + </hbox> + </widget> +</widget> +<customwidgets> +</customwidgets> +<images> + <image name="image0"> + <data format="PNG" length="18409">89504e470d0a1a0a0000000d49484452000000b700000164080600000062b3aaaa0000200049444154789ced9d7b7c54d5d5f77f13c630263064429ad8099090385c26354447218fc638c608f5e111e205ac5a354d63c5372a78ad85965b955aad0a563fd2823168a5af201aa23c568b318dc89baad1814a22991226c150833197c1e01081f3feb1b3cf9c3973cedc72cedcb2bf9f0f1f923367ce9c24bfbdceda6bafbd9666efdebd508ba1a1210e0012131335aa7d48002c5fbe9caba9a9c1d1a347237a1f8cf092a0e6c51b1b1b919292a2e647044457d711381c8e48df0623cca82a6e87c38113274e44dc5adaed87d0d8d818e9db60841955c5ddd6b64fcdcb07456f6f6fa46f8111665415f79e3dcd6a5e3e20a8df6fb3d9227d2b8c30a39ab893929238b5ae1d0aa74e1d8ff42d30c28c6ae2eeefef57ebd221110d4f114678514ddcd132818b96fb60841fd5c4dddbdb0babd5aad6e54382fadf8cd1816ae266133846a4514ddc5d5d47d4ba74c8b00137ba504ddc76fb21b52e1d324ea733d2b7c00823aa88db6834468d6f5b5f5fcf7fcd96e04717aa889b8a282727478dcb874c7b7b7ba46f8111465415777676b61a97673002421571777676aa71d911d3d0d010e95b608411ad1a17552b4969686888733a9db2518fe2e2626467677be46dd7d4d4a0bcbc9c097b14a28ab8950ab925252571353535f8e4930f3c96cf2d799ee7b51fd1a1cfe9e2bf2f2ab270d75f7f0bcacbcbd1d2d2a259b76e1d7a7a7ab8fbefbf1766b3996b696989781a2e437d5411f748309bcddc33cf3c83975fdec287130d7a1dcae602f3b380b9c51a4c481607635cb07701db1a81cdefe8b0674f33f6ec69c69a950fe39aeb6fe256ae5c09009a8f3f6e8e9a280e437d5411f7be4f9b827e0f15f5fc1f97f056d892072cb918985b7c12c7fa01db7e60e9cb1c8e7401cd07dcef35e875a89ce7c25d0b34b86bc1492c7d19a87d17e873ba505d5d8deaea6a545454702b57ae645bcd4611aa4c28852e4220b4b5b57153a79c83952b57a2cfe982250f78e949e0f5873568eb070aaac6e2a2bb81db3711d10a854d3fef89ede4bc7f1ce350b304787091e739d5d5d5c8cece465b5b1bb3dea304c52db7d96c0e583c66b399bbedb69bb173e72e00c4023f7e830b8bae021efd2bb0f49db1b8acd085ca792e9c5708cc4c044c99e4bdf62e62c9dbfa892bd2e774a1cfe9c2adf7031fff11587123601f208341c8bdf72cc1fb8d4d5c346c7f63a88be2e20e7432999494c44d9d720e6fe5cbe6021b6e39094083f28d1ce66701b6e74e6242b2f4fb4d996ea1afb8d185bac3c0d23544e47357e870b8c68515577b8bbbcfe9c2b265cbb06eddba107f4246aca0ea3633397a7a7ab8cb8b0bd1e774c1a0d761d3ed40cd12f013c59a25c0a2ab2031719467c154e0708d0b65738980eb0ebbc52fa6a1a121ea760a3194477171fb4b4e6a6b6be3162c58c00bfbdd47891b420946d052d42c214f017fd4d5d58de87318d18fe2e2f6e59624252571e5e5e5004824e4dd475d5ed6d5de055cf19b91ddc38aab8925f7054ba28a7f548d73ebf57afe6bb3d9cc4d9d720e0022ecd71ff68e5753bff9b2c2e0a22d62e8801918d400907e12b024aaf84771cb4d97de0d7a1da8950680db6ebb997745362e018ef57b8a6e6050c34f08e76729732fef36cabb38050505ca7c08236a512d5a72ef03cb4197b93ffae8236ee7ce5dbc8f0d007357e878b1e74c76a1fdc858fefb4557c95b6e6108d03e407c6c397675c8bf969a9a1ac24fc7882554714b4ca65cdc73cf3d68696981d96ce6e6ffb80400f0f80d6e1ffbdd475d58b211683ee01a5e9471f1e7081918d4e0dd460ebb3a807f3479e69088176a84d8bbbcc380428a8b8b43f9d11831842ae2deb4e905de6adf77df7de8739210dda285a918a3cfc7e99e06983289df5d50359617acd06adbbb8047df04fed1345620684fe14ff3516373c946f9d70c7a5dc42bcf32d44771714f9890cc0b67686888abaeae06402218c917fe15aeb6dff1e71eebe73c2c71ce6417ca3742943b22efa28c9d217d7cfbdbde4bf442ee7d6079603f0c23a6517c42b961c373fcd7ab57af0640dc07f3a5772161dc4538ddd3c0bf6edbeff9dee603d2b92300b1b6624e7ee17d9ebd0b78e855ef7385d7b9e79e7b7cfe0c8cf8407171d3acbba4a424aea1a10106bd0e772dd04077ee5a9c3ab6cde3dc8d3eeade5bf2c8a078e949922b22958c257ebfbdcb3d51ad4a97beae70a2cb886f548b733ff3cc330080ca792ea49d771712920d38e5fc9c7f7d605083e603ee509d250f2831133f5a9cb35d3eec3f0b13abc40c0c6ab06423c7475c52b35cc031cf7384135d46fca39ab8df786d2b00e0ae051a9ccafa0900e08c40dcc7fa396cba9df8cddeab896e61d3a80759f8914ea4b277014b3672bc3b73d77417fef784f779c2892e23fe5145dc4949491c8d904c48e6a04fb904003cfc6d61569f2fb635d28c41a91d389eae080054a503530a80e64d9ee7ad5dbb9645484619aa889bee7ef75a694c4c058682db3cbce246fa95b7b0b7bf4d268f54d885c9c0af2e35e2faad473dceaba8a84069692913f62843959457ba4a3976063026cdca1f1fa3cf57ec33b6bf4d76e608b7a4bdf66323167e71144d83eef38a8a2c78eaa9a714fb5c46ec10d67ceec4493f19f13506063528df48844da94a0776ce30e2771f1cf50823161559f0d65bef313f7b94a28a5b3265ca1400240e7d7a6a83fbc3d2170389cb83764d28246bd07345f3f11b5cb8aa9758ec664174c464ca65c21ee5a862b969c61d8d430f1dfe13f9b06403ce3607bfbd8b5aeb5bef87c7c4f15f57a6e2aa5e23aeff9ba7c55eb8703e3efeb899097b94a38ae53e71e284c664cae59a0f1c42dd616041e272b80c3f823ee512244ebd0300f09dcd473adf3003831a3c5bc761f33b633d7ceb9d338c00803f751ec5b30775e813f8d8ebd7afc7ecd9b3352c96cdd0a8d51e7bf7eeddb4180e1fca4b3baf0a89e75c8d847117e1dbef5bc07db840d2451116d8115aea5f5d4a44fd76ea51bcb0151e1347abd58af5ebd7474553574674a09ab8cd6633575656c6d7e833e8c90e9b15574bc7b7eb0e03ff6af22cc760d0eb70d77417ee98e2b6d47f6ff114b5c9948b152b7e8369d3a629226ab3d9ccd96c3616138f03541337656868887bfae927f8da24145aef4f5ce7cfa0d7e1269d0ba959c01d538c783bf5286c3b81ad2ecff3ac562bcacbcb1513b5f07e013071c7019ab56bd7720b162c50fd716e341ab9dada5abcf6dacb3e7b42fee622f27f6f87b7a04da65ccc9f7f35cacbcb55bb5f26eef881df411b0ee1508c4623d7d0d08037ded8ee65d185141559306dda2ce4e7e7a3acac2c2c75fe68b935a59f088cf0a3299b0b4ebc7daba8c882cacabbc3f207a63eae10718d6d35686b6be3a47ebeddbb777300d8727d1c90b0e3f55e7cd95889979e74fbc17bf634a3bcbc1cb7dd760ba776e1c89696164d6262a2c7bf7058e837ded8aef64730224c4242b20149b3fe885beee3b0f7cd8de87859c3576cb2db0ff122efe9e9899bf26346a3911b1818947c4dadae108cf09330f08606df7e70394eecbb1b09495391f23f1fa06609d9fd422db9dd7e080b162cc0f2e5cbb9686ac3172a0e8703a74e1d977ccd66b379b4f763c42e0900c9b3febefd590cee9d07eefd22002416fdde6f814db7bbf72f363434e0fcfc19315fe3bab1b1d167c486111ff8cd2d59741529254c5d953ea70be5e5e5d8bc7973cc0a9cb91ea3039fe2b67791ff2724936e05422b5e5d5d8d871eba9b0ba6d87cb4e0af8638eb7c161f68b7bfedaeff71f20b7799b27f34913267effdd67df2a2ab80827c5a298a44552ebac8828f3f6e8ec90e6146a39113476642e9e7c3884ee4cba0829456706ff3723330a8c1b58fb937e4c6daa6808b2fbe9803888516af44d2d7fafbfb63e6e76148e3d32d592c534e6f423287d71fd678c4c52fbac8a2f4bd4514a57a6932224702e0eef328aeea3477858ef7bbc550810b63e2b13cc964c41f090089805c3b8ff49479e949e28e18f4eee649be045eb3c41d0fafaeae86dc624fb44c3c03ed85c33a2fc43ebc5bb2740db1da0ba6123ffb708d0b9b6e072e2b2402dffeb6fc455e7f58c35bfdc71f5f23794e59599982b71d3afdfdfd019d47cb533062175edc7d4e979780175d458abbdb9e3b0980b6e1f0664232c7d7d5deb3a759d23a363434f0e9a4d1426363a3c7f7d1f27461284302752900f9eaa8139239bfadf3165de5f6d9699d400a15bb584cd1069b44c6170993055bbefa9c2e59ff3a102ae711eb5d5d5ded6105e923beb9f99fa15f5c21fcb512a4b0fc92d82741dc5a435c333b18ce2b747f5d5353e3bee6b04594cbc40b27cc3a8f1ebce2dc6d81cdb77884967e66a2fbebfdfbdda3845a41b6accd0827099b6e27a13fa1ef1d0cdb046e747a8a7bc229ecf3d8d57524d4fb5315619f4c46fca1a585dc570c1ff0d59854ccf6b7491e0ae51fc7dcefcbc9c90140221076fb21056e5579582fcaf8c6cb2d09b4f7faa37f25c52885658a853d6a6ebdf55600d1e7e3fa9a2846dbbd32464650e5d4684f485a135bdc109584125da8a9a9e11392a23dfc2724d0480a23369015b7bd0b681d72a7c1d6b740d0c3c6e5d10d18202e4a9f93085bb8ab5cb88c6d32e52aff130489707bd9f02efb08de0d434db40011e6c6bdbe7b370a21b5ff487f1a5aacf289edf0123600b4b5ede3bfcecc9cace0ad87c6238f3c89828202b4b4b468c4c2a6a59719f18176fbdb6475914e2ca9c50648ed3e807418a31b1a2e4b27bd69060689df4d2bb04a091b40d4ed554c4c4c94ad003b6dda34cddebd7b313434c4b1c4a9d8470380a3452ae76779b7c91362ef228b3cbb3a3cfbaacb093b292989134624d6ae5dab4ab11b5ad8c7e17078ac86fa5a349a30211916cb1c00a40f7c380a0131c28b1620be72edbb402d006c22c216c6bdc5c52a2906bd0e5bfeb20d69696992a210471f94882bd39a83fbf7ef477b7b7bc00b4334ef45f873884bb919f43aaef8f22b70cd358b5839b53820f0a0b688a2220b9e7df6059f75058535ba01e96d5d81303434c4d5d6d662d7ae37218e995bf280c999806982db7d9a99e8bf0da0f484d9fdbadcd388113b68ac562bb7efd32649cb2c8541afc3bd0f2c0fc8bd58be7c3927b4acc1ec4b4c4a4ae26a6a6abc045d369708f9bc42a9e6acd2d41d768b182083a0209f885b780d7b17f0e89b6e978b093cb6f1a8cf4df3ad85beab90e2e2e2a02cef430fddcdd109a541afc3aebfd5fb7d6f5b5b1b575353e355b47e7e1624db62070315f9ae0eb2bb5fd88aa4c44cba1d4f48e6f8368040e84f1b46e451b5f83cdd490e9062f1ebd6ad9315495b5b1bf7e8a3bfe5ad74d95c78097a6ab9efee0cc1e0eeb7e329f2d71f26027ff4afc013dbc9e03adcf915db091f83a8266e71a464d9b26558bc78b19740a4442d27de2b7ee3f68b1f5c44dc8b5d1dc0912eb7df4dad6fa088db6b5bf24819b981410d0aaa4898d3dfc0644427aa743303bcf72a6667677b7c6f341ab9f2f272defda0a2365f7a17be6f7fd6eb7a03831ac10a29b1aa94071709451ddcfcd89409bcfba80b17dd4dbe6f3e40046fca245be76edf445c939e9e1e4e2e2ac4884e54eb202cce29115af1b6b636eefcfc196868688041afc34b4f022fffda8afcf91bf1fd975bbdae458b0089b1e4916ab42b6e0c3ce14b0a5326f81215807b118b2e5c01c0fdf7df1bf2f51991216cedb1a9b8376fdecc959797a3cfe942d95cb2f9f8ba2beec299f31ec1772ddedd85ed5df0a86e05103f78d3edc47d18a9ef4d1166370a375df0f7613f245bb682119da8e69608534b0d7a1d5a5a5a349b376fe6aaabab0190a29a8b16a622f9c2bf2261dc4538de70ae97b0e9a44e789dca792edcb580e4b52809b5d2963cf78079fd1def9f69f1e2c5ca7e3043355413b790591714424ad89a4beaa04db904df7e70b987b0ed5de08b6d8a79623bf0c4766917450c9d6452ce2b74e7c68839f9853b5a029070a0d7fe529b8d893b865055dc4545163cf2c893686c6c045da9140a5b9f72094eecbb1ba77b1af8f70c0c6a3077c558c945255f0b4d5203417cec41000b6e94f62c0af281f7ae02a8b0699c9b11bba8de64d5683472345242abc69e5db0118953efc099c13e4977042022176e5b0b84935f80df4821c492072cad086c45b37ca3b7c5a6d0bef241dd142362a8ee96ac5dbb160011d88a1b0124a62271ea1d008053c7b6490a1b20d18f4097d70192932e16b6250f587271602b9bbe5c21803c8598b0630bd5c5fdc66b24b4576226df9f35e926feb553cecf477c7dcf8d169e167be312ffd114e1660b390c7a1d4d121bf1fd32c287aae21e1a1ae27cf9c967421437cd2b97724184f81336b1f6d2febd90a79fd9a87a576586f284255a22e4fb2fb7e2ccb96b91906c40e2a49fe03bc164520e2a66716a6ad95c9250357686db9fa69b987775485fcb3ba7c4b7b0596660eca2fa84f2b6db6ee1ecf6432401a98608e9ac9cbb9034eb8f3833d887139f5eeb112d1163eff22cfc030497ee4ad9feb6f70e225f18f43a3cfdcc4626ec18c6afb87ffffb47b95ffe7245c87fe0b6b636aebcbc1c0059e2ae59428ed38889b3ff43701f2e909d58868ad0828b7bdbfbc364cac5f6ed3b982b12e3f815f7c5175fccd5d5d5c96e250b04615eb730eb6f4c9a1589937e0200f8ceb624d4cb03f0ccd53ed215f84e7e31cb962dc3ead5ab598a6b1c1090b8e5d2550365686888b35aad1ec7849b10c6ce905f3914527798fc2fdc1a26b7bf3358ac562bd6af5fcfac751ce153dc3427db64cac5962d2f8fe88f2ede4fa92685c9c0c1318189de6ab562f5ead56cb74d1ce2335a4273b2edf643309bcd236aa45a5a5aaa71381c7c7e895218f43a4c3fedc29566604a01d06903fedee27ba9dea0d7e1b68a2558b66c192be710c7f814b7b0769ecd664362a2442e68103cf5d453686bdbc717ea294c065efbb1116fa71e45ea7b806d6260d79952005cf821f083994600c0dba94761db09fcf663f9f7984cb9b8f4d2cb505656c6cf1f5829b5f8c6a7b88575476c361b66cf9e3da20f6b6969d1bcf5d67bdc7df7dd87eaea6a340d0293761c456132f07d36f0dfc3e74d290052df037aaff0febfd346fe6dd400ed7fef95b5d026532ecce619b8fcf22b61b55a992f3d0a09781147a90aa82d2d2d9acaca4a3cf5d4539ccd6643636323eaebeb71f0d326fc960a955ae07699ff795c30997291993919393939c8cece467171315f0b909ec596cd47273ec5ad6683a69696164d6262224a4b4b515a5a0ac05d160d90dea626ac58e56b0228570b9031baf0296e61adbde6e67ff222540b2a7800aa7f1623fe09780f65347422633082c1a7b859f731462c13b0e5a60d9c188c584156dc46a3d1632d5c5c5487c1887664c5cd3a0b30629db015e56130c24dc0969b75db65c41ab2e216d7e766dd7619b106734b18714bc0e266d11246ac11b0cfcdf29e19b1866c6e497bbb570a5e50188d46cee170c0e9747ab5eca3e8f57ade97677d20194a1350caabbf9eed66b399a3a9ab369b6d24cbf65c519105175e78294a4a4a505c5ccc36ea32422620714bf56c379bcd5c6d6d2dde7863bb57b3d291b0674f33f6ec69c6faf5eb01000b17cee77efef33b46b4fb9e313a09bae294d168e4d6af5f8f2dd51b2577c1d0a6a7be76b5fb6b702a64e7ce5dd8b973174ca65c6ec58adfb022398c80f1296e835e87556b1fc3ecd9b33566b3997be6996720dec1eeb76f7ca20163f4f9008004fd8fa0d5ff08e69ccf61fa722b309514e25901cf223a5255a1ecf643282f2f87c994cb6ddaf402dbadcef08b6c6907a3d1c8a5a4a4a0a5a545333434c4dd7efbcf3d3af94a96074e4cc5187d3e1227fd04094953a1cd205d94ce0cf6e1d4b16d18faf2fffa2c9d46a125d4843d22c5b0e2390c7ff82dcab36ddb368efabf80449fc8c4549c35e926249e73352f662078414b216e570d0055e9c05617117d20fde719a317597127252571ab56ade0278b5edd098645ad1baed84a39d5fd2e86be7a53b29764a808db781426033fbf89ec801797721057b592a2a4a4c4e3fbe2e26200404a4a0a1b247186c6e17070e2051b87c30161575fdaee837256ce5d5ea276f67f88847ffd3a642bed0f719f9adf5c444a40bcb015685278071cdd510fb8078330262fde5dcf884e48eb2e1f6cbadded578f49b3e2cc798f409f7209fffa99c13eb8febd52514b2d8758e055e9c0af2e35e2fabf1d555ce0fe58bb762d4a4b4b99c055a4a0dfc18df9e114347f9710d2efd9a7b8c5c24ebae0752f1764f0931b7d961fae3b0cfcab09585cac4c4354290b7ec71423cefb7b2fd2323271cb2db7057caddede5eaf7a2cdf7cd3edb119fad4a9e338b0ff80e4c496766b63911be5989d6de44eecacc1f1e757f3c732fee6c0478ee057af65c52daca54d8bc50b193afc277c675bc287f00af2dde2a5c71e7a550780f45657aad32fe0dd7c75470ea94675fb26a0a2a2429524affafa7ad9955716b9193996b3cf70c79f5f83934d7ff77a8d3319f1c32d9fe39ffbbf08eaf7ab31997239ea5feefbb4097d4e170c7a1d6ccf9dc484640e63d2ac1877e9fb1e6f3ab1ef6ede0db1770117dd4d8e1bf43ae44c76f1658569d3d291f46597636ab93b4c68c90376ce30e2771f1cc556970ef73eb05cf1cf0388a5975bbc329972c1e2efc131277f06f75ded4be87fe37168ecbeeb364e78683df6e704d74d2ee1965b6e434949090a0a0af83fdae337b87841265df0bac71b84c20688b536e88985ee73bad07c007c5ff7f77e0b55844def91d27c8014c3fcd5a546720f2a55ca4a4d4dc5bd0f2c475191c5eb35bbfd10ac562bb66ddbc699cd66d623de07b3b38ddcccdd35dc5717a762e0f165b2c2e64c468cbf733532fee6085ad80090408570f8b01d00112af5b3cfcab9cbc3c71e3afc27c98963e53c4f4b267469d4626eb1e7cf6adb49fe277de6bf50f7b3e72e444545053fa885ac5fbf1e175d64c1d0d01013b808cbd967b86935abb8ee1f67e3c46b7f963d6ffc9dab91b6650f3aef7f4df3f9acb99a50fc6d0048b058e600002f08a145d4ea7fc47fedecff50b2b587bd8bac2452c2216c803c1184fdde9b7e40febf49e7424f7797ea9f9f9d9dcdac7800ccc99fc115f43bb8294f5ecff5dc5624e9530340d2f5bf80e1b1ade8d8b857f3f9acb99a50232442f8dc121ad31e3bc3fd624292bb6558c2bf7e8dd3a2378bfbb41bf43a6cb8e524fc4417c9e78992a7ec03a4970d405a81d0ce6781d27e4407cc0052b380be8f5d7c0e8c41af435a86f76c562ad371c284644c9c98e1714cafd723353555f673e7ce5d88acac1cfcef9b6f7af9e2ebd7afc7ae5d6fe2c9279fe6465b56238d7a7cb5e46200247221666ce195482afb196c29d9aafc6eb400992851842df0e872faa9ee77251767ae7d8cf3f883e64c76e1d93ae90faa1f2ebc1a480f9b071705dfe346ee9a7d4e17fa9c87bc8e0bf36482459cdf9e99391979f9797c517df1e72c58b0000b17cee7b66c7925ee232a96b3cf7027de7c09dd326e07673222f5e77fc0d8e21f071dfd08166d7d7d3d4e9d3aeef32457dbefbc8ed9253a86351ff0df45cc92474448db658b93a3cae67aae86068a94ffab16e28111c840d9b9731752525260d0ebb859171462c28464582c73f895cf585ffe2fe87770276a5f448f4c282fe59a87a02b29c3478ea39a4e00d8afeebc0800b4be76cd9cea7e17da8cb9b24bea429f97323913304d707f7f5e21f95f2eaf7bf33b9ed70bd55fcf994c06c8df5b8865955acce9ededc5d75ffbf6c73ffae853c9e33ddd5d8a744deb73baf878b9789387d56ae56a6a6a6266bbdd9cfc19dcc9c6bfa1f78507d0278a787026239267fd0f92aebe15cddf2510413bc2dba6454b1fb1d4fa0c0cba4578e6c4619c19ec937ca3299384fa02c75bd87357785aed8d410a5bf89428fc1ac00c92676242e875566eb8e19680cf6d6f6ff758e13c78f05ffcd71d1dede8ee7637cc0a848686069c9f3f03abd63ec6cd9e1d7ce82b5cccc99fc10dbefc8ca43f9d74fd2f78410300be8bc00d0ea3a9ababe37a7b7b41bbfc0a97dccfcab90b5afd8f46dc00558cbd0b58b2d1db850926d2225e86df74bb3b53f0e69b1761ecd8f1004854235a0a0ad1c1409f201f7df4a9acf0a3319d7776b6913bbe799d57182fe9fa5f60ec8556d52686a1a2a9ababe300d269aca1a1c15360c369ad4a264549596c8ab03fbc3fca37baf3bc0d7a1dfe75652a167e7154d2e737e875f8efabafc69557cef719f98804b5b5b578e3b5adb2bf0fba132a02b7c64327894251ab1de950028dc994cb016e9f52b8f40e10ebad94b8b7bf0d3cf4aa8e5fc1bc769ef739c2688d1c03831a14548df55a7e9fb4c3b74f6732e562c58adf449dc0dbdbdbf1dc737f94b5e256ab15b5b5b5618fb4d049228d4d53418723d2a104928953e2fced912274430c7a1d36ac7205246239c489535f5e6794b5da620c7a1d9e7e6663d4091c005e78e14fb295040c7a1db6fc655b58aa0014f43bb8de171e80c67e34e6042d44362b70d3ed9e0b3a62e8e28b3fc43bdb1f5ce43e2e8738e2224628ecc264f27f30f9dcd16ac101927d28979c05900cc4c58b172b2e326112936e625ecc0a5a88dfcd0af14a340bdc9f9b525464c15b6fbda7889b42231fa7bf3e8ab1175a635ed04264c5edafca54a86464a4202b2b32fd7584e1397a2f95957747a5c07b7b7bb179f31f25573d01e2a6bcdfd834a2680a8d53c793a08568d6ae5dcb454ba88ce18d2f3f1c006a6a6a58a12219f85020237a89941f1eebb0e2f3314049490956ad7d4c367f66fdfaf5d8bc7933335222f838b792289971170c9999939199f9039fe7fce00799b23ef6942953a2d2ffa6f4f6f67a94dc10b370e17c8c86ccc34019b5d1926090cb09379b6760dcb8248f63d3a79fe7759ed2731aba9a2c859291946886e68b6bb3a6c9ae9232714700a97c70e11347fc74917aa2d4d6d6a2baba5af2faf12a70cbd967b8ef0f7c0257e32e8f1d3de7eced958cf668007006bd0e79f912f9abc34c9c780e0a0a2605740356eb85c1df75186868f824a0f36cb62ff1cd375f49bed6dddd3f22974b09844f115f69b826532e3efeb839a6053e3bdbc80dd9f6e2e4270d3ef75c1a1edb2a69bd351cf719b3dc23c4d93f884f6dad5ec7a506d47bef791e932bf8a304151515a8acac8c2971d3ccc3c17d6ff92df740195b7825dacad73071473badadede8ee766ffbb3d90ea2bf9fec94eaef3f8ee6e683fc6b720b3c426241e005fd0ece9f75f647da963d5e65d798b8e380dadaf75153f39eec624fb42df4d0955161c661a08c2dbc1263264dc5981f1831b6b01400646b093271c711ab576fc49a357f927ccd66b34574e3c3ec6c23e7aaaff59a0cca41457c568e1967e55d08adc91c7c393526eed8a3b5b51d0b173e84d9b32f4065e55c8f49fca597564aba2b9158c5a482f6552e4d0911cbc1c41da308ad2e8c38270000200049444154f4c285f3f152cdafa04f49466b6b3bcce6ebbcce379972b165cbcbaa8b9beeda114f08399311ba8979483cff128c2d2cc5988c4921556e0d0626ee1866dab4ebf9d0645191051f7cb01900f0d39faec32baf6cf73a5f0dd784facf74422814b1366b1a46525f7ba43071c730b5b5efe39a6beee3bf7ff1c535282f5f80d6d6765c5278b364052c25f663d2c59433c7fb71faeba3aab8144ac0c41de3087d6c9329176d6daf0100caca7eed153d59b8703e7ef9cb1551233eb561598131cefdf7dfcc7f6db71f426b6b3b00c06a9de9756ee3fbef85edbea20126ee18a7acec728f5c95575f25f52ee6cdfb2faf73fb9c2e2425258d9a273513771c70d34da5fcd75bb7ee0600cc9c992399ff6db3d9c2765f9126e8deef9120d0a4274a46462a66ce8ccc3ecd4870c30d73f9b0a0dd7e08cefe41e85392252bcfda6c364c9b362d12b719768216b738f70190169f380f424c2079116a6332e5222323c5e35856560ece3dd79d5e9a9d6d4476b61140f40e1a6aa56974a4bee12394955d8e2baeb85052dc8b172f8ec46d861dedb2654fc88a50cd8cb568c06e3f04bbddf358a0834e3830aeb882ac1016144c474aca785c503013fa946445efd51f422b6db31d4459d9e5282898ee759eafaabef18676c386ad91be8798443830e40644519105595939b8fefaff4259d9e5aade8fd04adb6c5f0200a64fcf923cd7683472b152267924c8eec42994313c161f06e9dc44604aa2ef0f2c1c17e09da9c0b15340bbc483a87308f8f790e7b1664105ab8363fc7783f087c9948bc71faf524de40d0d9fe0f2cb6fe73f8bc6bb359af325ce6d18152d05355df9a3679b999cb8831970c26b0807051d0cfecabadd7cf322fce52fcaf7c974f60f6282a188ff9ee33e03209d48a5d44a65b4a3bd2e845d53966460f939cadf4c3878a29bfc1f486d41835e87e9a7dda341f8d42a19479e528b2536cbd301f044b7f7e7d09c0fa505ae4f4986c994cbe79ad0884956568e97b8c52dc1e3156da00524e91ffa0683f41f14009abef5fcbefe5bef734a4456329c6e4aba16d821aa1ce1d3129f76798853f8f573c73caf53984cc47f6e2260d5939f6bc738605b2f70ef979ee7bef2ca769496fe08e5e50b14f9b928b3675fc08bfb535b2bacd60b3d223f94e6e67fa2b4b4d4eb78bce1150aa4be36b552f48fd5e074614a2290a32322aeff36f047b110b128a43e97faee6a0bbfe95bb77515ce312cc9ee9fff0603b9173a509b65c4de34e8f9bd41afc34abd8b37046281af5bf712ae2dbb42d1a84a69e98fbcb201a52226030341fcc16218ed8e1c790b5b388e08a0c109bcdaa7ccc44a8e269981422d22bd1f25a1d6b5e95b69fff9e0181dfa8e85f6f3f6395db877f8e9bf38955c5b38b0edf643f83f776d50d43d9933c7dd14d7663b08abf542a4a48cf73acf5ff7ba78c1235a62d0eb7093ce85e64122a8adaed0c52cec741648efc940294c26d6d4aa276e86926ceb750b5cec3ed57f4b8e853289fc6cb82de1f9a29ae406bd0ebd03ff6f64372d2275c27fa1cfe9c2aa557760f5ea25b29b17f6eedd1bff134a00787ab824c9bf875cbc75217f3069415af248817800c89a0974b492ff01c03245ea1dded769ee24ff770c574438d215d820103efe0b9381073394b3e8563df0ef1e225a29f743cea5f247839358efaa74cf6bf4395dfcc44f29c44beed1b8a23a122c679fe100e0f47f3a71aaa30d0030f4d9870000d73707f8dd3f86c7b642fbf424e272f8b23e06bd0e9715ba780177b4bac598d3e5e2856e99e2295adaee7a72a667ff1b7a0e20ee8be3e25fa7eff7d50aa46910b8aedddbbf0d9574ad3b0a249c1ce7e8dc4f097a9c5af040acb7f069201e2074e2a7147431e7dfffeef57f721411a8687b7c5c43f8283ad5d106ad78a2e3f5a17900e0c23f9a74e87bd7c50bbda814281a1663472bb06737b0e180dbaa67cd24c2a542dd50ed699985d69f5a7b39d1d36bfca349dab253ff76ad5319910344bcfe7e3781b2d5a5c372b8249f30b4268952d0096447473b7f4c1822a40c0d0d71e158c8515ab48172faeba39ed11261f20dc56d39ddc7a9d029657389508b86a34b54ec2f0d8b990e88c999c4fa0bc5dcd10abcf4a2fb730d7a1de9065cea3ecf3285fcbb769e0bcd9de4da52165d28f2cd69d2620a94c5a9eed5567a1da94520611445ce7afb72b5681e8852482db96764a478e5d08c94d9d946ee743719fd279b489aede9af8fe2f4978755156da09cfef23011b7258f0873cf6e97df8e60527f28da0f12efba8f59f22070655cbc6f2d143de016331d1896292e5ef47b76bbadbd41afc3ad3f7311a15790735f7f47f0d9a27bbcce497cf2e7b3429f78168e03d67d455c1fa5284c0e2e741a2c6afad8c2bd9327f679bf3ee607468cf9811189b80450398c4e371ffb3c67e952f285d07a2a41f38161eb2a129fa7e8012afc3d64f063c301f779e469e01ab6e044f4af0b7ac567cd045e9a475c1ea941d93408947c393257a532cd3b72421186507d59ee7053546451e5bacddf256890335b956b87849fd6db5af7235eddd456deddc0b0b595103d756de8c4b4a3d5db6da1d7baacd03df95c3a6cc9a5444e5d957f0f11a1066bc5d3b540fa3880a6290863fde2e5f9c264ff6b01e118005959391e3eb7d412fc68401b48635225e873cabb3c6573496444eccb532c792e7e924a84ef6deda9db52542afd147aee18b1aea1ba293718485469fa6917402377a2e57942e4f3dfcf3d37151f7df4a9c7f7a311edf84be6e1ac8c4c7cdfdd85ef7bbec2d9ff3918f60d0a6ebfd9c55b78711c9d8a7f83c882e74c26adb6b366badc31f69f11f18bfdf1a641b290b22327f8d8389d608a577385c954a1c4c169eeb59258ad17cad60c1c4d688f7f489cd8f197ccc38492324c285980f1875a71b2c38e13fb9ba0fdd73fc22af69cc92eb41fd1a1f9c0f0670e0b54287ab1bf2e14be7082ba74a6b415afecd161b34c68cee7bde9c8e4529887128c9b71ec94f731b942f723212363745a6a31da8c3b57e1fbee233871a019ddcfafc1d02bbf87a6f426a4dd58850925246b4df3d7e7d0fbba748b0aa511fbff6e51bb70a48b08596891dd7177fa1e17efabd3452431349af299393817255d4bde230c078a97e37d895d2a975c0de26d553254b403f5b548cab320fd670f42973b13ae43de1d02d26eacc2b8d92538d961c7407d2d5c0725e2402a417d75297f9d0a5bce5727c82b2a1417255d0b94f4784e24fd596fb95d4d0c75d126e55970e240337a5faf46c69dab786b2d46973b13badc991eaf0b07c2c90e3bbeef3ee2f19e13079a55f5e1fb9cae612b2e7f7d835e074de94d1837bb04c75e7cc26b6056f6e850af730565c13fcf76f169bf80dbef0e2505582dd40a07c612da71b34b907663155c875aa1cbf52ec1e50be1f9bede3bfe502bbefda81edceead210bdda0d7e1d47997e1ac8c4c9c95311963b34cfc803a71a099172d3d2f29bf1063b34c1ef735e5916a7cfdf34b3ceea1cfe9c29d1dc147512a7bd44bff652883b6efcd97f1c365eb8216763050ab8f1bab82f2df855657eafe42b9e7f1cb37a3efe19f7a1c6b1a0436f704b7758e5a6f2038bf3b5c4c9c18a3fb001544fbc365ebc2fa81d47f977211004fcb2be7228d045dee4c4c79ec2fe81409fcb963c16d883876cabd1f13f02d685f1503d4a2ac2c8a5612234444caa9e9726762ca23d5700dbb2b00785743cd2788f0f353afadf07a8254f6e8f0f9b8c05c8d742d71651a86573f85028e06eb4dab6401ca671ec60ada507c6da5e0dd950890766395d71ca0cfe9c2b65ef90dd052289516ab265215c54643dd12edc90e7bc40416295cc38b54dffd703ae0f4748dd63a75589c1ab8f5eeca97de412fb4dee726ba5f63840f2d80902225b1c6407d1d4eec6f025d91952358eb2d55ba410ccd0b1757b562a88b76a0be1667a59d83704f2cc381eb502bfade7c39e8148257fb0217b770534334464d46335aba32196f0cd4d761e895dfe3b81f51d302ed42f1370d125723d0b8b75465292139de35e079b2b2d45f2a3fb0df737957d889219ed1763efc5318f43a8c5fbe396e44fe9ff5cbfdba1fee4dcfd2198474c77a203c9f457c6eba62497d6cbaf19a0e92668901108e7454f1532b33737248d729e87770a73ada30f4d987707d7300ba8979fedf24016d6f1d2801edba91e8a4a69df2d85f42bac168c59fb0dddbd55c78fd1d41590a91b883f18fdb5dca6e458b566c29d91aa46403b3e6f2c7e85e4abafd8c6efe0db6a7bb1264892a166b699c39edc6aab0df8cd20cd4d7490adb739fa68bdfc749f7640a77dd53a4acac1c85e3484d12a9423ec15c474968b908dadd4c2d4817e004f0dbcfa8f0cbd70070ef7ea79b88c3257ece64742fe2c443c4e4c4fe26d9d7da8fe8d0fe2278911709362d4b17120a8e7313fd57908dc40453dce205004a4a4ac2f6f97cf7602a7a19f1fb2bfd102cba897924710a082d4f23daf8be473af15fb8bb9e6e4f13170a1297b508568856bd67e17da982f60c6f78f1a764937f8087db43a1830000ef0201ee721214e11341dbf9f04fa19b3e0bf1103539fb3f0725935f693e382d614177f26ca8766f2ece99ecbfac853fa4fc6e9acb2db50b0790aec2aa24c176828b563cfac7fbda813ffc440000edb4edf1b32b5a2e964d454dad76b36043b152a46b49cd45a1b56e1e241b8b01f95d38525558d5a6a0a020ec9f1909b4c2fce6298ffd25e6adb714c29d3c06bd0e4b97baf8e23f4a32259184ffc4c72289d40664bd5e1f813b093f5a4de94dc80863465e24f02ce4e9f2a86b48ab578909656b588ece5dac5eec73472aaf448d0dc8b182f6c481669cbd7b2bba9d2ee8a6cf42529e2526c382527b3f01e97d96963c60e952cf288912f55bd2b524f1aacfe9e207070d0fca4d2e95acf02a85787512206e494b4b8bc4d9f18516004e9d7719523332412327b1c8c90ee94a8f749fa5413f2c6a41752a61b14d31a16e30d89ce602d2c8d79d43be97dec381d43ca4a5a525eed35d81e10dc200d9cc2bcc6fa63b620c57df1213eecad02bbf973c4e2793744572e91a6259859d1fa47cef7343f495851193c264f7127e241673d45ec08976b4c2dd282eb845adc9c84452c6e4981076e7af2be0125928ea8e1ce922857968a52a4b9e8baf4748e990f068ac21ceb976e4b8fd6d7f0344ed1dea520b3856ab55d5cf8c26b4e32f99e7b5539cfaaf273bec443807f741377d16a63c129ec23c8140b7a8c96d3676977d00005a728d7c474382d445f947930ec2f21085c9a1973d16660856a5bb8f476275325e62dca1a2fdbee72b0cd4d70280c7865d835e87ef7e38dda3604fb4d013c00e7a613e09cd1f918a7137777afba534361d0a3718dcfeba5ce9638adae9aea375ef2445eb3ab88f1772eab515382b83a4438ecd3241339c5475ecc527009015c048a7c60692ce5a3617a09658e8920863dc14255d120af5af4b041d19a4503bdd556aef6438f34a228d960afbecff1c44af8f3269b486482485ed3ad4ea57d8803837db2599e64af34ac479dc55e9236b013825d16db969a42452ab935261402510e67900a2a5f12842dbe77401ce7d92391934ee4d8be2b80eb562a0be4e957a2281d0f7e6cb019f2b5cb8a1a27ee945728c0a7b838467539936b27bccd1b9b3032b05214129d4cc2b71f60f4a860195589d6cfe2e41333bdbc80dd9f6e2e4270d98b2ef2d4e633f8ab1855702706f46185b58ca9f3fe20f0d01af864fc282387497b8701fa241af832b02ab99815a6d71e84fb81ab96195fb8fdddce9bd703352ab0d90f73f98e1fe1a905fc051b3d4f0a736e9452da5f24a3e721c251b174acb81d27208c53eb8ef2d68ec4771fcf9d500c82602ce64846e621ec64c9a8ab372cc382bef42c9dd334ac277101617a1174f2e8565cd2291fb4da33672085bffd1967e7452b954d0208a7667a0f16e8a41af43fda4e00a624ad1f4adbb86f78ee1ad8aebbe922e4ccf719f8decc37cb07efd2bb8f7de3f781defefef0fcb228e94d8e5185b78a52aa2f7688f2d44377d16269494f12142ba939c5acf7085065d875a654bafc9415d12da07937645a33b6fa4baa085d26d410a610d137abdeb0e798702d5688d2de4a73f5d87575ed9ee71cc64cac5962d2f47c4450846ec142afab1175a31e6875382766fbcc44de3dedf771fc1f7dd5d5e6511e8ebd46d01d4dde8e0cf6253a4ba1c93f835785103d2c27e7a527055a6fc71dd2132a9a4853533f77b9f535464c1071f6c56ee4345d01ef0422a2a2a505959191593bf50c40e90ed63ba8979483cff127ee3b09ce8bd1ec2c73f7cc7cbb7155a71c07b712792717071e705ea9258f23c450d102b2e151d5152d880db1df185c5a2de64b2b6f67dc9c9647171b16a9f192c629f7d4efe0cee64e3dffc8a5d633f8a93f6a31e3b6e843e7d5bf91a5ee83e3dccd46b89b37a56c6649cd8df84a1577ecfffd252afade0453d505f87632f3ec1f7d451127f565bd87981f6b814b6e5a62b91cd9dc0860d9eef2d4c0eae6c7128347d2b7d5ccd30e06baf49bb3b0505053871e2846a9f3b12feb9ff8b90c44ed1d88f7a45fc7c8a5bb80a48232919c32ec9407d9d870f4e8bdb841bb13bb267b77bb25836575ed855e9ea0b1b900f03aa99eafabf6fbee975cca0d7e1c4891351e192044228624f9ef53f1edffb8d0d505127e61742d37d0403f5b5e87e7e0dffdaf84be6f1998303f575e8fc7585626e8a5c8e36fd6cd268d5e5d1044a389904a41baf864bd840f8c380adaded922ec9ac0b0a55f9bc701188d893aebed5a3abb05f71f7395d80c80fa73eb894054fbdb64231ff9bd65491bb2fa1ff2c8c6fd3145671b80f507ef2e80fa95457835ea75ac7b1575f7d57f278bc2dbb8bc53e3bdbc8911a2a6e828eeaeaa6cfc25969e778587000bc050748fec7480b6bba0eb5fa4d8ea28206dc6db43b0add39254261172693c51525c27dc170708c67c62100e4e58756862c10de7b4f3a13309a26936a2016361082b85d07f7793557325c7d0b4e76d8f978b46efa2cb2a2b8ae12e39793709770c249d355858b42273becfc64b4e7afcf81dbbd55f61e84bbd9a988697444aa977d557a687ddf47cab153d23b61d48c94c8f5781f0dc5e6c568972e8540108133fe9279d0646402dd5d38beaed2e38f98feb30789986ffe2500e0f8ba4ab89c2ea4ffec413e5dd5a0d74177631506eaebd0fdfc1a64dcb90aae43ad5ebd6ac418f43ab41f019a37b83c26937b767b4f1a0d7a1d56ea5d61754384c8254ca995532297bf3d9a362808d16ed8405244a9253cd21598d0e56af28d5fbe197d6fbe8ca43c0bc6669978e167dcb90a273becbcab9178f32fe13ad48aeee7d740377d167fae3fa89516d6fd13c7ae016aad47be9c3e12c47de22973e6fc4895cf9313f768a95322c6638552ecc31ee922cbd681166ecfb87315df6158d85089ae6ad238396dc9ddfb7a35bf08e4cf620bef11901f80917241a4905a7607d4cb29b9f4d24a49b7a4aeae0e696969a3ce2d91cd2d71471f489c38f128503f5c0d205817465c874f377d9687dfae44b3d2681235259ccbeecefe414c301449be16ae64a96843fb999934181567ad89ab345d56e8b9b19686dba88517be4f8c58bcc255c73e7ee32ef99e66f651fc3d3da251d480fcee9b2bae5067f1a6bee123c9e356ab75540a1b00b4e7b79030d96766d24d80760310c2c7944571652a44eaca48d500912b5926354800399fdf53d885c964af62a4268a81b0b947fab85a2b9372fe76bcc5b7834153954edc92e64192c95699e66e813192a645161fa1dc60fc784a553a299560d5479f95164373baa518e8db037d8af22d85a74dbb1e76fb21afe3369b2da696dd958417b71c25e3a47b2baa8941afc3f4d32ebeee5eb8175e46c2b153c0f93295ca4ca65cb4b5bda6f867caf9db06bd0ebbfe563f2a850d005aa91d22429e3b467e4937e95cfc0a5fd3b79e8207c84a5c30d6980a18204f8c73138990737440ba56b9d2c2e1e4d829e0ce0ef9d7fffbbfe7a8f2b9afd7be2779fc9aeb6f52e5f36205ad78078a7027891b72a07308a8ff8ab82eded6345841c6a680e568fad67fcb3eb5fcedddbb3f973c5e5656a6cae7c50ab2a1403984169712ed933b25a1835ff8e40af4a9a5567c5b6ad70d307a438014ed8ea093d3dcbf44e242287a3f1145b8b140b8ba485d2f79abec5fd837dfbc28e4fbf285dcae9b850be72b2eecd9d946ce555f8b84d474d2b62fcad10aff88feca7f459b98e576b950a496bfc529a8e19a20af58f11355ae2db7ebe69a6b941f4c1f398e6a90331b96b3cf70d39e5fc58d993415e32b974b66e4450341bb2581104c57826027a2b1c81b6f3c85b2b2cb55b9b6944b12ae284941bf83eb7de101e826e621a9ec675167cd55b1c3c159c3f815b641af43f59675aa095bce25095794c49692adc1fdaf21bffd23aeefe19b90057049d7ff024957df1a1525d654b1dc0c9243f2e73f2f576dc70d009495fd1a3b77eef23a1e89859b39f933b8c1979fe1ab4c712623527ffe078c2dfeb1aa55a57cc1c4ad300b17cec7b26565aaf7ba696d6d87d97c9dd771abd58a75ebd645cc6a8a450e0049d7ff02632fb486dd6d61e20e11932917191929b058a62325653cacd60b7141c14c5596d6a590aa28050035353598366d5ac45d82d9d946eef8e67538f1da9ff9639cc9087de92f90b4b03c2c93d051296e2a4c29a858292929e33d76cea86d910341ce6a47b25c9a1c52220748a934b527a11111b72f7101c0c489e7a0a06092eceb62c1c9110d42548368b7da52c8891c206e8b1a9350cdfbef6f524cdcf12aa6682296acb614be44ce998c48b9e6219c5d76ab32555e39eeb351e796c432725bc9a2d96a4be14be48032935026ee18a2b6f67d5c73cd7d5ec763c56a4b21155d113292492813778cd0dada8e4b0a6f965cb469686888f9ba2473f26770dfd5be84fe371e972d7a99f1374750024f50ecee18aab270e143b20952b12e6c809447db9f335bd379ff6b1ac3635bc1998c1eaf732623b3dcf1885c74c4a0d7e170e757719bd66a39fb0c77fcf93538d9f477a46dd9137c670526eee8a5b5b51dbff8c53ad91269a3a51ec99cfc195c28d11326ee28a3b5b51defbcf3ffb063c7fbb2a206622f3a1209b4ebd7bf8279f3fe4bd5041f06116d77772fffbdc371140e079938d96c5fe29b6fbef22966214cd881a1edef3f8e575f7d170505d3554bcd8c17c4b541c4df8bcb071fd87f40d15c759329179b36bd101713c870c0e773db6c0761b379f70a0780ec6c23b2b38d92af499191911a154f0267ffa06cb351b130fbfb8f7bf54a0fd492aa8941afc3ac0b0a51565686f2f2f2b89d3caac1a84c9c8a568a8a2c9838f11c582c7350505080fcfc7c1c3d1a9d5bb8620126ee3042ad30e02e73565c5c8c949494515b154a4d98b803c064f2dd5852aa8c59434303ff75b03eb2d96ce61c0e0713fc08d1fafbc301404f77d788264606bd0e691999fe4ff44346460ab4dac0fa374e98908c891333fc9e979d9ded75acb7b7174ea793fffe9b6fba31304036869e3a751cddddfdfc6b3ddd5d5eef070217b4d168e41c0e071a1b1bd1dcfc4f4c9d6a42414101cacacad0d22253978d11109ab56bd7c695e5160b1300dadb3dab524a0954e91df8e27d8c4949495c6767276c361b9a9bffe911fa5bb8703eaeb966111534b3d60aa175381cfc377abd1ea9a9ea948e127e8e14274f1e4757d7d792af89c5485143944a61b3d9306dda3498cd666eea9473bcee73e1c2f9a8a9a9f11034b3d4caa2adaef6dd0e8f111a8d8d8d98366d1a152e0790cdbbe5e5e54cd061824d2855429863ddd3d3c3b1b05ef8d158ad562ed8c997c3e1f0f2637dd1d575849f0ceefbb4296a5d09a519cd85dfa3016d57d711747501c017fc41357c59bb5dd1cbc504757575282d2d8df46d8c5a985ba2224545163cfef81f99e58e102316b770d54d8a9c9c1cc958b210bd5e1ff146a00e87039d9dd2dda99a9bffc9c7b929c2451a7fd765be7664e0e3dcbe04969d9dcdfe407e181a1ae28d84cd66e363ed0b162c607e7784d0ecddbb37d2f7c060a802db20cc885b98b819710b1337236ed10a274200593696a3b7b717369b2da00b8773b1c664ca4566e6649fe7141414f8cd9b292e2ef6f93acbbb8e2d589c5b61e442a3e21eece281c4f6452a8f2271eebc7c1f8dde25b8e28a91578395daf32887d21b75d5463c408403830e0af614f18f66d5aa3b8633d6a405172d9b7dd5405c6e01201ba5fbfb8f7b9d2bded92e24d21b898b8a2cd06ac763c28464582ca405371d04a3f989c08af2a884d4cefbfefee35e15068483a6bbbb5f72cb9a1258ad5600e4294017ec0a0a0ae27a7304137714237cb2089f287440283518ac562b727272909f9f8fb2b2b2b8598d66e28e03844f095a8f8556b10a65be51515181952b57c6bcc899b847090d0d9ff06ed1bfffdd8b8e8e769f7305835e872d7fd916d3853699b84739b4f0e6da55cf4a5af858ae4be8d5f0495c664c4c3021385f041a61f0d7f92c10c4edf7a4188dd12221be3a37c46aa964b688132252832e2b2b07e79eebb90a2a1e34e16cc41a2c720237e87578bfb129e6e2ea4cdc11a6a8c8c27f2deebf490746389f1ef12470d5c45d980c1c1ca31bf1cae06766e07c05aa1f1426034d83fecf53ebfd4a409f16f40941abef2a2dfe86864f70f9e5b77b1d8fb536255ee2ae4a079a0747f68734e875f83cdb85eb0e8dec3a55e9c0f27330e2eb00c08e1ca0fe5be0b963a1bfffbac037fc7ba0d440f7074d85a0e21f493ffaf5eb5fc1bdf7fec1eb782ced0bf510776132f07c16d0ee0afd0f091021148e0332f78fece63e3303e95a60dd57a18bb23019b8c1002c4e05b6f502f77e19fc35e8200be53ee8400ff5b3957862984cb9983dfb02ac58f193a02cbc5c43d78a8a0a54565646bdc03dc44d4579ec54e0ae4061326019360c25e3801c1d1124109cb80d7a1d6ed2b9706e2260d5bbaf01042e2ae135a624929f458caf6bd1f703e0af21fc79821168e1f0efe4f92cf7fb7fe408de7a77e5076f24aad2a57f469329179f7cb425604bee2b82120b2142cd8e1c705222f0e70a54a50395699e221412ec00b9c140c424c513dd8159afaa74f9d74ac6f9764b0c7a1d36a7913fa250d062e484e66b70520219a474505892c93d178e0bee89419f32720369e1c2f9a8ad7d24b08b01a8a9a9c3cf7eb6caeb782c4c30353b72bc27949d43a13d42473306bd0ed34fbbf8a718e0b6fe14b9c145dd41b90105480f2ae140a09fb7783812e9eb29f3f4d30f60d9b29be53f4c4459d9afb173e72eafe3454516bcf5d67b513bc164a1c028422cd692e127aad4fc45f8b4937bd2f87afab6b4ec08d8ff76f60f223bab54f249b06cd9322c5ebc7874885b890910f5e3439d442a7d3fb1027d7a00ee0122f73b0cc5ff369baf937c2d5a5730f90dc2befcd540a94a071ef45f4fd32fcf1d863ea80000079849444154679147ac123c9f15fa7b0b9347f67b290cf342649fd385a6e130ee73c77c1b07bbfd106e2dff5dc0d79e393307ab56dd21f9da6d3f5d0cb3d91c751e4002e09e848cf48f519936f21baa4a278fd8c523ac815f984ca23fe9dad07fae1b0ca1ff4c55e9c08e5c624d832594f784c2ce9dbb505bfb7ec0e7af5ebd44b23f509fd385db6e0bdc870f179ac264703b86ef379859b97002258c1084120bae4a970ebd051a0293bb174a20911b6148537c8d601691e8bdd0096228f16dfab408e6f718aafb65d0ebe0e8d8ad487830dadc13cd676670f48fd8f4adffc59ba727c987ba80e0c35672e1c44043893404572211ce04c860d9dc237f4ff4a9e50b7f02a5933ba9df4b3021513a3076e406f73e80fc5d80d0a25cc1860757afde88356bfee4753cda96e7355df9ee09e5485602e305e1a40cf06f0da9951587fd02790255a5bb63d952f8fa7b48c5c381d0168a00e0fdf737c9a6fd4a316ddaf5925bdca269f5928502238838364e9f3e7460483d31fc3d390379fa4a6132e5a2adedb580cff7153d696868888a5df78a8adba01f7972d0d39380b5ce915f47897b8905a416725eed0bcdff5eb5ea0eac5ebd24e0f3e5dc13613fa0489260d0eb140903eec8019f97112a85c9244a32d2eb00e41a4f4ff27f9e1c4f4f1a59d4225c110f71e8efde2f438feb3ff3f416b4b6066ef6e5a22776fb21ecdebd3be21e41c2e634f9c958a054a5139faf79848b253718c8ff238d71d389aa551fdafb0d7a1dacfad007d98e1cf0792ab1449fd385471ffdbf41bde7cf7f7e58f2f8d37f5807a3d1185181f313ca6026227261b360f3aee975c4597cc1f88dbeb21201ff910e29bf97de47b0110b1ab9a1d19750f2d077e4049e2846517a1536d8c9a55cee49a42797bcb8fd454a7c85bb2881fe310d7a1d56ea5db20b3581c6860b93fdaf88fa120a4df1f585af9f49188694caef083645d6924cc292c1c6c63f330377762827f0a2220b3ef86073c0e7fbca3d89e4e4921777a8bb5d84a1b358cfe110af64fada3d2395fbcd5f27802790bfa887afbf87701249d709428d92c811acf596dbb963b55ab16eddbac8881b2c1418118402150e0e3a30c4e2a6f9e6be72cd955ca708363408c8c7be23b5b181893b46910a0102cae6e1bff8e21a94972f08f87cb98dc5910a0d3271336409c57acb4d2ed7ae5d8bd2d2d2b00a9c899be19360adb7dcca6524f24e7c8a7ba4a5cc0229633652822def16e942f1b14628d65b6ee532dcd69b15c2f481bfba8900e0701c85c37154f6755f834fcd62f34a12acf5960b0d86db7a3371472152ed4c00efc1263570d478328562bde576cd87736187893b8e11b72e117667100e8c409e20c1c6bd01f9d0a0c3e1084b617b266e860772ad4aee5b764bd065d9e44283e1b2de51296ea96649a1305a6a6b47337225d9c261bdbd8acf537c4da67cb5ad036267a2e48f407b6cfaeaab492bb18a192d032f92d69bc5b9a30ca9f0ab544835968ada47ca7a4795b80d7a1dd23232255fcbc8488156eb3b669e93139a25cccece0ee97d421c0e4750e79f3c791c5d5d5f4bbe76ead4717477f77b1defe9ee0a382d593c48c403443838d47e8ac82deca86dbd5517b79460333327877cbdaeae2323bd259f042320397c0d523172bf8bcccc1f60ec58cfc1acd7eb919aea99272c1c54e201231c2481fe5c62574ce8721514b80748b04f0a29ebad76c66054596e46f088b7790907cb8409c99838d19df02e7c42d141f1cd37dd181820e987d470043bc085ad4fa4e61ffdfdc7b161c356afe34cdc0c55103e5d842e9f7040e8f57a389d4efe89409f044a3cdd00f597e399b81901419f10e2b9cfa953c743ea521c8ea578266e46440847e9b504ffa73018ca52515111969a824cdc8cb0525464095fe214a2d02d319972030a17969494781d73381c686ff7de29dbd575c463d5d46ab506742fa74e1d6739e00a61d0ebf0d9fe2fc2923405001a835ec7cdbaa050f24529f188292e2e0ef8c30a0a0aa2a60268b8181a1a0ad97834363606757e7d7dbdec6b0d0d0da1de46d0d0488c380523dc1b85357bf7ee0dfa4d46a3910b7645ce66b3c1e97406fd59912698c10b0029292951dde14b48525212d7dfef5e0975381ce8ecece4bf170e967d9f36f98d8818f43a145f7e052c963978f7dd9dfc13afa8c882679f7d21ecbf178dd56af5b22ce24738435d0c7a1de49e9e724c98900c8b654e50efd1ebf528282808ea3d42e8c0351a8ddcd2a5551e1b810d7a1d6eab204534b7546f449fd3059329172b56fc2662fd2aa3d2e766442f26532e9e7cf269a4a5a569dadadab8f2f27200eec29f7d4e170c7a1dee7d6039eeb9e79e88baa14cdc8c90a0197d62814783a8294cdc8c9010f69fece9e9e16a6b6bb172e5cab045420281899b1132d1deff9d2de23042a6bcbc1c6d6d6d516b1c7d741b8f4ee4220b393939b29b0e0a0a0aa0d7fbaf441fa938fc4862e14a136cc8b6b3b313d3a64d53f18e4227ac6e495191c523a34cbc482416612cc58c19d147c8e2162e5f0b452a142813272392f0e2163eee858f78a158a3a1fd1a831128dafefefea8884932184a93c084cd885758289011b7307133e216266e46dcc2c4cd885b98b819710b1337236e61e266c42d4cdc8cb885899b11b7307133e216266e46dcc2c4cd885b98b819710b1337236e61e266c42d4cdc8cb885899b11b7307133e216266e46dcf2ff014aec27be8fe3b5c70000000049454e44ae426082</data> + </image> +</images> +<tabstops> + <tabstop>m_editUsername</tabstop> + <tabstop>m_editPassword</tabstop> + <tabstop>m_textDetails</tabstop> + <tabstop>m_listFi</tabstop> + <tabstop>m_listAccount</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kmymoney2/plugins/ofximport/dialogs/konlinebankingsetupwizard.cpp b/kmymoney2/plugins/ofximport/dialogs/konlinebankingsetupwizard.cpp new file mode 100644 index 0000000..d24f9a4 --- /dev/null +++ b/kmymoney2/plugins/ofximport/dialogs/konlinebankingsetupwizard.cpp @@ -0,0 +1,445 @@ +/*************************************************************************** + 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. * + * * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qtextbrowser.h> +#include <qlineedit.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qregexp.h> +#include <qcheckbox.h> +#include <qtabwidget.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <klocale.h> +#include <kdebug.h> +#include <kmessagebox.h> +#include <kstandarddirs.h> +#include <kprogress.h> +#include <kapplication.h> +#include <klistview.h> +#include <klistviewsearchline.h> +#include <kcombobox.h> +#include <kurlrequester.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "konlinebankingsetupwizard.h" +#include <../ofxpartner.h> +#include <mymoneyofxconnector.h> + +class KOnlineBankingSetupWizard::Private +{ +public: + QFile m_fpTrace; + QTextStream m_trace; +}; + +KOnlineBankingSetupWizard::KOnlineBankingSetupWizard(QWidget *parent, const char *name): + KOnlineBankingSetupDecl(parent,name), + d(new Private), + m_fDone(false), + m_fInit(false), + m_appId(0) +{ + m_appId = new OfxAppVersion(m_applicationCombo, ""); + m_headerVersion = new OfxHeaderVersion(m_headerVersionCombo, ""); + + // fill the list view with banks + KProgressDialog* dlg = new KProgressDialog(this, 0, i18n("Loading banklist"), i18n("Getting list of banks from http://moneycentral.msn.com/\nThis may take some time depending on the available bandwidth."), true); + dlg->setAllowCancel(false); + // force to show immediately as the call to OfxPartner::BankNames() + // does not call the processEvents() loop + dlg->setMinimumDuration(0); + kapp->processEvents(); + + tabLayout->insertWidget(0, new KListViewSearchLineWidget(m_listFi, tab, 0)); + + OfxPartner::setDirectory(locateLocal("appdata", "")); + QStringList banks = OfxPartner::BankNames(); + QStringList::const_iterator it_bank = banks.begin(); + while (it_bank != banks.end()) + { + new KListViewItem( m_listFi, (*it_bank)); + ++it_bank; + } + m_fInit = true; + delete dlg; +} + +KOnlineBankingSetupWizard::~KOnlineBankingSetupWizard() +{ + delete m_appId; + delete d; +} + +void KOnlineBankingSetupWizard::next(void) +{ + bool ok = true; + + switch (indexOf(currentPage())) + { + case 0: + ok = finishFiPage(); + break; + case 1: + ok = finishLoginPage(); + break; + case 2: + m_fDone = ok = finishAccountPage(); + break; + } + + if (ok) + KOnlineBankingSetupDecl::next(); + + setFinishEnabled(currentPage(), m_fDone ); +} + +bool KOnlineBankingSetupWizard::finishFiPage(void) +{ + bool result = false; + + m_bankInfo.clear(); + OfxFiServiceInfo info; + + if(m_selectionTab->currentPageIndex() == 0) { + + // Get the fipids for the selected bank + QListViewItem* item = m_listFi->currentItem(); + if ( item ) + { + QString bank = item->text(0); + m_textDetails->clear(); + m_textDetails->append(QString("<p>Details for %1:</p>").arg(bank)); + QStringList fipids = OfxPartner::FipidForBank(bank); + QStringList::const_iterator it_fipid = fipids.begin(); + while ( it_fipid != fipids.end() ) + { + // For each fipid, get the connection details + info = OfxPartner::ServiceInfo(*it_fipid); + + // Print them to the text browser + QString message = QString("<p>Fipid: %1<br>").arg(*it_fipid); + + // If the bank supports retrieving statements + if ( info.accountlist ) + { + m_bankInfo.push_back(info); + + message += QString("URL: %1<br>Org: %2<br>Fid: %3<br>").arg(info.url,info.org,info.fid); + if ( info.statements ) + message += i18n("Supports online statements<br>"); + if ( info.investments ) + message += i18n("Supports investments<br>"); + if ( info.billpay ) + message += i18n("Supports bill payment (but not supported by KMyMoney yet)<br>"); + } + else + { + message += i18n("Does not support online banking</p>"); + } + 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(QString("<p>Details for %1:</p>").arg(m_bankName->text())); + + memset(&info, 0, sizeof(OfxFiServiceInfo)); + strncpy(info.fid, m_fid->text().data(), OFX_FID_LENGTH-1); + strncpy(info.org, m_bankName->text().latin1(), OFX_ORG_LENGTH-1); + strncpy(info.url, m_url->url().data(), OFX_URL_LENGTH-1); + info.accountlist = 1; + info.statements = 1; + info.billpay = 1; + info.investments = 1; + + m_bankInfo.push_back(info); + + QString message; + message += QString("URL: %1<br>Org: %2<br>Fid: %3<br>").arg(info.url,info.org,info.fid); + if ( info.statements ) + message += i18n("Supports online statements<br>"); + if ( info.investments ) + message += i18n("Supports investments<br>"); + if ( info.billpay ) + message += i18n("Supports bill payment (but not supported by KMyMoney yet)<br>"); + m_textDetails->append(message); + result = true; + } + return result; +} + +bool KOnlineBankingSetupWizard::finishLoginPage(void) +{ + bool result = true; + + QString username = m_editUsername->text(); + QString password = m_editPassword->text(); + + m_listAccount->clear(); + + // Process an account request for each fipid + m_it_info = m_bankInfo.begin(); + while ( m_it_info != m_bankInfo.end() ) + { + OfxFiLogin fi; + memset(&fi,0,sizeof(OfxFiLogin)); + strncpy(fi.fid,(*m_it_info).fid,OFX_FID_LENGTH-1); + strncpy(fi.org,(*m_it_info).org,OFX_ORG_LENGTH-1); + strncpy(fi.userid,username.latin1(),OFX_USERID_LENGTH-1); + strncpy(fi.userpass,password.latin1(),OFX_USERPASS_LENGTH-1); + +#if LIBOFX_IS_VERSION(0,9,0) + // 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.search(appId) != -1) { + strncpy(fi.appid, exp.cap(1).latin1(), OFX_APPID_LENGTH-1); + strncpy(fi.appver, exp.cap(2).latin1(), 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.latin1(), OFX_HEADERVERSION_LENGTH-1); +#endif + + // who owns this memory?!?! + char* request = libofx_request_accountinfo( &fi ); + + KURL filename(QString("%1response.ofx").arg(locateLocal("appdata", ""))); + QByteArray req; + req.setRawData(request, strlen(request)); + OfxHttpsRequest("POST", (*m_it_info).url, req, QMap<QString, QString>(), filename, true); + req.resetRawData(request, strlen(request)); + + 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.path(), AUTODETECT); + libofx_free_context(ctx); + + ++m_it_info; + } + + if ( ! m_listAccount->childCount() ) + { + KMessageBox::sorry(this,i18n("No suitable accounts were found at this bank.")); + result = false; + } + return result; +} + +bool KOnlineBankingSetupWizard::finishAccountPage(void) +{ + 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<KOnlineBankingSetupWizard*>(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()); + + 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",""); + QListViewItem* item = pthis->m_listFi->currentItem(); + if ( item ) + kvps.setValue("bankname",item->text(0)); + + // 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'", kvps.value("bankname").data()); + } + kvps.setValue("protocol","OFX"); + + new ListViewItem( pthis->m_listAccount, kvps ); + + return 0; +} + +int KOnlineBankingSetupWizard::ofxStatusCallback(struct OfxStatusData data, void * pv) +{ + KOnlineBankingSetupWizard* pthis = reinterpret_cast<KOnlineBankingSetupWizard*>(pv); + + QString message; + + if(data.code_valid==true) + { + message += QString("#%1 %2: \"%3\"\n").arg(data.code).arg(data.name,data.description); + } + + if(data.server_message_valid==true){ + message += i18n("Server message: %1\n").arg(data.server_message); + } + + if(data.severity_valid==true){ + switch(data.severity){ + case OfxStatusData::INFO : + break; + case OfxStatusData::WARN : + KMessageBox::detailedError( pthis, i18n("Your bank returned warnings when signing on"), i18n("WARNING %1").arg(message) ); + break; + case OfxStatusData::ERROR : + KMessageBox::detailedError( pthis, i18n("Error signing onto your bank"), i18n("ERROR %1").arg(message) ); + break; + default: + break; + } + } + return 0; +} + +bool KOnlineBankingSetupWizard::chosenSettings( MyMoneyKeyValueContainer& settings ) +{ + bool result = false;; + + if ( m_fDone ) + { + QListViewItem* qitem = m_listAccount->currentItem(); + ListViewItem* item = dynamic_cast<ListViewItem*>(qitem); + if ( item ) + { + settings = *item; + settings.deletePair("appId"); + settings.deletePair("kmmofx-headerVersion"); + QString appId = m_appId->appId(); + if(!appId.isEmpty()) + settings.setValue("appId", appId); + QString hVer = m_headerVersion->headerVersion(); + if(!hVer.isEmpty()) + settings.setValue("kmmofx-headerVersion", hVer); + result = true; + } + } + + return result; +} + +KOnlineBankingSetupWizard::ListViewItem::ListViewItem( QListView* parent, const MyMoneyKeyValueContainer& kvps ): + MyMoneyKeyValueContainer( kvps ), QListViewItem( parent ) +{ + setText( 0, value("accountid") ); + setText( 1, value("type") ); + setText( 2, value("bankid") ); + setText( 3, value("branchid") ); +} + +void KOnlineBankingSetupWizard::ListViewItem::x(void) {} + +#include "konlinebankingsetupwizard.moc" + +// vim:cin:si:ai:et:ts=2:sw=2: diff --git a/kmymoney2/plugins/ofximport/dialogs/konlinebankingsetupwizard.h b/kmymoney2/plugins/ofximport/dialogs/konlinebankingsetupwizard.h new file mode 100644 index 0000000..65a89de --- /dev/null +++ b/kmymoney2/plugins/ofximport/dialogs/konlinebankingsetupwizard.h @@ -0,0 +1,106 @@ +/*************************************************************************** + konlinebankingsetupwizard.h + ------------------- + 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. * + * * + ***************************************************************************/ + +#ifndef KONLINEBANKINGSETUPWIZARD_H +#define KONLINEBANKINGSETUPWIZARD_H + +// ---------------------------------------------------------------------------- +// Library Includes + +#include <libofx/libofx.h> + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qwidget.h> +#include <qvaluelist.h> +#include <qlistview.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "konlinebankingsetupdecl.h" +#include <kmymoney/mymoneykeyvaluecontainer.h> +class OfxAppVersion; +class OfxHeaderVersion; + +/** + * @author Ace Jones + */ + +/** + * This class implementes a wizard for setting up an existing account + * with online banking. + * + * The user is asked to choose his bank from the supported bank, and + * his account. + * + * Currently works only with OFX Direct Connect, but I imagined that + * other protocols could be included here. To accomodate this, we'd + * add another page at the start of the wizard to ask which protocol + * they wanted. + * + */ +class KOnlineBankingSetupWizard : public KOnlineBankingSetupDecl +{ + Q_OBJECT +public: + class ListViewItem: public MyMoneyKeyValueContainer, public QListViewItem + { + public: + ListViewItem( QListView* parent, const MyMoneyKeyValueContainer& kvps ); + virtual void x(void); + }; + + KOnlineBankingSetupWizard(QWidget *parent=0, const char *name=0); + ~KOnlineBankingSetupWizard(); + + bool chosenSettings( MyMoneyKeyValueContainer& settings ); + + bool isInit(void) const { return m_fInit; } + +public slots: + void next(); + +protected: + bool finishAccountPage(void); + bool finishLoginPage(void); + bool finishFiPage(void); + bool post(const char* request, const char* url,const char* filename); + + static int ofxAccountCallback(struct OfxAccountData data, void * pv); + static int ofxStatusCallback(struct OfxStatusData data, void * pv); + +private: + /// \internal d-pointer class. + class Private; + /// \internal d-pointer instance. + Private* const d; + + QValueList<OfxFiServiceInfo> m_bankInfo; + QValueList<OfxFiServiceInfo>::const_iterator m_it_info; + bool m_fDone; + bool m_fInit; + OfxAppVersion* m_appId; + OfxHeaderVersion* m_headerVersion; +}; + +#endif +// vim:cin:si:ai:et:ts=2:sw=2: diff --git a/kmymoney2/plugins/ofximport/dialogs/konlinebankingstatus.cpp b/kmymoney2/plugins/ofximport/dialogs/konlinebankingstatus.cpp new file mode 100644 index 0000000..6e5cef9 --- /dev/null +++ b/kmymoney2/plugins/ofximport/dialogs/konlinebankingstatus.cpp @@ -0,0 +1,112 @@ +/*************************************************************************** + konlinebankingstatus.cpp + ------------------- + begin : Wed Apr 16 2008 + copyright : (C) 2008 by Thomas Baumgart + email : ipwizard@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. * + * * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + + +// ---------------------------------------------------------------------------- +// System Includes + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qlabel.h> +#include <qpushbutton.h> +#include <qradiobutton.h> +#include <qspinbox.h> +#include <qdatetimeedit.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <klocale.h> +#include <kled.h> +#include <kcombobox.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "konlinebankingstatus.h" +#include <kmymoney/mymoneykeyvaluecontainer.h> +#include <kmymoney/mymoneyaccount.h> +#include <libofx/libofx.h> +#include "mymoneyofxconnector.h" + +KOnlineBankingStatus::KOnlineBankingStatus(const MyMoneyAccount& acc, QWidget *parent, const char *name) : + KOnlineBankingStatusDecl(parent,name), + m_appId(0) +{ + m_ledOnlineStatus->off(); + + // Set up online banking settings if applicable + MyMoneyKeyValueContainer settings = acc.onlineBankingSettings(); + m_textOnlineStatus->setText(i18n("Enabled & configured")); + m_ledOnlineStatus->on(); + + QString account = settings.value("accountid"); + QString bank = settings.value("bankname"); + QString bankid = QString("%1 %2").arg(settings.value("bankid")).arg(settings.value("branchid")); + if ( bankid.length() > 1 ) + bank += QString(" (%1)").arg(bankid); + m_textBank->setText(bank); + m_textOnlineAccount->setText(account); + + m_appId = new OfxAppVersion(m_applicationCombo, settings.value("appId")); + m_headerVersion = new OfxHeaderVersion(m_headerVersionCombo, settings.value("kmmofx-headerVersion")); + + int numDays = 60; + QString snumDays = settings.value("kmmofx-numRequestDays"); + if (!snumDays.isEmpty()) + numDays = snumDays.toInt(); + m_numdaysSpin->setValue(numDays); + m_todayRB->setChecked(settings.value("kmmofx-todayMinus").isEmpty() || settings.value("kmmofx-todayMinus").toInt() != 0); + m_lastUpdateRB->setChecked(!settings.value("kmmofx-lastUpdate").isEmpty() && settings.value("kmmofx-lastUpdate").toInt() != 0); + m_lastUpdateTXT->setText(acc.value("lastImportedTransactionDate")); + m_pickDateRB->setChecked(!settings.value("kmmofx-pickDate").isEmpty() && settings.value("kmmofx-pickDate").toInt() != 0); + QString specificDate = settings.value("kmmofx-specificDate"); + if (!specificDate.isEmpty()) + m_specificDate->setDate(QDate::fromString(specificDate)); + else + m_specificDate->setDate(QDate::currentDate()); + m_specificDate->setMaxValue(QDate::currentDate()); + m_payeeidRB->setChecked(settings.value("kmmofx-preferPayeeid").isEmpty() || settings.value("kmmofx-preferPayeeid").toInt() != 0); + m_nameRB->setChecked(!settings.value("kmmofx-preferName").isEmpty() && settings.value("kmmofx-preferName").toInt() != 0); +} + +KOnlineBankingStatus::~KOnlineBankingStatus() +{ + delete m_appId; +} + +const QString& KOnlineBankingStatus::appId(void) const +{ + if(m_appId) + return m_appId->appId(); + return QString::null; +} + +QString KOnlineBankingStatus::headerVersion(void) const +{ + if(m_headerVersion) + return m_headerVersion->headerVersion(); + return QString::null; +} + +#include "konlinebankingstatus.moc" + diff --git a/kmymoney2/plugins/ofximport/dialogs/konlinebankingstatus.h b/kmymoney2/plugins/ofximport/dialogs/konlinebankingstatus.h new file mode 100644 index 0000000..bbd62ea --- /dev/null +++ b/kmymoney2/plugins/ofximport/dialogs/konlinebankingstatus.h @@ -0,0 +1,58 @@ +/*************************************************************************** + konlinebankingstatus.h + ------------------- + begin : Wed Apr 16 2008 + copyright : (C) 2008 by Thomas Baumgart + email : ipwizard@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. * + * * + ***************************************************************************/ + +#ifndef KONLINEBANKINGSTATUS_H +#define KONLINEBANKINGSTATUS_H + +// ---------------------------------------------------------------------------- +// Library Includes + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qwidget.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "konlinebankingstatusdecl.h" +class MyMoneyAccount; +class OfxAppVersion; +class OfxHeaderVersion; + +/** + * @author Thomas Baumgart + */ + +class KOnlineBankingStatus : public KOnlineBankingStatusDecl +{ + Q_OBJECT +public: + KOnlineBankingStatus(const MyMoneyAccount& acc, QWidget *parent=0, const char *name=0); + ~KOnlineBankingStatus(); + const QString& appId(void) const; + QString headerVersion(void) const; +private: + OfxAppVersion* m_appId; + OfxHeaderVersion* m_headerVersion; +}; + +#endif +// vim:cin:si:ai:et:ts=2:sw=2: diff --git a/kmymoney2/plugins/ofximport/dialogs/konlinebankingstatusdecl.ui b/kmymoney2/plugins/ofximport/dialogs/konlinebankingstatusdecl.ui new file mode 100644 index 0000000..f76f9e4 --- /dev/null +++ b/kmymoney2/plugins/ofximport/dialogs/konlinebankingstatusdecl.ui @@ -0,0 +1,483 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KOnlineBankingStatusDecl</class> +<widget class="QWidget"> + <property name="name"> + <cstring>KOnlineBankingStatusDecl</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>568</width> + <height>529</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox1</cstring> + </property> + <property name="title"> + <string>Account Details</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="1" column="1"> + <property name="name"> + <cstring>m_textBank</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <bold>1</bold> + </font> + </property> + <property name="text"> + <string>&lt;Not configured&gt;</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>BANK/BROKER:</string> + </property> + </widget> + <widget class="QLabel" row="2" column="1"> + <property name="name"> + <cstring>m_textOnlineAccount</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <bold>1</bold> + </font> + </property> + <property name="text"> + <string>&lt;Not configured&gt;</string> + </property> + </widget> + <widget class="QLayoutWidget" row="0" column="1"> + <property name="name"> + <cstring>layout5</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KLed"> + <property name="name"> + <cstring>m_ledOnlineStatus</cstring> + </property> + <property name="state"> + <enum>Off</enum> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>m_textOnlineStatus</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>4</vsizetype> + <horstretch>10</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Unavailable</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer4</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>41</width> + <height>21</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>ACCOUNT:</string> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>STATUS:</string> + </property> + </widget> + </grid> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox2</cstring> + </property> + <property name="title"> + <string>OFX Details</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout8</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel1_3</cstring> + </property> + <property name="text"> + <string>Header Version</string> + </property> + </widget> + <widget class="KComboBox" row="0" column="1"> + <property name="name"> + <cstring>m_applicationCombo</cstring> + </property> + </widget> + <spacer row="0" column="2"> + <property name="name"> + <cstring>spacer4_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>150</width> + <height>21</height> + </size> + </property> + </spacer> + <widget class="KComboBox" row="1" column="1"> + <property name="name"> + <cstring>m_headerVersionCombo</cstring> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="text"> + <string>Identify as</string> + </property> + </widget> + </grid> + </widget> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>buttonGroup2</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="midLineWidth"> + <number>0</number> + </property> + <property name="title"> + <string>Start date of import</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout17</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout8</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton"> + <property name="name"> + <cstring>m_todayRB</cstring> + </property> + <property name="text"> + <string>To&day minus</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <widget class="QSpinBox"> + <property name="name"> + <cstring>m_numdaysSpin</cstring> + </property> + <property name="maxValue"> + <number>180</number> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel2_2</cstring> + </property> + <property name="text"> + <string>days</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout16</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton"> + <property name="name"> + <cstring>m_lastUpdateRB</cstring> + </property> + <property name="text"> + <string>Last &update</string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>m_lastUpdateTXT</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout7</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton"> + <property name="name"> + <cstring>m_pickDateRB</cstring> + </property> + <property name="text"> + <string>Pi&ck date</string> + </property> + </widget> + <widget class="QDateEdit"> + <property name="name"> + <cstring>m_specificDate</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="order"> + <enum>YMD</enum> + </property> + <property name="date"> + <date> + <year>2000</year> + <month>1</month> + <day>1</day> + </date> + </property> + </widget> + </hbox> + </widget> + </vbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacer10</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>41</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox3</cstring> + </property> + <property name="title"> + <string>Name is derived from</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout11</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>buttonGroup1</cstring> + </property> + <property name="frameShape"> + <enum>NoFrame</enum> + </property> + <property name="title"> + <string></string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>1</number> + </property> + <widget class="QRadioButton"> + <property name="name"> + <cstring>m_payeeidRB</cstring> + </property> + <property name="text"> + <string>P&AYEEID</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>m_nameRB</cstring> + </property> + <property name="text"> + <string>&NAME</string> + </property> + </widget> + </vbox> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel3_2</cstring> + </property> + <property name="text"> + <string>field if both are present in download</string> + </property> + </widget> + </hbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacer11_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>31</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + </vbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacer11</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>50</height> + </size> + </property> + </spacer> + </vbox> +</widget> +<connections> + <connection> + <sender>m_pickDateRB</sender> + <signal>toggled(bool)</signal> + <receiver>m_specificDate</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>m_todayRB</sender> + <signal>toggled(bool)</signal> + <receiver>m_numdaysSpin</receiver> + <slot>setEnabled(bool)</slot> + </connection> +</connections> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kmymoney2/plugins/ofximport/dialogs/mymoneyofxconnector.cpp b/kmymoney2/plugins/ofximport/dialogs/mymoneyofxconnector.cpp new file mode 100644 index 0000000..6e841bb --- /dev/null +++ b/kmymoney2/plugins/ofximport/dialogs/mymoneyofxconnector.cpp @@ -0,0 +1,725 @@ +/*************************************************************************** + mymoneyofxconnector.cpp + ------------------- + begin : Sat Nov 13 2004 + copyright : (C) 2002 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. * + * * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +// ---------------------------------------------------------------------------- +// System Includes + +#include <libofx/libofx.h> + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qdatetime.h> +#include <qregexp.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <klocale.h> +#include <kdebug.h> +#include <kcombobox.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/mymoneyfile.h> +#include <kmymoney/mymoneyaccount.h> +#include <kmymoney/mymoneyinstitution.h> +#include <kmymoney/mymoneykeyvaluecontainer.h> +#include "mymoneyofxconnector.h" + +OfxHeaderVersion::OfxHeaderVersion(KComboBox* combo, const QString& headerVersion) : + m_combo(combo) +{ + combo->clear(); + combo->insertItem("102"); + combo->insertItem("103"); + + if(!headerVersion.isEmpty()) { + combo->setCurrentItem(headerVersion); + } else { + combo->setCurrentItem("102"); + } + +#if ! LIBOFX_IS_VERSION(0,9,0) + // This feature does not work with libOFX < 0.9 so + // we just make disable the button in this case + combo->setDisabled(true); +#endif +} + +QString OfxHeaderVersion::headerVersion(void) const +{ + return m_combo->currentText(); +} + +OfxAppVersion::OfxAppVersion(KComboBox* combo, const QString& appId) : + m_combo(combo) +{ +// 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/ + + // Quicken + m_appMap[i18n("Quicken Windows 2003")] = "QWIN:1200"; + m_appMap[i18n("Quicken Windows 2004")] = "QWIN:1300"; + m_appMap[i18n("Quicken Windows 2005")] = "QWIN:1400"; + m_appMap[i18n("Quicken Windows 2006")] = "QWIN:1500"; + m_appMap[i18n("Quicken Windows 2007")] = "QWIN:1600"; + m_appMap[i18n("Quicken Windows 2008")] = "QWIN:1700"; + + // MS-Money + m_appMap[i18n("MS-Money 2003")] = "Money:1100"; + m_appMap[i18n("MS-Money 2004")] = "Money:1200"; + m_appMap[i18n("MS-Money 2005")] = "Money:1400"; + m_appMap[i18n("MS-Money 2006")] = "Money:1500"; + m_appMap[i18n("MS-Money 2007")] = "Money:1600"; + m_appMap[i18n("MS-Money Plus")] = "Money:1700"; + + // KMyMoney + m_appMap["KMyMoney"] = "KMyMoney:1000"; + + combo->clear(); + combo->insertStringList(m_appMap.keys()); + + QMap<QString, QString>::const_iterator it_a; + for(it_a = m_appMap.begin(); it_a != m_appMap.end(); ++it_a) { + if(*it_a == appId) + break; + } + + if(it_a != m_appMap.end()) { + combo->setCurrentItem(it_a.key()); + } else { + combo->setCurrentItem(i18n("Quicken Windows 2008")); + } + +#if ! LIBOFX_IS_VERSION(0,9,0) + // This feature does not work with libOFX < 0.9 so + // we just make disable the button in this case + combo->setDisabled(true); +#endif +} + +const QString& OfxAppVersion::appId(void) const +{ + static QString defaultAppId("QWIN:1700"); + + QString app = m_combo->currentText(); + if(m_appMap[app] != defaultAppId) + return m_appMap[app]; + return QString::null; +} + +MyMoneyOfxConnector::MyMoneyOfxConnector(const MyMoneyAccount& _account): + m_account(_account) +{ + m_fiSettings = m_account.onlineBankingSettings(); +} + +QString MyMoneyOfxConnector::iban(void) const { return m_fiSettings.value("bankid"); } +QString MyMoneyOfxConnector::fiorg(void) const { return m_fiSettings.value("org"); } +QString MyMoneyOfxConnector::fiid(void) const { return m_fiSettings.value("fid"); } +QString MyMoneyOfxConnector::username(void) const { return m_fiSettings.value("username"); } +QString MyMoneyOfxConnector::password(void) const { return m_fiSettings.value("password"); } +QString MyMoneyOfxConnector::accountnum(void) const { return m_fiSettings.value("accountid"); } +QString MyMoneyOfxConnector::url(void) const { return m_fiSettings.value("url"); } + +QDate MyMoneyOfxConnector::statementStartDate(void) const { + if ((m_fiSettings.value("kmmofx-todayMinus").toInt() != 0) && !m_fiSettings.value("kmmofx-numRequestDays").isEmpty()) + { + return QDate::currentDate().addDays(-m_fiSettings.value("kmmofx-numRequestDays").toInt()); + } + else if ((m_fiSettings.value("kmmofx-lastUpdate").toInt() != 0) && !m_account.value("lastImportedTransactionDate").isEmpty()) + { + return QDate::fromString(m_account.value("lastImportedTransactionDate"), Qt::ISODate); + } + else if ((m_fiSettings.value("kmmofx-pickDate").toInt() != 0) && !m_fiSettings.value("kmmofx-specificDate").isEmpty()) + { + return QDate::fromString(m_fiSettings.value("kmmofx-specificDate")); + } + return QDate::currentDate().addMonths(-2); +} + +#if LIBOFX_IS_VERSION(0,9,0) +OfxAccountData::AccountType MyMoneyOfxConnector::accounttype(void) const +{ + OfxAccountData::AccountType result = OfxAccountData::OFX_CHECKING; + + QString type = m_account.onlineBankingSettings()["type"]; + if(type == "CHECKING") + result = OfxAccountData::OFX_CHECKING; + else if(type == "SAVINGS") + result = OfxAccountData::OFX_SAVINGS; + else if(type == "MONEY MARKET") + result = OfxAccountData::OFX_MONEYMRKT; + else if(type == "CREDIT LINE") + result = OfxAccountData::OFX_CREDITLINE; + else if(type == "CMA") + result = OfxAccountData::OFX_CMA; + else if(type == "CREDIT CARD") + result = OfxAccountData::OFX_CREDITCARD; + else if(type == "INVESTMENT") + result = OfxAccountData::OFX_INVESTMENT; + else { + switch( m_account.accountType()) { + case MyMoneyAccount::Investment: + result = OfxAccountData::OFX_INVESTMENT; + break; + case MyMoneyAccount::CreditCard: + result = OfxAccountData::OFX_CREDITCARD; + break; + case MyMoneyAccount::Savings: + result = OfxAccountData::OFX_SAVINGS; + break; + default: + break; + } + } + + // This is a bit of a personalized hack. Sometimes we may want to override the + // ofx type for an account. For now, I will stash it in the notes! + + QRegExp rexp("OFXTYPE:([A-Z]*)"); + if ( rexp.search(m_account.description()) != -1 ) + { + QString override = rexp.cap(1); + kdDebug(2) << "MyMoneyOfxConnector::accounttype() overriding to " << result << endl; + + if ( override == "BANK" ) + result = OfxAccountData::OFX_CHECKING; + else if ( override == "CC" ) + result = OfxAccountData::OFX_CREDITCARD; + else if ( override == "INV" ) + result = OfxAccountData::OFX_INVESTMENT; + else if ( override == "MONEYMARKET") + result = OfxAccountData::OFX_MONEYMRKT; + } + + return result; +} +#else +AccountType MyMoneyOfxConnector::accounttype(void) const +{ + AccountType result = OFX_BANK_ACCOUNT; + + switch( m_account.accountType() ) + { + case MyMoneyAccount::Investment: + result = OFX_INVEST_ACCOUNT; + break; + case MyMoneyAccount::CreditCard: + result = OFX_CREDITCARD_ACCOUNT; + break; + default: + break; + } + + // This is a bit of a personalized hack. Sometimes we may want to override the + // ofx type for an account. For now, I will stash it in the notes! + + QRegExp rexp("OFXTYPE:([A-Z]*)"); + if ( rexp.search(m_account.description()) != -1 ) + { + QString override = rexp.cap(1); + kdDebug(2) << "MyMoneyOfxConnector::accounttype() overriding to " << result << endl; + + if ( override == "BANK" ) + result = OFX_BANK_ACCOUNT; + else if ( override == "CC" ) + result = OFX_CREDITCARD_ACCOUNT; + else if ( override == "INV" ) + result = OFX_INVEST_ACCOUNT; +#if 0 // money market is not supported by 0.8.x + else if ( override == "MONEYMARKET") + result = OFX_MONEYMRKT; +#endif + } + + return result; +} +#endif + +void MyMoneyOfxConnector::initRequest(OfxFiLogin* fi) const +{ + memset(fi,0,sizeof(OfxFiLogin)); + strncpy(fi->fid, fiid().latin1(), OFX_FID_LENGTH-1); + strncpy(fi->org, fiorg().latin1(), OFX_ORG_LENGTH-1); + strncpy(fi->userid, username().latin1(), OFX_USERID_LENGTH-1); + strncpy(fi->userpass, password().latin1(), OFX_USERPASS_LENGTH-1); + +#if LIBOFX_IS_VERSION(0,9,0) + // If we don't know better, we pretend to be 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_account.onlineBankingSettings().value("appId"); + QRegExp exp("(.*):(.*)"); + if(exp.search(appId) != -1) { + strncpy(fi->appid, exp.cap(1).latin1(), OFX_APPID_LENGTH-1); + strncpy(fi->appver, exp.cap(2).latin1(), OFX_APPVER_LENGTH-1); + } else { + strncpy(fi->appid, "QWIN", OFX_APPID_LENGTH-1); + strncpy(fi->appver, "1700", OFX_APPVER_LENGTH-1); + } + + QString headerVersion = m_account.onlineBankingSettings().value("kmmofx-headerVersion"); + if(!headerVersion.isEmpty()) { + strncpy(fi->header_version, headerVersion.latin1(), OFX_HEADERVERSION_LENGTH-1); + } +#endif +} + +const QByteArray MyMoneyOfxConnector::statementRequest(void) const +{ + OfxFiLogin fi; + initRequest(&fi); + +#if LIBOFX_IS_VERSION(0,9,0) + OfxAccountData account; + memset(&account,0,sizeof(OfxAccountData)); + + if(iban().latin1() != 0) { + strncpy(account.bank_id,iban().latin1(),OFX_BANKID_LENGTH-1); + strncpy(account.broker_id,iban().latin1(),OFX_BROKERID_LENGTH-1); + } + strncpy(account.account_number,accountnum().latin1(),OFX_ACCTID_LENGTH-1); + account.account_type = accounttype(); +#else + OfxAccountInfo account; + memset(&account,0,sizeof(OfxAccountInfo)); + + if(iban().latin1() != 0) { + strncpy(account.bankid,iban().latin1(),OFX_BANKID_LENGTH-1); + strncpy(account.brokerid,iban().latin1(),OFX_BROKERID_LENGTH-1); + } + strncpy(account.accountid,accountnum().latin1(),OFX_ACCOUNT_ID_LENGTH-1); + account.type = accounttype(); +#endif + + char* szrequest = libofx_request_statement( &fi, &account, QDateTime(statementStartDate()).toTime_t() ); + QString request = szrequest; + // remove the trailing zero + QByteArray result = request.utf8(); + result.truncate(result.size()-1); + free(szrequest); + + QString msg(result); + return result; +} + +#if 0 +// this code is not used anymore. The logic is now +// contained in KOnlineBankingSetupWizard::finishLoginPage(void) +const QByteArray MyMoneyOfxConnector::accountInfoRequest(void) const +{ + OfxFiLogin fi; + initRequest(&fi); + + char* szrequest = libofx_request_accountinfo( &fi ); + QString request = szrequest; + // remove the trailing zero + QByteArray result = request.utf8(); + result.truncate(result.size()-1); + free(szrequest); + + return result; +} +#endif + +#if 0 + +MyMoneyOfxConnector::Tag MyMoneyOfxConnector::message(const QString& _msgType, const QString& _trnType, const Tag& _request) +{ + return Tag(_msgType+"MSGSRQV1") + .subtag(Tag(_trnType+"TRNRQ") + .element("TRNUID",uuid()) + .element("CLTCOOKIE","1") + .subtag(_request)); +} + +MyMoneyOfxConnector::Tag MyMoneyOfxConnector::investmentRequest(void) const +{ + QString dtnow_string = QDateTime::currentDateTime().toString(Qt::ISODate).remove(QRegExp("[^0-9]")); + QString dtstart_string = _dtstart.toString(Qt::ISODate).remove(QRegExp("[^0-9]")); + + return message("INVSTMT","INVSTMT",Tag("INVSTMTRQ") + .subtag(Tag("INVACCTFROM").element("BROKERID", fiorg()).element("ACCTID", accountnum())) + .subtag(Tag("INCTRAN").element("DTSTART",dtstart_string).element("INCLUDE","Y")) + .element("INCOO","Y") + .subtag(Tag("INCPOS").element("DTASOF", dtnow_string).element("INCLUDE","Y")) + .element("INCBAL","Y")); +} + +MyMoneyOfxConnector::Tag MyMoneyOfxConnector::bankStatementRequest(const QDate& _dtstart) const +{ + QString dtstart_string = _dtstart.toString(Qt::ISODate).remove(QRegExp("[^0-9]")); + + return message("BANK","STMT",Tag("STMTRQ") + .subtag(Tag("BANKACCTFROM").element("BANKID", iban()).element("ACCTID", accountnum()).element("ACCTTYPE", "CHECKING")) + .subtag(Tag("INCTRAN").element("DTSTART",dtstart_string).element("INCLUDE","Y"))); +} + +MyMoneyOfxConnector::Tag MyMoneyOfxConnector::creditCardRequest(const QDate& _dtstart) const +{ + QString dtstart_string = _dtstart.toString(Qt::ISODate).remove(QRegExp("[^0-9]")); + + return message("CREDITCARD","CCSTMT",Tag("CCSTMTRQ") + .subtag(Tag("CCACCTFROM").element("ACCTID",accountnum())) + .subtag(Tag("INCTRAN").element("DTSTART",dtstart_string).element("INCLUDE","Y"))); +} + +MyMoneyOfxConnector::Tag MyMoneyOfxConnector::signOn(void) const +{ + QString dtnow_string = QDateTime::currentDateTime().toString(Qt::ISODate).remove(QRegExp("[^0-9]")); + + Tag fi("FI"); + fi.element("ORG",fiorg()); + if ( !fiid().isEmpty() ) + fi.element("FID",fiid()); + + return Tag("SIGNONMSGSRQV1") + .subtag(Tag("SONRQ") + .element("DTCLIENT",dtnow_string) + .element("USERID",username()) + .element("USERPASS",password()) + .element("LANGUAGE","ENG") + .subtag(fi) + .element("APPID","QWIN") + .element("APPVER","1100")); +} + +QString MyMoneyOfxConnector::header(void) +{ + return QString("OFXHEADER:100\r\n" + "DATA:OFXSGML\r\n" + "VERSION:102\r\n" + "SECURITY:NONE\r\n" + "ENCODING:USASCII\r\n" + "CHARSET:1252\r\n" + "COMPRESSION:NONE\r\n" + "OLDFILEUID:NONE\r\n" + "NEWFILEUID:%1\r\n" + "\r\n").arg(uuid()); +} + +QString MyMoneyOfxConnector::uuid(void) +{ + static int id = 1; + return QDateTime::currentDateTime().toString("yyyyMMdd-hhmmsszzz-") + QString::number(id++); +} + +// +// Methods to provide RESPONSES to OFX requests. This has no real use in +// KMyMoney, but it's included for the purposes of unit testing. This way, I +// can create a MyMoneyAccount, write it to an OFX file, import that OFX file, +// and check that everything made it through the importer. +// +// It's also a far-off dream to write an OFX server using KMyMoney as a +// backend. It really should not be that hard, and it would fill a void in +// the open source software community. +// + +const QByteArray MyMoneyOfxConnector::statementResponse(const QDate& _dtstart) const +{ + QString request; + + if ( accounttype()=="CC" ) + request = header() + Tag("OFX").subtag(signOnResponse()).subtag(creditCardStatementResponse(_dtstart)); + else if ( accounttype()=="INV" ) + request = header() + Tag("OFX").subtag(signOnResponse()).data(investmentStatementResponse(_dtstart)); + else + request = header() + Tag("OFX").subtag(signOnResponse()).subtag(bankStatementResponse(_dtstart)); + + // remove the trailing zero + QByteArray result = request.utf8(); + result.truncate(result.size()-1); + + return result; +} + +MyMoneyOfxConnector::Tag MyMoneyOfxConnector::signOnResponse(void) const +{ + QString dtnow_string = QDateTime::currentDateTime().toString(Qt::ISODate).remove(QRegExp("[^0-9]")); + + Tag sonrs("SONRS"); + sonrs + .subtag(Tag("STATUS") + .element("CODE","0") + .element("SEVERITY","INFO") + .element("MESSAGE","The operation succeeded.") + ) + .element("DTSERVER",dtnow_string) + .element("LANGUAGE","ENG"); + + Tag fi("FI"); + if ( !fiorg().isEmpty() ) + fi.element("ORG",fiorg()); + if ( !fiid().isEmpty() ) + fi.element("FID",fiid()); + + if ( !fi.isEmpty() ) + sonrs.subtag(fi); + + return Tag("SIGNONMSGSRSV1").subtag(sonrs); +} + +MyMoneyOfxConnector::Tag MyMoneyOfxConnector::messageResponse(const QString& _msgType, const QString& _trnType, const Tag& _response) +{ + return Tag(_msgType+"MSGSRSV1") + .subtag(Tag(_trnType+"TRNRS") + .element("TRNUID",uuid()) + .subtag(Tag("STATUS").element("CODE","0").element("SEVERITY","INFO")) + .element("CLTCOOKIE","1") + .subtag(_response)); +} + +MyMoneyOfxConnector::Tag MyMoneyOfxConnector::bankStatementResponse(const QDate& _dtstart) const +{ + MyMoneyFile* file = MyMoneyFile::instance(); + + QString dtstart_string = _dtstart.toString(Qt::ISODate).remove(QRegExp("[^0-9]")); + QString dtnow_string = QDateTime::currentDateTime().toString(Qt::ISODate).remove(QRegExp("[^0-9]")); + + QString transactionlist; + + MyMoneyTransactionFilter filter; + filter.setDateFilter(_dtstart,QDate::currentDate()); + filter.addAccount(m_account.id()); + QValueList<MyMoneyTransaction> transactions = file->transactionList(filter); + QValueList<MyMoneyTransaction>::const_iterator it_transaction = transactions.begin(); + while ( it_transaction != transactions.end() ) + { + transactionlist += transaction( *it_transaction ); + ++it_transaction; + } + + return messageResponse("BANK","STMT",Tag("STMTRS") + .element("CURDEF","USD") + .subtag(Tag("BANKACCTFROM").element("BANKID", iban()).element("ACCTID", accountnum()).element("ACCTTYPE", "CHECKING")) + .subtag(Tag("BANKTRANLIST").element("DTSTART",dtstart_string).element("DTEND",dtnow_string).data(transactionlist)) + .subtag(Tag("LEDGERBAL").element("BALAMT",file->balance(m_account.id()).formatMoney(QString(),2)).element("DTASOF",dtnow_string ))); +} + +MyMoneyOfxConnector::Tag MyMoneyOfxConnector::creditCardStatementResponse(const QDate& _dtstart) const +{ + MyMoneyFile* file = MyMoneyFile::instance(); + + QString dtstart_string = _dtstart.toString(Qt::ISODate).remove(QRegExp("[^0-9]")); + QString dtnow_string = QDateTime::currentDateTime().toString(Qt::ISODate).remove(QRegExp("[^0-9]")); + + QString transactionlist; + + MyMoneyTransactionFilter filter; + filter.setDateFilter(_dtstart,QDate::currentDate()); + filter.addAccount(m_account.id()); + QValueList<MyMoneyTransaction> transactions = file->transactionList(filter); + QValueList<MyMoneyTransaction>::const_iterator it_transaction = transactions.begin(); + while ( it_transaction != transactions.end() ) + { + transactionlist += transaction( *it_transaction ); + ++it_transaction; + } + + return messageResponse("CREDITCARD","CCSTMT",Tag("CCSTMTRS") + .element("CURDEF","USD") + .subtag(Tag("CCACCTFROM").element("ACCTID", accountnum())) + .subtag(Tag("BANKTRANLIST").element("DTSTART",dtstart_string).element("DTEND",dtnow_string).data(transactionlist)) + .subtag(Tag("LEDGERBAL").element("BALAMT",file->balance(m_account.id()).formatMoney(QString(),2)).element("DTASOF",dtnow_string ))); +} + +QString MyMoneyOfxConnector::investmentStatementResponse(const QDate& _dtstart) const +{ + MyMoneyFile* file = MyMoneyFile::instance(); + + QString dtstart_string = _dtstart.toString(Qt::ISODate).remove(QRegExp("[^0-9]")); + QString dtnow_string = QDateTime::currentDateTime().toString(Qt::ISODate).remove(QRegExp("[^0-9]")); + + QString transactionlist; + + MyMoneyTransactionFilter filter; + filter.setDateFilter(_dtstart,QDate::currentDate()); + filter.addAccount(m_account.id()); + filter.addAccount(m_account.accountList()); + QValueList<MyMoneyTransaction> transactions = file->transactionList(filter); + QValueList<MyMoneyTransaction>::const_iterator it_transaction = transactions.begin(); + while ( it_transaction != transactions.end() ) + { + transactionlist += investmentTransaction( *it_transaction ); + ++it_transaction; + } + + Tag securitylist("SECLIST"); + QCStringList accountids = m_account.accountList(); + QCStringList::const_iterator it_accountid = accountids.begin(); + while ( it_accountid != accountids.end() ) + { + MyMoneySecurity equity = file->security(file->account(*it_accountid).currencyId()); + + securitylist.subtag(Tag("STOCKINFO") + .subtag(Tag("SECINFO") + .subtag(Tag("SECID") + .element("UNIQUEID",equity.id()) + .element("UNIQUEIDTYPE","KMYMONEY")) + .element("SECNAME",equity.name()) + .element("TICKER",equity.tradingSymbol()) + .element("FIID",equity.id()))); + + ++it_accountid; + } + + return messageResponse("INVSTMT","INVSTMT",Tag("INVSTMTRS") + .element("DTASOF", dtstart_string) + .element("CURDEF","USD") + .subtag(Tag("INVACCTFROM").element("BROKERID", fiorg()).element("ACCTID", accountnum())) + .subtag(Tag("INVTRANLIST").element("DTSTART",dtstart_string).element("DTEND",dtnow_string).data(transactionlist)) + ) + + Tag("SECLISTMSGSRSV1").subtag(securitylist); +} + +MyMoneyOfxConnector::Tag MyMoneyOfxConnector::transaction(const MyMoneyTransaction& _t) const +{ + // This method creates a transaction tag using ONLY the elements that importer uses + + MyMoneyFile* file = MyMoneyFile::instance(); + + //Use this version for bank/cc transactions + MyMoneySplit s = _t.splitByAccount( m_account.id(), true ); + + //TODO (Ace) Write "investmentTransaction()"... + //Use this version for inv transactions + //MyMoneySplit s = _t.splitByAccount( m_account.accountList(), true ); + + Tag result ("STMTTRN"); + + result + // This is a temporary hack. I don't use the trntype field in importing at all, + // but libofx requires it to be there in order to import the file. + .element("TRNTYPE","DEBIT") + .element("DTPOSTED",_t.postDate().toString(Qt::ISODate).remove(QRegExp("[^0-9]"))) + .element("TRNAMT",s.value().formatMoney(QString(),2)); + + if ( ! _t.bankID().isEmpty() ) + result.element("FITID",_t.bankID()); + else + result.element("FITID",_t.id()); + + if ( ! s.number().isEmpty() ) + result.element("CHECKNUM",s.number()); + + if ( ! s.payeeId().isEmpty() ) + result.element("NAME",file->payee(s.payeeId()).name()); + + if ( ! _t.memo().isEmpty() ) + result.element("MEMO",_t.memo()); + + return result; +} + +MyMoneyOfxConnector::Tag MyMoneyOfxConnector::investmentTransaction(const MyMoneyTransaction& _t) const +{ + MyMoneyFile* file = MyMoneyFile::instance(); + + //Use this version for inv transactions + MyMoneySplit s = _t.splitByAccount( m_account.accountList(), true ); + + QCString stockid = file->account(s.accountId()).currencyId(); + + Tag invtran("INVTRAN"); + invtran.element("FITID",_t.id()).element("DTTRADE",_t.postDate().toString(Qt::ISODate).remove(QRegExp("[^0-9]"))); + if ( !_t.memo().isEmpty() ) + invtran.element("MEMO",_t.memo()); + + if ( s.action() == MyMoneySplit::ActionBuyShares ) + { + if ( s.shares().isNegative() ) + { + return Tag("SELLSTOCK") + .subtag(Tag("INVSELL") + .subtag(invtran) + .subtag(Tag("SECID").element("UNIQUEID",stockid).element("UNIQUEIDTYPE","KMYMONEY")) + .element("UNITS",QString(((s.shares())).formatMoney(QString(),2)).remove(QRegExp("[^0-9.\\-]"))) + .element("UNITPRICE",QString((s.value()/s.shares()).formatMoney(QString(),2)).remove(QRegExp("[^0-9.]"))) + .element("TOTAL",QString((-s.value()).formatMoney(QString(),2)).remove(QRegExp("[^0-9.\\-]"))) + .element("SUBACCTSEC","CASH") + .element("SUBACCTFUND","CASH")) + .element("SELLTYPE","SELL"); + } + else + { + return Tag("BUYSTOCK") + .subtag(Tag("INVBUY") + .subtag(invtran) + .subtag(Tag("SECID").element("UNIQUEID",stockid).element("UNIQUEIDTYPE","KMYMONEY")) + .element("UNITS",QString((s.shares()).formatMoney(QString(),2)).remove(QRegExp("[^0-9.\\-]"))) + .element("UNITPRICE",QString((s.value()/s.shares()).formatMoney(QString(),2)).remove(QRegExp("[^0-9.]"))) + .element("TOTAL",QString((-(s.value())).formatMoney(QString(),2)).remove(QRegExp("[^0-9.\\-]"))) + .element("SUBACCTSEC","CASH") + .element("SUBACCTFUND","CASH")) + .element("BUYTYPE","BUY"); + } + } + else if ( s.action() == MyMoneySplit::ActionReinvestDividend ) + { + // Should the TOTAL tag really be negative for a REINVEST? That's very strange, but + // it's what they look like coming from my bank, and I can't find any information to refute it. + + return Tag("REINVEST") + .subtag(invtran) + .subtag(Tag("SECID").element("UNIQUEID",stockid).element("UNIQUEIDTYPE","KMYMONEY")) + .element("INCOMETYPE","DIV") + .element("TOTAL",QString((-s.value()).formatMoney(QString(),2)).remove(QRegExp("[^0-9.\\-]"))) + .element("SUBACCTSEC","CASH") + .element("UNITS",QString((s.shares()).formatMoney(QString(),2)).remove(QRegExp("[^0-9.\\-]"))) + .element("UNITPRICE",QString((s.value()/s.shares()).formatMoney(QString(),2)).remove(QRegExp("[^0-9.]"))); + } + else if ( s.action() == MyMoneySplit::ActionDividend ) + { + // find the split with the category, which has the actual amount of the dividend + QValueList<MyMoneySplit> splits = _t.splits(); + QValueList<MyMoneySplit>::const_iterator it_split = splits.begin(); + bool found = false; + while( it_split != splits.end() ) + { + QCString accid = (*it_split).accountId(); + MyMoneyAccount acc = file->account(accid); + if ( acc.accountType() == MyMoneyAccount::Income || acc.accountType() == MyMoneyAccount::Expense ) + { + found = true; + break; + } + ++it_split; + } + + if ( found ) + return Tag("INCOME") + .subtag(invtran) + .subtag(Tag("SECID").element("UNIQUEID",stockid).element("UNIQUEIDTYPE","KMYMONEY")) + .element("INCOMETYPE","DIV") + .element("TOTAL",QString((-(*it_split).value()).formatMoney(QString(),2)).remove(QRegExp("[^0-9\\.\\-]"))) + .element("SUBACCTSEC","CASH") + .element("SUBACCTFUND","CASH"); + else + return Tag("ERROR").element("DETAILS","Unable to determine the amount of this income transaction."); + } + + //FIXME: Do something useful with these errors + return Tag("ERROR").element("DETAILS","This transaction contains an unsupported action type"); +} +#endif diff --git a/kmymoney2/plugins/ofximport/dialogs/mymoneyofxconnector.h b/kmymoney2/plugins/ofximport/dialogs/mymoneyofxconnector.h new file mode 100644 index 0000000..1091b15 --- /dev/null +++ b/kmymoney2/plugins/ofximport/dialogs/mymoneyofxconnector.h @@ -0,0 +1,128 @@ +/*************************************************************************** + mymoneyofxconnector.cpp + ------------------- + begin : Sat Nov 13 2004 + copyright : (C) 2002 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. * + * * + ***************************************************************************/ + +#ifndef MYMONEYOFXCONNECTOR_H +#define MYMONEYOFXCONNECTOR_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + + +// ---------------------------------------------------------------------------- +// Library Includes + +#include <libofx/libofx.h> + +// if OFX has a major version number defined, we'll take it +// if not, we assume 0.8.3. 0.8.3 was the last version w/o version number info +#ifdef LIBOFX_MAJOR_VERSION + #define LIBOFX_VERSION KDE_MAKE_VERSION(LIBOFX_MAJOR_VERSION, LIBOFX_MINOR_VERSION, LIBOFX_MICRO_VERSION) +#else + #define LIBOFX_VERSION KDE_MAKE_VERSION(0,8,3) +#endif +#define LIBOFX_IS_VERSION(a,b,c) (LIBOFX_VERSION >= KDE_MAKE_VERSION(a,b,c)) + +// ---------------------------------------------------------------------------- +// QT Includes + +class QDate; + +// ---------------------------------------------------------------------------- +// KDE Includes +class KComboBox; + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/mymoneykeyvaluecontainer.h> + +class MyMoneyAccount; +class MyMoneyInstitution; +class MyMoneyTransaction; + +/** + * @author Thomas Baumgart + */ +class OfxAppVersion +{ +public: + OfxAppVersion(KComboBox* combo, const QString& appId); + /** + * This method returns the currently selected application id + * as a colon separated value consisting of the application + * and version (eg. "QWIN:1700"). If current value is the + * default, an empty string is returned. + */ + const QString& appId(void) const; + +private: + QMap<QString, QString> m_appMap; + KComboBox* m_combo; +}; + +/** + * @author Thomas Baumgart + */ +class OfxHeaderVersion +{ +public: + OfxHeaderVersion(KComboBox* combo, const QString& headerVersion); + QString headerVersion(void) const; + +private: + KComboBox* m_combo; +}; + +/** +@author ace jones +*/ +class MyMoneyOfxConnector +{ +public: + MyMoneyOfxConnector(const MyMoneyAccount& _account); + QString url(void) const; + + /** + * Constructs the request for a statement. The first date + * for which transactions will be requested is determined + * by statementStartDate() + */ + const QByteArray statementRequest(void) const; + const QByteArray statementResponse(const QDate& _dtstart) const; + +private: + void initRequest(OfxFiLogin* fi) const; + QDate statementStartDate(void) const; + QString iban(void) const; + QString fiorg(void) const; + QString fiid(void) const; + QString username(void) const; + QString password(void) const; + QString accountnum(void) const; +#if LIBOFX_IS_VERSION(0,9,0) + OfxAccountData::AccountType accounttype(void) const; +#else + AccountType accounttype(void) const; +#endif + +private: + const MyMoneyAccount& m_account; + MyMoneyKeyValueContainer m_fiSettings; +}; + +#endif // OFXCONNECTOR_H diff --git a/kmymoney2/plugins/ofximport/kmm_ofximport.desktop b/kmymoney2/plugins/ofximport/kmm_ofximport.desktop new file mode 100644 index 0000000..32ff003 --- /dev/null +++ b/kmymoney2/plugins/ofximport/kmm_ofximport.desktop @@ -0,0 +1,12 @@ +[Desktop Entry] +Name=KMyMoney OFX +Comment=Add OFX importing to KMyMoney +ServiceTypes=KMyMoneyPlugin +Type=Service +Icon=connect_creating +X-KDE-Library=kmm_ofximport +X-KDE-PluginInfo-Name=KMyMoney OFX +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-EnabledByDefault=true +X-KDE-PluginInfo-Author=Ace Jones,Thomas Baumgart +X-KDE-PluginInfo-Email=acejones@users.sourceforge.net,ipwizard@users.sourceforge.net diff --git a/kmymoney2/plugins/ofximport/kmm_ofximport.rc b/kmymoney2/plugins/ofximport/kmm_ofximport.rc new file mode 100644 index 0000000..7254470 --- /dev/null +++ b/kmymoney2/plugins/ofximport/kmm_ofximport.rc @@ -0,0 +1,10 @@ +<!DOCTYPE kpartgui> +<kpartgui name="kmymoneyplugin-ofximporter" version="2"> + <MenuBar> + <Menu name="file"> + <Menu name="import" append="import_merge"> + <Action name="file_import_ofx" /> + </Menu> + </Menu> + </MenuBar> +</kpartgui> diff --git a/kmymoney2/plugins/ofximport/ofximporterplugin.cpp b/kmymoney2/plugins/ofximport/ofximporterplugin.cpp new file mode 100644 index 0000000..21a6466 --- /dev/null +++ b/kmymoney2/plugins/ofximport/ofximporterplugin.cpp @@ -0,0 +1,688 @@ +/*************************************************************************** + ofxiimporterplugin.cpp + ------------------- + begin : Sat Jan 01 2005 + copyright : (C) 2005 by Ace Jones + email : Ace Jones <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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qfile.h> +#include <qtextstream.h> +#include <qradiobutton.h> +#include <qspinbox.h> +#include <qdatetimeedit.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <kgenericfactory.h> +#include <kdebug.h> +#include <kfile.h> +#include <kurl.h> +#include <kaction.h> +#include <kmessagebox.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "ofximporterplugin.h" +#include "konlinebankingstatus.h" +#include "konlinebankingsetupwizard.h" +#include "kofxdirectconnectdlg.h" + +K_EXPORT_COMPONENT_FACTORY( kmm_ofximport, + KGenericFactory<OfxImporterPlugin>( "kmm_ofximport" ) ) + +OfxImporterPlugin::OfxImporterPlugin(QObject *parent, const char *name, const QStringList&) : + KMyMoneyPlugin::Plugin( parent, name ), + KMyMoneyPlugin::ImporterPlugin(), + m_valid( false ) +{ + setInstance(KGenericFactory<OfxImporterPlugin>::instance()); + setXMLFile("kmm_ofximport.rc"); + createActions(); +} + +OfxImporterPlugin::~OfxImporterPlugin() +{ +} + +void OfxImporterPlugin::createActions(void) +{ + new KAction(i18n("OFX..."), "", 0, this, SLOT(slotImportFile()), actionCollection(), "file_import_ofx"); +} + +void OfxImporterPlugin::slotImportFile(void) +{ + KURL url = importInterface()->selectFile(i18n("OFX import file selection"), + "", + "*.ofx *.qfx *.ofc|OFX files (*.ofx, *.qfx, *.ofc)\n*.*|All files (*.*)", + static_cast<KFile::Mode>(KFile::File | KFile::ExistingOnly)); + if(url.isValid()) { + if ( isMyFormat(url.path()) ) { + slotImportFile(url.path()); + } else { + KMessageBox::error( 0, i18n("Unable to import %1 using the OFX importer plugin. This file is not the correct format.").arg(url.prettyURL(0, KURL::StripFileProtocol)), i18n("Incorrect format")); + } + + } +} + +QString OfxImporterPlugin::formatName(void) const +{ + return "OFX"; +} + +QString OfxImporterPlugin::formatFilenameFilter(void) const +{ + return "*.ofx *.qfx *.ofc"; +} + + +bool OfxImporterPlugin::isMyFormat( const QString& filename ) const +{ + // filename is considered an Ofx file if it contains + // the tag "<OFX>" or "<OFC>" in the first 20 lines + // which contain some data. + bool result = false; + + QFile f( filename ); + if ( f.open( IO_ReadOnly ) ) + { + QTextStream ts( &f ); + + int lineCount = 20; + while ( !ts.atEnd() && !result && lineCount != 0) + { + // get a line of data and remove all unnecessary whitepace chars + QString line = ts.readLine().simplifyWhiteSpace(); + if ( line.contains("<OFX>",false) + || line.contains("<OFC>",false) ) + result = true; + // count only lines that contains some non white space chars + if(!line.isEmpty()) + lineCount--; + } + f.close(); + } + + return result; +} + +bool OfxImporterPlugin::import( const QString& filename ) +{ + m_fatalerror = i18n("Unable to parse file"); + m_valid = false; + m_errors.clear(); + m_warnings.clear(); + m_infos.clear(); + + m_statementlist.clear(); + m_securitylist.clear(); + + QCString filename_deep( filename.utf8() ); + + LibofxContextPtr ctx = libofx_get_new_context(); + Q_CHECK_PTR(ctx); + + ofx_set_transaction_cb(ctx, ofxTransactionCallback, this); + ofx_set_statement_cb(ctx, ofxStatementCallback, this); + ofx_set_account_cb(ctx, ofxAccountCallback, this); + ofx_set_security_cb(ctx, ofxSecurityCallback, this); + ofx_set_status_cb(ctx, ofxStatusCallback, this); + libofx_proc_file(ctx, filename_deep, AUTODETECT); + libofx_free_context(ctx); + + if ( m_valid ) + { + m_fatalerror = QString(); + m_valid = storeStatements(m_statementlist); + } + return m_valid; +} + +QString OfxImporterPlugin::lastError(void) const +{ + if(m_errors.count() == 0) + return m_fatalerror; + return m_errors.join("<p>"); +} + +/* __________________________________________________________________________ + * AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + * + * Static callbacks for LibOFX + * + * YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY + */ + +int OfxImporterPlugin::ofxTransactionCallback(struct OfxTransactionData data, void * pv) +{ +// kdDebug(2) << __func__ << endl; + + OfxImporterPlugin* pofx = reinterpret_cast<OfxImporterPlugin*>(pv); + MyMoneyStatement& s = pofx->back(); + + MyMoneyStatement::Transaction t; + + if(data.date_posted_valid==true) + { + QDateTime dt; + dt.setTime_t(data.date_posted, Qt::UTC); + t.m_datePosted = dt.date(); + } + else if(data.date_initiated_valid==true) + { + QDateTime dt; + dt.setTime_t(data.date_initiated, Qt::UTC); + t.m_datePosted = dt.date(); + } + + if(data.amount_valid==true) + { + t.m_amount = MyMoneyMoney(data.amount, 1000); + // if this is an investment statement, reverse the sign. not sure + // why this is needed, so I suppose it's a bit of a hack for the moment. + if (data.invtransactiontype_valid==true) + t.m_amount = -t.m_amount; + } + + if(data.check_number_valid==true) + { + t.m_strNumber = data.check_number; + } + + if(data.fi_id_valid==true) + { + t.m_strBankID = QString("ID ") + data.fi_id; + } + else if(data.reference_number_valid==true) + { + t.m_strBankID = QString("REF ") + data.reference_number; + } + // Decide whether to import NAME or PAYEEID if both are present in the download + if (pofx->m_preferName) { + if(data.name_valid==true) + { + t.m_strPayee = data.name; + } + else if(data.payee_id_valid==true) + { + t.m_strPayee = data.payee_id; + } + } + else { + if(data.payee_id_valid==true) + { + t.m_strPayee = data.payee_id; + } + else if(data.name_valid==true) + { + t.m_strPayee = data.name; + } + } + if(data.memo_valid==true){ + t.m_strMemo = data.memo; + } + + // If the payee or memo fields are blank, set them to + // the other one which is NOT blank. (acejones) + if ( t.m_strPayee.isEmpty() ) + { + // But we only create a payee for non-investment transactions (ipwizard) + if ( ! t.m_strMemo.isEmpty() && data.invtransactiontype_valid == false) + t.m_strPayee = t.m_strMemo; + } + else + { + if ( t.m_strMemo.isEmpty() ) + t.m_strMemo = t.m_strPayee; + } + + if(data.security_data_valid==true) + { + struct OfxSecurityData* secdata = data.security_data_ptr; + + if(secdata->ticker_valid==true){ + t.m_strSymbol = secdata->ticker; + } + + if(secdata->secname_valid==true){ + t.m_strSecurity = secdata->secname; + } + } + + t.m_shares = MyMoneyMoney(); + if(data.units_valid==true) + { + t.m_shares = MyMoneyMoney(data.units, 100000).reduce(); + } + + t.m_price = MyMoneyMoney(); + if(data.unitprice_valid == true) + { + t.m_price = MyMoneyMoney(data.unitprice, 100000).reduce(); + } + + t.m_fees = MyMoneyMoney(); + if(data.fees_valid==true) + { + t.m_fees += MyMoneyMoney(data.fees, 1000).reduce(); + } + + if(data.commission_valid==true) + { + t.m_fees += MyMoneyMoney(data.commission, 1000).reduce(); + } + + bool unhandledtype = false; + QString type; + + if(data.invtransactiontype_valid==true) + { + switch (data.invtransactiontype) + { + case OFX_BUYDEBT: + case OFX_BUYMF: + case OFX_BUYOPT: + case OFX_BUYOTHER: + case OFX_BUYSTOCK: + t.m_eAction = MyMoneyStatement::Transaction::eaBuy; + break; + case OFX_REINVEST: + t.m_eAction = MyMoneyStatement::Transaction::eaReinvestDividend; + break; + case OFX_SELLDEBT: + case OFX_SELLMF: + case OFX_SELLOPT: + case OFX_SELLOTHER: + case OFX_SELLSTOCK: + t.m_eAction = MyMoneyStatement::Transaction::eaSell; + break; + case OFX_INCOME: + t.m_eAction = MyMoneyStatement::Transaction::eaCashDividend; + // NOTE: With CashDividend, the amount of the dividend should + // be in data.amount. Since I've never seen an OFX file with + // cash dividends, this is an assumption on my part. (acejones) + break; + + // + // These types are all not handled. We will generate a warning for them. + // + case OFX_CLOSUREOPT: + unhandledtype = true; + type = "CLOSUREOPT (Close a position for an option)"; + break; + case OFX_INVEXPENSE: + unhandledtype = true; + type = "INVEXPENSE (Misc investment expense that is associated with a specific security)"; + break; + case OFX_JRNLFUND: + unhandledtype = true; + type = "JRNLFUND (Journaling cash holdings between subaccounts within the same investment account)"; + break; + case OFX_MARGININTEREST: + unhandledtype = true; + type = "MARGININTEREST (Margin interest expense)"; + break; + case OFX_RETOFCAP: + unhandledtype = true; + type = "RETOFCAP (Return of capital)"; + break; + case OFX_SPLIT: + unhandledtype = true; + type = "SPLIT (Stock or mutial fund split)"; + break; + case OFX_TRANSFER: + unhandledtype = true; + type = "TRANSFER (Transfer holdings in and out of the investment account)"; + break; + default: + unhandledtype = true; + type = QString("UNKNOWN %1").arg(data.invtransactiontype); + break; + } + } + else + t.m_eAction = MyMoneyStatement::Transaction::eaNone; + + // In the case of investment transactions, the 'total' is supposed to the total amount + // of the transaction. units * unitprice +/- commission. Easy, right? Sadly, it seems + // some ofx creators do not follow this in all circumstances. Therefore, we have to double- + // check the total here and adjust it if it's wrong. + +#if 0 + // Even more sadly, this logic is BROKEN. It consistently results in bogus total + // values, because of rounding errors in the price. A more through solution would + // be to test if the comission alone is causing a discrepency, and adjust in that case. + + if(data.invtransactiontype_valid==true && data.unitprice_valid) + { + double proper_total = t.m_dShares * data.unitprice + t.m_moneyFees; + if ( proper_total != t.m_moneyAmount ) + { + pofx->addWarning(QString("Transaction %1 has an incorrect total of %2. Using calculated total of %3 instead.").arg(t.m_strBankID).arg(t.m_moneyAmount).arg(proper_total)); + t.m_moneyAmount = proper_total; + } + } +#endif + + if ( unhandledtype ) + pofx->addWarning(QString("Transaction %1 has an unsupported type (%2).").arg(t.m_strBankID,type)); + else + s.m_listTransactions += t; + +// kdDebug(2) << __func__ << "return 0 " << endl; + + return 0; +} + +int OfxImporterPlugin::ofxStatementCallback(struct OfxStatementData data, void* pv) +{ +// kdDebug(2) << __func__ << endl; + + OfxImporterPlugin* pofx = reinterpret_cast<OfxImporterPlugin*>(pv); + MyMoneyStatement& s = pofx->back(); + + pofx->setValid(); + + if(data.currency_valid==true) + { + s.m_strCurrency = data.currency; + } + if(data.account_id_valid==true) + { + s.m_strAccountNumber = data.account_id; + } + + if(data.date_start_valid==true) + { + QDateTime dt; + dt.setTime_t(data.date_start, Qt::UTC); + s.m_dateBegin = dt.date(); + } + + if(data.date_end_valid==true) + { + QDateTime dt; + dt.setTime_t(data.date_end, Qt::UTC); + s.m_dateEnd = dt.date(); + } + + if(data.ledger_balance_valid==true) + { + s.m_closingBalance = MyMoneyMoney(data.ledger_balance); + } + +// kdDebug(2) << __func__ << " return 0" << endl; + + return 0; +} + +int OfxImporterPlugin::ofxAccountCallback(struct OfxAccountData data, void * pv) +{ +// kdDebug(2) << __func__ << endl; + + OfxImporterPlugin* pofx = reinterpret_cast<OfxImporterPlugin*>(pv); + pofx->addnew(); + MyMoneyStatement& s = pofx->back(); + + // Having any account at all makes an ofx statement valid + pofx->m_valid = true; + + if(data.account_id_valid==true) + { + s.m_strAccountName = data.account_name; + s.m_strAccountNumber = data.account_id; + } + if(data.bank_id_valid == true) + { + s.m_strRoutingNumber = data.bank_id; + } + if(data.broker_id_valid == true) + { + s.m_strRoutingNumber = data.broker_id; + } + if(data.currency_valid==true) + { + s.m_strCurrency = data.currency; + } + + if(data.account_type_valid==true) + { + switch(data.account_type) + { + case OfxAccountData::OFX_CHECKING : s.m_eType = MyMoneyStatement::etCheckings; + break; + case OfxAccountData::OFX_SAVINGS : s.m_eType = MyMoneyStatement::etSavings; + break; + case OfxAccountData::OFX_MONEYMRKT : s.m_eType = MyMoneyStatement::etInvestment; + break; + case OfxAccountData::OFX_CREDITLINE : s.m_eType = MyMoneyStatement::etCreditCard; + break; + case OfxAccountData::OFX_CMA : s.m_eType = MyMoneyStatement::etCreditCard; + break; + case OfxAccountData::OFX_CREDITCARD : s.m_eType = MyMoneyStatement::etCreditCard; + break; + case OfxAccountData::OFX_INVESTMENT : s.m_eType = MyMoneyStatement::etInvestment; + break; + } + } + + // ask KMyMoney for an account id + s.m_accountId = pofx->account("kmmofx-acc-ref", QString("%1-%2").arg(s.m_strRoutingNumber, s.m_strAccountNumber)).id(); + + // copy over the securities + s.m_listSecurities = pofx->m_securitylist; + +// kdDebug(2) << __func__ << " return 0" << endl; + + return 0; +} + +int OfxImporterPlugin::ofxSecurityCallback(struct OfxSecurityData data, void* pv) +{ + // kdDebug(2) << __func__ << endl; + + OfxImporterPlugin* pofx = reinterpret_cast<OfxImporterPlugin*>(pv); + MyMoneyStatement::Security sec; + + if(data.unique_id_valid==true){ + sec.m_strId = data.unique_id; + } + if(data.secname_valid==true){ + sec.m_strName = data.secname; + } + if(data.ticker_valid==true){ + sec.m_strSymbol = data.ticker; + } + + pofx->m_securitylist += sec; + + return 0; +} + +int OfxImporterPlugin::ofxStatusCallback(struct OfxStatusData data, void * pv) +{ +// kdDebug(2) << __func__ << endl; + + OfxImporterPlugin* pofx = reinterpret_cast<OfxImporterPlugin*>(pv); + QString message; + + // if we got this far, we know we were able to parse the file. + // so if it fails after here it can only because there were no actual + // accounts in the file! + pofx->m_fatalerror = "No accounts found."; + + if(data.ofx_element_name_valid==true) + message.prepend(QString("%1: ").arg(data.ofx_element_name)); + + if(data.code_valid==true) + message += QString("%1 (Code %2): %3").arg(data.name).arg(data.code).arg(data.description); + + if(data.server_message_valid==true) + message += QString(" (%1)").arg(data.server_message); + + if(data.severity_valid==true){ + switch(data.severity){ + case OfxStatusData::INFO: + pofx->addInfo( message ); + break; + case OfxStatusData::ERROR: + pofx->addError( message ); + break; + case OfxStatusData::WARN: + pofx->addWarning( message ); + break; + default: + pofx->addWarning( message ); + pofx->addWarning( "Previous message was an unknown type. 'WARNING' was assumed."); + break; + } + } + +// kdDebug(2) << __func__ << " return 0 " << endl; + + return 0; +} + +bool OfxImporterPlugin::importStatement(const MyMoneyStatement& s) +{ + qDebug("OfxImporterPlugin::importStatement start"); + return statementInterface()->import(s); +} + +const MyMoneyAccount& OfxImporterPlugin::account(const QString& key, const QString& value) const +{ + return statementInterface()->account(key, value); +} + +void OfxImporterPlugin::protocols(QStringList& protocolList) const +{ + protocolList.clear(); + protocolList << "OFX"; +} + +QWidget* OfxImporterPlugin::accountConfigTab(const MyMoneyAccount& acc, QString& name) +{ + name = i18n("Online settings"); + m_statusDlg = new KOnlineBankingStatus(acc, 0, 0); + return m_statusDlg; +} + +MyMoneyKeyValueContainer OfxImporterPlugin::onlineBankingSettings(const MyMoneyKeyValueContainer& current) +{ + MyMoneyKeyValueContainer kvp(current); + // keep the provider name in sync with the one found in kmm_ofximport.desktop + kvp["provider"] = "KMyMoney OFX"; + if(m_statusDlg) { + kvp.deletePair("appId"); + kvp.deletePair("kmmofx-headerVersion"); + if(!m_statusDlg->appId().isEmpty()) + kvp.setValue("appId", m_statusDlg->appId()); + kvp.setValue("kmmofx-headerVersion", m_statusDlg->headerVersion()); + kvp.setValue("kmmofx-numRequestDays", QString::number(m_statusDlg->m_numdaysSpin->value())); + kvp.setValue("kmmofx-todayMinus", QString::number(m_statusDlg->m_todayRB->isChecked())); + kvp.setValue("kmmofx-lastUpdate", QString::number(m_statusDlg->m_lastUpdateRB->isChecked())); + kvp.setValue("kmmofx-pickDate", QString::number(m_statusDlg->m_pickDateRB->isChecked())); + kvp.setValue("kmmofx-specificDate", m_statusDlg->m_specificDate->date().toString()); + kvp.setValue("kmmofx-preferPayeeid", QString::number(m_statusDlg->m_payeeidRB->isChecked())); + kvp.setValue("kmmofx-preferName", QString::number(m_statusDlg->m_nameRB->isChecked())); + } + return kvp; +} + +bool OfxImporterPlugin::mapAccount(const MyMoneyAccount& acc, MyMoneyKeyValueContainer& settings) +{ + Q_UNUSED(acc); + + bool rc = false; + KOnlineBankingSetupWizard wiz(0, "onlinebankingsetup"); + if(wiz.isInit()) { + if(wiz.exec() == QDialog::Accepted) { + rc = wiz.chosenSettings( settings ); + } + } + + return rc; +} + +bool OfxImporterPlugin::updateAccount(const MyMoneyAccount& acc, bool moreAccounts) +{ + Q_UNUSED(moreAccounts); + + try { + if(!acc.id().isEmpty()) { + // Save the value of preferName to be used by ofxTransactionCallback + m_preferName = acc.onlineBankingSettings().value("kmmofx-preferName").toInt() != 0; + KOfxDirectConnectDlg dlg(acc); + + connect(&dlg, SIGNAL(statementReady(const QString&)), + this, SLOT(slotImportFile(const QString&))); + + dlg.init(); + dlg.exec(); + } + } catch (MyMoneyException *e) { + KMessageBox::information(0 ,i18n("Error connecting to bank: %1").arg(e->what())); + delete e; + } + + return false; +} + +void OfxImporterPlugin::slotImportFile(const QString& url) +{ + + if(!import(url)) { + KMessageBox::error( 0, QString("<qt>%1</qt>").arg(i18n("Unable to import %1 using the OFX importer plugin. The plugin returned the following error:<p>%2").arg(url, lastError())), i18n("Importing error")); + } +} + +bool OfxImporterPlugin::storeStatements(QValueList<MyMoneyStatement>& statements) +{ + bool hasstatements = (statements.count() > 0); + bool ok = true; + bool abort = false; + + // FIXME Deal with warnings/errors coming back from plugins + /*if ( ofx.errors().count() ) + { + if ( KMessageBox::warningContinueCancelList(this,i18n("The following errors were returned from your bank"),ofx.errors(),i18n("OFX Errors")) == KMessageBox::Cancel ) + abort = true; + } + + if ( ofx.warnings().count() ) + { + if ( KMessageBox::warningContinueCancelList(this,i18n("The following warnings were returned from your bank"),ofx.warnings(),i18n("OFX Warnings"),KStdGuiItem::cont(),"ofxwarnings") == KMessageBox::Cancel ) + abort = true; + }*/ + + qDebug("OfxImporterPlugin::storeStatements() with %d statements called", static_cast<int>(statements.count())); + QValueList<MyMoneyStatement>::const_iterator it_s = statements.begin(); + while ( it_s != statements.end() && !abort ) { + ok = ok && importStatement((*it_s)); + ++it_s; + } + + if ( hasstatements && !ok ) { + KMessageBox::error( 0, i18n("Importing process terminated unexpectedly."), i18n("Failed to import all statements.")); + } + + return ( !hasstatements || ok ); +} + +#include "ofximporterplugin.moc" +// vim:cin:si:ai:et:ts=2:sw=2: diff --git a/kmymoney2/plugins/ofximport/ofximporterplugin.h b/kmymoney2/plugins/ofximport/ofximporterplugin.h new file mode 100644 index 0000000..b665439 --- /dev/null +++ b/kmymoney2/plugins/ofximport/ofximporterplugin.h @@ -0,0 +1,145 @@ +/*************************************************************************** + ofxiimporterplugin.h + ------------------- + begin : Sat Jan 01 2005 + copyright : (C) 2005 by Ace Jones + email : Ace Jones <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. * + * * + ***************************************************************************/ + +#ifndef OFXIMPORTERPLUGIN_H +#define OFXIMPORTERPLUGIN_H + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qstringlist.h> + +// ---------------------------------------------------------------------------- +// Library Includes + +#include <libofx/libofx.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "../kmymoneyplugin.h" +class KOnlineBankingStatus; + +/** +@author Ace Jones +*/ +class OfxImporterPlugin : public KMyMoneyPlugin::Plugin, public KMyMoneyPlugin::ImporterPlugin, public KMyMoneyPlugin::OnlinePlugin +{ +Q_OBJECT +public: + OfxImporterPlugin(QObject *parent = 0, const char *name = 0, const QStringList& = QStringList()); + + ~OfxImporterPlugin(); + + /** + * This method returns the english-language name of the format + * this plugin imports, e.g. "OFX" + * + * @return QString Name of the format + */ + virtual QString formatName(void) const; + + /** + * This method returns the filename filter suitable for passing to + * KFileDialog::setFilter(), e.g. "*.ofx *.qfx" which describes how + * files of this format are likely to be named in the file system + * + * @return QString Filename filter string + */ + virtual QString formatFilenameFilter(void) const; + + /** + * This method returns whether this plugin is able to import + * a particular file. + * + * @param filename Fully-qualified pathname to a file + * + * @return bool Whether the indicated file is importable by this plugin + */ + virtual bool isMyFormat( const QString& filename ) const; + + /** + * Import a file + * + * @param filename File to import + * + * @return bool Whether the import was successful. + */ + virtual bool import( const QString& filename ); + + /** + * Returns the error result of the last import + * + * @return QString English-language name of the error encountered in the + * last import, or QString() if it was successful. + * + */ + virtual QString lastError(void) const; + + QWidget* accountConfigTab(const MyMoneyAccount& acc, QString& name); + + MyMoneyKeyValueContainer onlineBankingSettings(const MyMoneyKeyValueContainer& current); + + const MyMoneyAccount& account(const QString& key, const QString& value) const; + + void protocols(QStringList& protocolList) const; + + bool mapAccount(const MyMoneyAccount& acc, MyMoneyKeyValueContainer& settings); + bool updateAccount(const MyMoneyAccount& acc, bool moreAccounts); + +protected slots: + void slotImportFile(void); + void slotImportFile(const QString& url); + +protected: + void createActions(void); + void addnew(void) { m_statementlist.push_back(MyMoneyStatement()); } + MyMoneyStatement& back(void) { return m_statementlist.back(); } + bool isValid(void) const { return m_valid; } + void setValid(void) { m_valid = true; } + void addInfo(const QString& _msg ) { m_infos+=_msg; } + void addWarning(const QString& _msg ) { m_warnings+=_msg; } + void addError(const QString& _msg ) { m_errors+=_msg; } + const QStringList& infos(void) const { return m_infos; } + const QStringList& warnings(void) const { return m_warnings; } + const QStringList& errors(void) const { return m_errors; } + bool storeStatements(QValueList<MyMoneyStatement>& statements); + bool importStatement(const MyMoneyStatement& s); + + + static int ofxTransactionCallback( struct OfxTransactionData, void* ); + static int ofxStatementCallback( struct OfxStatementData, void* ); + static int ofxAccountCallback( struct OfxAccountData, void* ); + static int ofxStatusCallback( struct OfxStatusData, void* ); + static int ofxSecurityCallback( struct OfxSecurityData, void* ); + +private: + bool m_valid; + bool m_preferName; + QValueList<MyMoneyStatement> m_statementlist; + QValueList<MyMoneyStatement::Security> m_securitylist; + QString m_fatalerror; + QStringList m_infos; + QStringList m_warnings; + QStringList m_errors; + KOnlineBankingStatus* m_statusDlg; +}; + +#endif diff --git a/kmymoney2/plugins/ofximport/ofxpartner.cpp b/kmymoney2/plugins/ofximport/ofxpartner.cpp new file mode 100644 index 0000000..d36fbb2 --- /dev/null +++ b/kmymoney2/plugins/ofximport/ofxpartner.cpp @@ -0,0 +1,429 @@ +/*************************************************************************** + ofxpartner.cpp + ---------- + begin : Fri Jan 23 2009 + copyright : (C) 2009 by Thomas Baumgart + email : Thomas Baumgart <ipwizard@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. * + * * + ***************************************************************************/ + + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qdatetime.h> +#include <qeventloop.h> +#include <qfileinfo.h> +#include <qvaluelist.h> +#include <qapplication.h> +#include <qdom.h> +#include <qregexp.h> +#include <qdir.h> +#include <qtextstream.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + + +#include <kio/job.h> +#include <klocale.h> +#include <kmessagebox.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "ofxpartner.h" + +namespace OfxPartner +{ +bool post(const QString& request, const QMap<QString, QString>& attr, const KURL& url, const KURL& filename); +bool get(const QString& request, const QMap<QString, QString>& attr, const KURL& url, const KURL& 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(void) +{ + // TODO (Ace) Check whether these files exist and are recent enough before getting them again + + struct stat filestats; + KURL fname; + + QMap<QString, QString> attr; + attr["content-type"] = "application/x-www-form-urlencoded"; + attr["accept"] = "*/*"; + + fname = directory + kBankFilename; + QFileInfo i(fname.path()); + if(needReload(i)) + post("T=1&S=*&R=1&O=0&TEST=0", attr, KURL("http://moneycentral.msn.com/money/2005/mnynet/service/ols/filist.aspx?SKU=3&VER=" VER), fname); + + fname = directory + kCcFilename; + i = QFileInfo(fname.path()); + if(needReload(i)) + post("T=2&S=*&R=1&O=0&TEST=0", attr, KURL("http://moneycentral.msn.com/money/2005/mnynet/service/ols/filist.aspx?SKU=3&VER=" VER) ,fname); + + fname = directory + kInvFilename; + i = QFileInfo(fname.path()); + if(needReload(i)) + post("T=3&S=*&R=1&O=0&TEST=0", attr, KURL("http://moneycentral.msn.com/money/2005/mnynet/service/ols/filist.aspx?SKU=3&VER=" VER), fname); +} + +static void ParseFile(QMap<QString, QString>& result, const QString& fileName, const QString& bankName) +{ + QFile f(fileName); + if(f.open(IO_ReadOnly)) { + QTextStream stream(&f); + stream.setEncoding(QTextStream::Unicode); + QString msg; + int errl, errc; + QDomDocument doc; + if(doc.setContent(stream.read(), &msg, &errl, &errc)) { + QDomNodeList olist = doc.elementsByTagName("prov"); + for(int i = 0; i < olist.count(); ++i) { + QDomNode onode = olist.item(i); + if(onode.isElement()) { + bool collectGuid = false; + QDomElement elo = onode.toElement(); + QDomNodeList ilist = onode.childNodes(); + for(int j = 0; j < ilist.count(); ++j) { + QDomNode inode = ilist.item(j); + QDomElement el = inode.toElement(); + if(el.tagName() == "name") { + if(bankName.isEmpty()) + result[el.text()] = QString(); + else if(el.text() == bankName) { + collectGuid = true; + } + } + if(el.tagName() == "guid" && collectGuid) { + result[el.text()] = QString(); + } + } + } + } + } + f.close(); + } +} + +QValueList<QString> BankNames(void) +{ + QMap<QString, QString> result; + + // Make sure the index files are up to date + ValidateIndexCache(); + + ParseFile(result, directory + kBankFilename, QString()); + ParseFile(result, directory + kCcFilename, QString()); + ParseFile(result, directory + kInvFilename, QString()); + + // Add Innovision + result["Innovision"] = QString(); + + return result.keys(); +} + +QValueList<QString> FipidForBank(const QString& bank) +{ + QMap<QString, QString> result; + + ParseFile(result, directory + kBankFilename, bank); + ParseFile(result, directory + kCcFilename, bank); + ParseFile(result, directory + kInvFilename, bank); + + // the fipid for Innovision is 1. + if ( bank == "Innovision" ) + result["1"] = QString(); + + return result.keys(); +} + +QString extractNodeText(QDomElement& node, const QString& name) +{ + QString res; + QRegExp exp("([^/]+)/?([^/].*)?"); + if(exp.search(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.search(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) +{ + OfxFiServiceInfo result; + memset(&result, 0, sizeof(OfxFiServiceInfo)); + + // 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; + + return result; + } + + QMap<QString, QString> attr; + attr["content-type"] = "application/x-www-form-urlencoded"; + attr["accept"] = "*/*"; + + KURL guidFile(QString("%1fipid-%2.xml").arg(directory).arg(fipid)); + + // Apparently at some point in time, for VER=6 msn returned an online URL + // to a static error page (http://moneycentral.msn.com/cust404.htm). + // Increasing to VER=9 solved the problem. This may happen again in the + // future. + QFileInfo i(guidFile.path()); + if(!i.isReadable() || i.lastModified().addDays(7) < QDateTime::currentDateTime()) + get("", attr, KURL(QString("http://moneycentral.msn.com/money/2005/mnynet/service/olsvcupd/OnlSvcBrandInfo.aspx?MSNGUID=&GUID=%1&SKU=3&VER=" VER).arg(fipid)), guidFile); + + QFile f(guidFile.path()); + if(f.open(IO_ReadOnly)) { + QTextStream stream(&f); + stream.setEncoding(QTextStream::Unicode); + QString msg; + int errl, errc; + QDomDocument doc; + if(doc.setContent(stream.read(), &msg, &errl, &errc)) { + QString fid = extractNodeText(doc, "ProviderSettings/FID"); + QString org = extractNodeText(doc, "ProviderSettings/Org"); + QString url = extractNodeText(doc, "ProviderSettings/ProviderURL"); + strncpy(result.fid, fid.latin1(), OFX_FID_LENGTH-1); + strncpy(result.org, org.latin1(), OFX_ORG_LENGTH-1); + strncpy(result.url, url.latin1(), OFX_URL_LENGTH-1); + result.accountlist = (extractNodeText(doc, "ProviderSettings/AcctListAvail") == "1"); + result.statements = (extractNodeText(doc, "BankingCapabilities/Bank") == "1"); + result.billpay= (extractNodeText(doc, "BillPayCapabilities/Pay") == "1"); + result.investments= (extractNodeText(doc, "InvestmentCapabilities/BrkStmt") == "1"); + } + } + + return result; +} + +bool get(const QString& request, const QMap<QString, QString>& attr, const KURL& url, const KURL& filename) +{ + QByteArray req(0); + OfxHttpRequest job("GET", url, req, attr, filename, true); + + return job.error() == QHttp::NoError; +} + +bool post(const QString& request, const QMap<QString, QString>& attr, const KURL& url, const KURL& filename) +{ + QByteArray req; + req.fill(0, request.length()+1); + req.duplicate(request.ascii(), request.length()); + + OfxHttpRequest job("POST", url, req, attr, filename, true); + return job.error() == QHttp::NoError; +} + +} // namespace OfxPartner + +class OfxHttpsRequest::Private +{ +public: + QFile m_fpTrace; +}; + +OfxHttpsRequest::OfxHttpsRequest(const QString& type, const KURL &url, const QByteArray &postData, const QMap<QString, QString>& metaData, const KURL& dst, bool showProgressInfo) : + d(new Private), + m_dst(dst) +{ + QDir homeDir(QDir::home()); + if(homeDir.exists("ofxlog.txt")) { + d->m_fpTrace.setName(QString("%1/ofxlog.txt").arg(QDir::homeDirPath())); + d->m_fpTrace.open(IO_WriteOnly | IO_Append); + } + + m_job = KIO::http_post(url, postData, showProgressInfo); + m_job->addMetaData("content-type", "Content-type: application/x-ofx" ); + + if(d->m_fpTrace.isOpen()) { + QTextStream ts(&d->m_fpTrace); + ts << "url: " << url.prettyURL() << "\n"; + ts << "request:\n" << QString(postData) << "\n" << "response:\n"; + } + + connect(m_job,SIGNAL(result(KIO::Job*)),this,SLOT(slotOfxFinished(KIO::Job*))); + connect(m_job,SIGNAL(data(KIO::Job*, const QByteArray&)),this,SLOT(slotOfxData(KIO::Job*,const QByteArray&))); + connect(m_job,SIGNAL(connected(KIO::Job*)),this,SLOT(slotOfxConnected(KIO::Job*))); + + qApp->enter_loop(); +} + +OfxHttpsRequest::~OfxHttpsRequest() +{ + if(d->m_fpTrace.isOpen()) { + d->m_fpTrace.close(); + } +} + +void OfxHttpsRequest::slotOfxConnected(KIO::Job*) +{ + m_file.setName(m_dst.path()); + m_file.open(IO_WriteOnly); +} + +void OfxHttpsRequest::slotOfxData(KIO::Job*,const QByteArray& _ba) +{ + if(m_file.isOpen()) { + QTextStream ts(&m_file); + ts << QString(_ba); + + if(d->m_fpTrace.isOpen()) { + d->m_fpTrace.writeBlock(_ba, _ba.size()); + } + + + } +} + +void OfxHttpsRequest::slotOfxFinished(KIO::Job* /* e */) +{ + if(m_file.isOpen()) { + m_file.close(); + if(d->m_fpTrace.isOpen()) { + d->m_fpTrace.writeBlock("\nCompleted\n\n\n\n", 14); + } + } + + int error = m_job->error(); + if ( error ) { + m_job->showErrorDialog(); + unlink(m_dst.path()); + + } else if ( m_job->isErrorPage() ) { + QString details; + QFile f( m_dst.path() ); + if ( f.open( IO_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, i18n("Failed") ); + unlink(m_dst.path()); + } + + qApp->exit_loop(); +} + + + +OfxHttpRequest::OfxHttpRequest(const QString& type, const KURL &url, const QByteArray &postData, const QMap<QString, QString>& metaData, const KURL& dst, bool showProgressInfo) +{ + QFile f(dst.path()); + m_error = QHttp::NoError; + QString errorMsg; + if(f.open(IO_WriteOnly)) { + m_job = new QHttp(url.host()); + QHttpRequestHeader header(type, url.encodedPathAndQuery()); + header.setValue("Host", url.host()); + QMap<QString, QString>::const_iterator it; + for(it = metaData.begin(); it != metaData.end(); ++it) { + header.setValue(it.key(), *it); + } + + m_job->request(header, postData, &f); + + connect(m_job, SIGNAL(requestFinished(int, bool)), + this, SLOT(slotOfxFinished(int, bool))); + + qApp->enter_loop(); + + if(m_error != QHttp::NoError) + errorMsg = m_job->errorString(); + + delete m_job; + } else { + m_error = QHttp::Aborted; + errorMsg = i18n("Cannot open file %1 for writing").arg(dst.path()); + } + + if(m_error != QHttp::NoError) { + KMessageBox::error(0, errorMsg, i18n("OFX setup error")); + unlink(dst.path()); + } +} + +void OfxHttpRequest::slotOfxFinished(int, bool rc) +{ + if(rc) { + m_error = m_job->error(); + } + qApp->exit_loop(); +} + +#include "ofxpartner.moc" + +// vim:cin:si:ai:et:ts=2:sw=2: diff --git a/kmymoney2/plugins/ofximport/ofxpartner.h b/kmymoney2/plugins/ofximport/ofxpartner.h new file mode 100644 index 0000000..e624282 --- /dev/null +++ b/kmymoney2/plugins/ofximport/ofxpartner.h @@ -0,0 +1,102 @@ +/*************************************************************************** + ofxpartner.h + ---------- + begin : Fri Jan 23 2009 + copyright : (C) 2009 by Thomas Baumgart + email : Thomas Baumgart <ipwizard@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. * + * * + ***************************************************************************/ + +#ifndef OFXPARTNER_H +#define OFXPARTNER_H + +// ---------------------------------------------------------------------------- +// QT Includes + + +#include <qobject.h> +#include <qhttp.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <kurl.h> +namespace KIO +{ + class Job; + class TransferJob; +} + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <libofx/libofx.h> + +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(void); + OfxFiServiceInfo ServiceInfo(const QString& fipid); + QValueList<QString> BankNames(void); + QValueList<QString> FipidForBank(const QString& bank); + +} + +class OfxHttpRequest : public QObject +{ + Q_OBJECT +public: + OfxHttpRequest(const QString& method, const KURL &url, const QByteArray &postData, const QMap<QString, QString>& metaData, const KURL& dst, bool showProgressInfo=true); + virtual ~OfxHttpRequest() {} + + QHttp::Error error(void) const { return m_error; } + +protected slots: + void slotOfxFinished(int, bool); + +private: + QHttp* m_job; + KURL m_dst; + QHttp::Error m_error; + +}; + +class OfxHttpsRequest : public QObject +{ +Q_OBJECT +public: + OfxHttpsRequest(const QString& method, const KURL &url, const QByteArray &postData, const QMap<QString, QString>& metaData, const KURL& dst, bool showProgressInfo=true); + virtual ~OfxHttpsRequest(); + + QHttp::Error error(void) const { return m_error; } + +protected slots: + void slotOfxFinished(KIO::Job*); + void slotOfxData(KIO::Job*,const QByteArray&); + void slotOfxConnected(KIO::Job*); + +private: + class Private; + Private* d; + KURL m_dst; + QFile m_file; + QHttp::Error m_error; + KIO::TransferJob* m_job; +}; +#endif // OFXPARTNER_H diff --git a/kmymoney2/plugins/pluginloader.cpp b/kmymoney2/plugins/pluginloader.cpp new file mode 100644 index 0000000..0201337 --- /dev/null +++ b/kmymoney2/plugins/pluginloader.cpp @@ -0,0 +1,163 @@ +/*************************************************************************** + pluginloader.cpp + ------------------- + begin : Thu Feb 12 2009 + copyright : (C) 2009 Cristian Onet + email : onet.cristian@gmail.com + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qstringlist.h> +#include <qcheckbox.h> +#include <qlayout.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <ktrader.h> +#include <kparts/componentfactory.h> +#include <kdebug.h> +#include <kdialog.h> +#include <kconfig.h> +#include <kpluginselector.h> +#include <klocale.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "kmymoneyplugin.h" +#include "pluginloader.h" + +namespace KMyMoneyPlugin { + +//--------------------------------------------------------------------- +// +// PluginLoader +// +//--------------------------------------------------------------------- +static PluginLoader* s_instance = 0; + +typedef QMap<QString, Plugin*> PluginsMap; + +struct PluginLoader::Private +{ + QObject* m_parent; + KPluginInfo::List m_pluginList; + KPluginSelector* m_pluginSelector; + PluginsMap m_loadedPlugins; +}; + +PluginLoader::PluginLoader(QObject* parent) +{ + Q_ASSERT( s_instance == 0 ); + s_instance = this; + + d = new Private; + + d->m_parent = parent; + + KTrader::OfferList offers = KTrader::self()->query("KMyMoneyPlugin"); + d->m_pluginList = KPluginInfo::fromServices(offers); + + d->m_pluginSelector = new KPluginSelector(NULL); + d->m_pluginSelector->setShowEmptyConfigPage(false); + d->m_pluginSelector->addPlugins(d->m_pluginList); + d->m_pluginSelector->load(); + + connect(d->m_pluginSelector, SIGNAL(changed(bool)), this, SLOT(changed())); + connect(d->m_pluginSelector, SIGNAL(configCommitted(const QCString &)), this, SLOT(changedConfigOfPlugin(const QCString &))); +} + +PluginLoader::~PluginLoader() +{ + delete d; +} + +void PluginLoader::loadPlugins() +{ + for( KPluginInfo::List::Iterator it = d->m_pluginList.begin(); it != d->m_pluginList.end(); ++it ) + loadPlugin( *it ); +} + +void PluginLoader::loadPlugin(KPluginInfo* info) +{ + if (info->isPluginEnabled()) { + Plugin* plugin = getPluginFromInfo(info); + + if (!plugin) { + // the plugin is enabled but it is not loaded + KService::Ptr service = info->service(); + int error = 0; + Plugin* plugin = KParts::ComponentFactory + ::createInstanceFromService<Plugin>(service, d->m_parent, info->name().utf8(), QStringList(), &error); + if (plugin) { + kdDebug() << "KMyMoneyPlugin::PluginLoader: Loaded plugin " << plugin->name() << endl; + d->m_loadedPlugins.insert(info->name(), plugin); + emit PluginLoader::instance()->plug(info); + } + else { + kdWarning() << "KMyMoneyPlugin::PluginLoader:: createInstanceFromService returned 0 for " + << info->name() + << " with error number " + << error << endl; + if (error == KParts::ComponentFactory::ErrNoLibrary) + kdWarning() << "KLibLoader says: " + << KLibLoader::self()->lastErrorMessage() << endl; + } + } + } + else { + if (getPluginFromInfo(info) != NULL) { + // everybody interested should say goodbye to the plugin + emit PluginLoader::instance()->unplug(info); + d->m_loadedPlugins.erase(info->name()); + } + } +} + +void PluginLoader::changed() +{ + loadPlugins(); +} + +void PluginLoader::changedConfigOfPlugin(const QCString & name) +{ + PluginsMap::iterator itPlugin = d->m_loadedPlugins.find(QString(name)); + if (itPlugin != d->m_loadedPlugins.end()) + configChanged(*itPlugin); +} + +Plugin* PluginLoader::getPluginFromInfo(KPluginInfo* info) +{ + PluginsMap::iterator itPlugin = d->m_loadedPlugins.find(info->name()); + if (itPlugin != d->m_loadedPlugins.end()) + return *itPlugin; + else + return NULL; +} + +PluginLoader* PluginLoader::instance() +{ + Q_ASSERT( s_instance != 0); + return s_instance; +} + +KPluginSelector* PluginLoader::pluginSelectorWidget() +{ + return d->m_pluginSelector; +} + +} // namespace + +#include "pluginloader.moc" diff --git a/kmymoney2/plugins/pluginloader.h b/kmymoney2/plugins/pluginloader.h new file mode 100644 index 0000000..4d0c500 --- /dev/null +++ b/kmymoney2/plugins/pluginloader.h @@ -0,0 +1,76 @@ +/*************************************************************************** + pluginloader.h + ------------------- + begin : Thu Feb 12 2009 + copyright : (C) 2009 Cristian Onet + email : onet.cristian@gmail.com + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 PLUGINLOADER_H +#define PLUGINLOADER_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qwidget.h> +#include <qscrollview.h> + +// ---------------------------------------------------------------------------- +// KDE Includes +#include <kplugininfo.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/export.h> + +class KPluginSelector; + +namespace KMyMoneyPlugin +{ + class Plugin; + + class KMYMONEY_EXPORT PluginLoader : public QObject + { + Q_OBJECT + public: + PluginLoader(QObject* parent); + virtual ~PluginLoader(); + static PluginLoader* instance(); + + void loadPlugins(); + Plugin* getPluginFromInfo(KPluginInfo*); + KPluginSelector* pluginSelectorWidget(); + + private: + void loadPlugin(KPluginInfo*); + + signals: + void plug(KPluginInfo*); + void unplug(KPluginInfo*); + void configChanged(Plugin*); // consfiguration of the plugin has changed not the enabled/disabled state + + private slots: + void changed(); + void changedConfigOfPlugin( const QCString & ); + + private: + struct Private; + Private* d; + }; +} + +#endif /* PLUGINLOADER_H */ diff --git a/kmymoney2/plugins/statementinterface.cpp b/kmymoney2/plugins/statementinterface.cpp new file mode 100644 index 0000000..753db2e --- /dev/null +++ b/kmymoney2/plugins/statementinterface.cpp @@ -0,0 +1,34 @@ +/*************************************************************************** + statementinterface.cpp + ------------------- + begin : Wed Jan 5 2005 + copyright : (C) 2005 Thomas Baumgart + email : ipwizard@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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "statementinterface.h" + +KMyMoneyPlugin::StatementInterface::StatementInterface(QObject* parent, const char* name) : + QObject(parent, name) +{ +} + +#include "statementinterface.moc" diff --git a/kmymoney2/plugins/statementinterface.h b/kmymoney2/plugins/statementinterface.h new file mode 100644 index 0000000..a3a31d1 --- /dev/null +++ b/kmymoney2/plugins/statementinterface.h @@ -0,0 +1,72 @@ +/*************************************************************************** + statementinterface.h + ------------------- + begin : Wed Jan 5 2005 + copyright : (C) 2005 Thomas Baumgart + email : ipwizard@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. * + * * + ***************************************************************************/ + +#ifndef STATEMENTINTERFACE_H +#define STATEMENTINTERFACE_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qobject.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/mymoneystatement.h> +#include <kmymoney/mymoneyaccount.h> +#include <kmymoney/export.h> + +namespace KMyMoneyPlugin { + +/** + * This abstract class represents the interface to import statements + * into the KMyMoney application + */ +class KMYMONEY_EXPORT StatementInterface : public QObject { + Q_OBJECT + +public: + StatementInterface(QObject* parent, const char* name = 0); + ~StatementInterface() {} + + /** + * This method imports a MyMoneyStatement into the engine + */ + virtual bool import(const MyMoneyStatement& s) = 0; + + /** + * This method returns the account for a given @a key - @a value pair. + * If the account is not found in the list of accounts, MyMoneyAccount() + * is returned. + */ + virtual const MyMoneyAccount& account(const QString& key, const QString& value) const = 0; + + /** + */ + virtual void setAccountOnlineParameters(const MyMoneyAccount& acc, const MyMoneyKeyValueContainer& kvps) const = 0; + +}; + +}; // namespace +#endif diff --git a/kmymoney2/plugins/viewinterface.cpp b/kmymoney2/plugins/viewinterface.cpp new file mode 100644 index 0000000..4db12f1 --- /dev/null +++ b/kmymoney2/plugins/viewinterface.cpp @@ -0,0 +1,34 @@ +/*************************************************************************** + viewinterface.cpp + ------------------- + begin : Wed Jan 5 2005 + copyright : (C) 2005 Thomas Baumgart + email : ipwizard@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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "viewinterface.h" + +KMyMoneyPlugin::ViewInterface::ViewInterface(QObject* parent, const char* name) : + QObject(parent, name) +{ +} + +#include "viewinterface.moc" diff --git a/kmymoney2/plugins/viewinterface.h b/kmymoney2/plugins/viewinterface.h new file mode 100644 index 0000000..d7c7424 --- /dev/null +++ b/kmymoney2/plugins/viewinterface.h @@ -0,0 +1,123 @@ +/*************************************************************************** + viewinterface.h + ------------------- + begin : Wed Jan 5 2005 + copyright : (C) 2005 Thomas Baumgart + email : ipwizard@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. * + * * + ***************************************************************************/ + +#ifndef VIEWINTERFACE_H +#define VIEWINTERFACE_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qobject.h> +#include <qstring.h> +#include <qpixmap.h> +class QFrame; + +// ---------------------------------------------------------------------------- +// KDE Includes + +class KPopupMenu; + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/mymoneyaccount.h> +#include <kmymoney/mymoneyinstitution.h> +#include <kmymoney/export.h> +class KMyMoneyViewBase; +namespace KMyMoneyRegister { + class SelectedTransactions; +}; + +namespace KMyMoneyPlugin { + +/** + * This abstract class represents the ViewInterface to + * add new view pages to the JanusWidget of KMyMoney. It + * also gives access to the account context menu. + */ +class KMYMONEY_EXPORT ViewInterface : public QObject { + Q_OBJECT + +public: + ViewInterface(QObject* parent, const char* name = 0); + ~ViewInterface() {} + + /** + * This method creates a new page in the application. + * See KJanusWidget::addPage() for details. + */ + virtual KMyMoneyViewBase* addPage(const QString& item, const QString& icon) = 0; + + /** + * This method adds a widget to the layout of the view + * created with addPage() + * + * @param view pointer to view widget + * @param w widget to be added to @p page + */ + virtual void addWidget(KMyMoneyViewBase* view, QWidget* w) = 0; + +signals: + /** + * This signal is emitted when a new account has been selected by + * the GUI. If no account is selected or the selection is removed, + * @a account is identical to MyMoneyAccount(). This signal is used + * by plugins to get information about changes. + */ + void accountSelected(const MyMoneyAccount& acc); + + /** + * This signal is emitted when a transaction/list of transactions has been selected by + * the GUI. If no transaction is selected or the selection is removed, + * @p transactions is identical to an empty QValueList. This signal is used + * by plugins to get information about changes. + */ + void transactionsSelected(const KMyMoneyRegister::SelectedTransactions& transactions); + + /** + * This signal is emitted when a new institution has been selected by + * the GUI. If no institution is selected or the selection is removed, + * @a institution is identical to MyMoneyInstitution(). This signal is used + * by plugins to get information about changes. + */ + void institutionSelected(const MyMoneyInstitution& institution); + + /** + * This signal is emitted when an account has been successfully reconciled + * and all transactions are updated in the engine. It can be used by plugins + * to create reconciliation reports. + * + * @param account the account data + * @param date the reconciliation date as provided through the dialog + * @param startingBalance the starting balance as provided through the dialog + * @param endingBalance the ending balance as provided through the dialog + * @param transactionList reference to QValueList of QPair containing all + * transaction/split pairs processed by the reconciliation. + */ + void accountReconciled(const MyMoneyAccount& account, const QDate& date, const MyMoneyMoney& startingBalance, const MyMoneyMoney& endingBalance, const QValueList<QPair<MyMoneyTransaction, MyMoneySplit> >& transactionList); + + + void viewStateChanged(bool); + void kmmFilePlugin(unsigned int); +}; + +}; // namespace +#endif |