/*************************************************************************** ofxpartner.cpp ---------- begin : Fri Jan 23 2009 copyright : (C) 2009 by Thomas Baumgart email : Thomas Baumgart ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifdef HAVE_CONFIG_H #include #endif #include // ---------------------------------------------------------------------------- // TQt Includes #include #include #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- // TDE Includes #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "ofxpartner.h" namespace OfxPartner { bool post(const TQString& request, const TQMap& attr, const KURL& url, const KURL& filename); bool get(const TQString& request, const TQMap& attr, const KURL& url, const KURL& filename); const TQString kBankFilename = "ofx-bank-index.xml"; const TQString kCcFilename = "ofx-cc-index.xml"; const TQString kInvFilename = "ofx-inv-index.xml"; #define VER "9" static TQString directory; void setDirectory(const TQString& dir) { directory = dir; } bool needReload(const TQFileInfo& i) { return ((!i.isReadable()) || (i.lastModified().addDays(7) < TQDateTime::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; TQMap attr; attr["content-type"] = "application/x-www-form-urlencoded"; attr["accept"] = "*/*"; fname = directory + kBankFilename; TQFileInfo 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 = TQFileInfo(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 = TQFileInfo(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(TQMap& result, const TQString& fileName, const TQString& bankName) { TQFile f(fileName); if(f.open(IO_ReadOnly)) { TQTextStream stream(&f); stream.setEncoding(TQTextStream::Unicode); TQString msg; int errl, errc; TQDomDocument doc; if(doc.setContent(stream.read(), &msg, &errl, &errc)) { TQDomNodeList olist = doc.elementsByTagName("prov"); for(int i = 0; i < olist.count(); ++i) { TQDomNode onode = olist.item(i); if(onode.isElement()) { bool collectGuid = false; TQDomElement elo = onode.toElement(); TQDomNodeList ilist = onode.childNodes(); for(int j = 0; j < ilist.count(); ++j) { TQDomNode inode = ilist.item(j); TQDomElement el = inode.toElement(); if(el.tagName() == "name") { if(bankName.isEmpty()) result[el.text()] = TQString(); else if(el.text() == bankName) { collectGuid = true; } } if(el.tagName() == "guid" && collectGuid) { result[el.text()] = TQString(); } } } } } f.close(); } } TQValueList BankNames(void) { TQMap result; // Make sure the index files are up to date ValidateIndexCache(); ParseFile(result, directory + kBankFilename, TQString()); ParseFile(result, directory + kCcFilename, TQString()); ParseFile(result, directory + kInvFilename, TQString()); // Add Innovision result["Innovision"] = TQString(); return result.keys(); } TQValueList FipidForBank(const TQString& bank) { TQMap 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"] = TQString(); return result.keys(); } TQString extractNodeText(TQDomElement& node, const TQString& name) { TQString res; TQRegExp exp("([^/]+)/?([^/].*)?"); if(exp.search(name) != -1) { TQDomNodeList olist = node.elementsByTagName(exp.cap(1)); if(olist.count()) { TQDomNode onode = olist.item(0); if(onode.isElement()) { TQDomElement elo = onode.toElement(); if(exp.cap(2).isEmpty()) { res = elo.text(); } else { res = extractNodeText(elo, exp.cap(2)); } } } } return res; } TQString extractNodeText(TQDomDocument& doc, const TQString& name) { TQString res; TQRegExp exp("([^/]+)/?([^/].*)?"); if(exp.search(name) != -1) { TQDomNodeList olist = doc.elementsByTagName(exp.cap(1)); if(olist.count()) { TQDomNode onode = olist.item(0); if(onode.isElement()) { TQDomElement elo = onode.toElement(); if(exp.cap(2).isEmpty()) { res = elo.text(); } else { res = extractNodeText(elo, exp.cap(2)); } } } } return res; } OfxFiServiceInfo ServiceInfo(const TQString& 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; } TQMap attr; attr["content-type"] = "application/x-www-form-urlencoded"; attr["accept"] = "*/*"; KURL guidFile(TQString("%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. TQFileInfo i(guidFile.path()); if(!i.isReadable() || i.lastModified().addDays(7) < TQDateTime::currentDateTime()) get("", attr, KURL(TQString("http://moneycentral.msn.com/money/2005/mnynet/service/olsvcupd/OnlSvcBrandInfo.aspx?MSNGUID=&GUID=%1&SKU=3&VER=" VER).arg(fipid)), guidFile); TQFile f(guidFile.path()); if(f.open(IO_ReadOnly)) { TQTextStream stream(&f); stream.setEncoding(TQTextStream::Unicode); TQString msg; int errl, errc; TQDomDocument doc; if(doc.setContent(stream.read(), &msg, &errl, &errc)) { TQString fid = extractNodeText(doc, "ProviderSettings/FID"); TQString org = extractNodeText(doc, "ProviderSettings/Org"); TQString 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 TQString& request, const TQMap& attr, const KURL& url, const KURL& filename) { TQByteArray req(0); OfxHttpRequest job("GET", url, req, attr, filename, true); return job.error() == TQHttp::NoError; } bool post(const TQString& request, const TQMap& attr, const KURL& url, const KURL& filename) { TQByteArray req; req.fill(0, request.length()+1); req.duplicate(request.ascii(), request.length()); OfxHttpRequest job("POST", url, req, attr, filename, true); return job.error() == TQHttp::NoError; } } // namespace OfxPartner class OfxHttpsRequest::Private { public: TQFile m_fpTrace; }; OfxHttpsRequest::OfxHttpsRequest(const TQString& type, const KURL &url, const TQByteArray &postData, const TQMap& metaData, const KURL& dst, bool showProgressInfo) : d(new Private), m_dst(dst) { TQDir homeDir(TQDir::home()); if(homeDir.exists("ofxlog.txt")) { d->m_fpTrace.setName(TQString("%1/ofxlog.txt").arg(TQDir::homeDirPath())); d->m_fpTrace.open(IO_WriteOnly | IO_Append); } m_job = TDEIO::http_post(url, postData, showProgressInfo); m_job->addMetaData("content-type", "Content-type: application/x-ofx" ); if(d->m_fpTrace.isOpen()) { TQTextStream ts(&d->m_fpTrace); ts << "url: " << url.prettyURL() << "\n"; ts << "request:\n" << TQString(postData) << "\n" << "response:\n"; } connect(m_job,TQ_SIGNAL(result(TDEIO::Job*)),this,TQ_SLOT(slotOfxFinished(TDEIO::Job*))); connect(m_job,TQ_SIGNAL(data(TDEIO::Job*, const TQByteArray&)),this,TQ_SLOT(slotOfxData(TDEIO::Job*,const TQByteArray&))); connect(m_job,TQ_SIGNAL(connected(TDEIO::Job*)),this,TQ_SLOT(slotOfxConnected(TDEIO::Job*))); tqApp->enter_loop(); } OfxHttpsRequest::~OfxHttpsRequest() { if(d->m_fpTrace.isOpen()) { d->m_fpTrace.close(); } } void OfxHttpsRequest::slotOfxConnected(TDEIO::Job*) { m_file.setName(m_dst.path()); m_file.open(IO_WriteOnly); } void OfxHttpsRequest::slotOfxData(TDEIO::Job*,const TQByteArray& _ba) { if(m_file.isOpen()) { TQTextStream ts(&m_file); ts << TQString(_ba); if(d->m_fpTrace.isOpen()) { d->m_fpTrace.writeBlock(_ba, _ba.size()); } } } void OfxHttpsRequest::slotOfxFinished(TDEIO::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().local8Bit()); } else if ( m_job->isErrorPage() ) { TQString details; TQFile f( m_dst.path() ); if ( f.open( IO_ReadOnly ) ) { TQTextStream stream( &f ); TQString 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().local8Bit()); } tqApp->exit_loop(); } OfxHttpRequest::OfxHttpRequest(const TQString& type, const KURL &url, const TQByteArray &postData, const TQMap& metaData, const KURL& dst, bool showProgressInfo) { TQFile f(dst.path()); m_error = TQHttp::NoError; TQString errorMsg; if(f.open(IO_WriteOnly)) { m_job = new TQHttp(url.host()); TQHttpRequestHeader header(type, url.encodedPathAndQuery()); header.setValue("Host", url.host()); TQMap::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, TQ_SIGNAL(requestFinished(int, bool)), this, TQ_SLOT(slotOfxFinished(int, bool))); tqApp->enter_loop(); if(m_error != TQHttp::NoError) errorMsg = m_job->errorString(); delete m_job; } else { m_error = TQHttp::Aborted; errorMsg = i18n("Cannot open file %1 for writing").arg(dst.path()); } if(m_error != TQHttp::NoError) { KMessageBox::error(0, errorMsg, i18n("OFX setup error")); unlink(dst.path().local8Bit()); } } void OfxHttpRequest::slotOfxFinished(int, bool rc) { if(rc) { m_error = m_job->error(); } tqApp->exit_loop(); } #include "ofxpartner.moc"