1 /***************************************************************************
2                              ofx_partner.cpp
3                              -------------------
4     copyright            : (C) 2005 by Ace Jones
5     email                : acejones@users.sourceforge.net
6 ***************************************************************************/
7 /**@file
8  * \brief Methods for connecting to the OFX partner server to retrieve
9  * OFX server information
10 */
11 /***************************************************************************
12  *                                                                         *
13  *   This program is free software; you can redistribute it and/or modify  *
14  *   it under the terms of the GNU General Public License as published by  *
15  *   the Free Software Foundation; either version 2 of the License, or     *
16  *   (at your option) any later version.                                   *
17  *                                                                         *
18  ***************************************************************************/
19 
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 
24 #include <libofx.h>
25 
26 //#ifdef HAVE_LIBCURL
27 #include <curl/curl.h>
28 //#endif
29 
30 #include "ofxpartner.h"
31 #include "nodeparser.h"
32 
33 #include <sys/stat.h>
34 
35 #include <iostream>
36 #include <string>
37 #include <vector>
38 #include <algorithm>
39 #include <string.h>
40 #include <unistd.h>
41 
42 using std::string;
43 using std::vector;
44 using std::cout;
45 using std::endl;
46 
47 namespace OfxPartner
48 {
49 bool post(const string& request, const string& url, const string& filename);
50 
51 const string kBankFilename = "ofx-bank-index.xml";
52 const string kCcFilename = "ofx-cc-index.xml";
53 const string kInvFilename = "ofx-inv-index.xml";
54 
ValidateIndexCache(void)55 void ValidateIndexCache(void)
56 {
57   // TODO Check whether these files exist and are recent enough before getting them again
58 
59   struct stat filestats;
60   if ( stat( kBankFilename.c_str(), &filestats ) || difftime(time(0), filestats.st_mtime) > 7.0 * 24.0 * 60.0 * 60.0 )
61     post("T=1&S=*&R=1&O=0&TEST=0", "http://moneycentral.msn.com/money/2005/mnynet/service/ols/filist.aspx?SKU=3&VER=6", kBankFilename);
62   if ( stat( kCcFilename.c_str(), &filestats ) || difftime(time(0), filestats.st_mtime) > 7.0 * 24.0 * 60.0 * 60.0 )
63     post("T=2&S=*&R=1&O=0&TEST=0", "http://moneycentral.msn.com/money/2005/mnynet/service/ols/filist.aspx?SKU=3&VER=6", kCcFilename);
64   if ( stat( kInvFilename.c_str(), &filestats ) || difftime(time(0), filestats.st_mtime) > 7.0 * 24.0 * 60.0 * 60.0 )
65     post("T=3&S=*&R=1&O=0&TEST=0", "http://moneycentral.msn.com/money/2005/mnynet/service/ols/filist.aspx?SKU=3&VER=6", kInvFilename);
66 }
67 
BankNames(void)68 vector<string> BankNames(void)
69 {
70   vector<string> result;
71 
72   // Make sure the index files are up to date
73   ValidateIndexCache();
74 
75   xmlpp::DomParser parser;
76   parser.set_substitute_entities();
77   parser.parse_file(kBankFilename);
78   if ( parser )
79   {
80     vector<string> names = NodeParser(parser).Path("fi/prov/name").Text();
81     result.insert(result.end(), names.begin(), names.end());
82   }
83   parser.parse_file(kCcFilename);
84   if ( parser )
85   {
86     vector<string> names = NodeParser(parser).Path("fi/prov/name").Text();
87     result.insert(result.end(), names.begin(), names.end());
88   }
89   parser.parse_file(kInvFilename);
90   if ( parser )
91   {
92     vector<string> names = NodeParser(parser).Path("fi/prov/name").Text();
93     result.insert(result.end(), names.begin(), names.end());
94   }
95 
96   // Add Innovision
97   result.push_back("Innovision");
98 
99   // sort the list and remove duplicates, to return one unified list of all supported banks
100   sort(result.begin(), result.end());
101   result.erase(unique(result.begin(), result.end()), result.end());
102   return result;
103 }
104 
FipidForBank(const string & bank)105 vector<string> FipidForBank(const string& bank)
106 {
107   vector<string> result;
108 
109   xmlpp::DomParser parser;
110   parser.set_substitute_entities();
111   parser.parse_file(kBankFilename);
112   if ( parser )
113   {
114     vector<string> fipids = NodeParser(parser).Path("fi/prov").Select("name", bank).Path("guid").Text();
115     if ( ! fipids.back().empty() )
116       result.insert(result.end(), fipids.begin(), fipids.end());
117   }
118   parser.parse_file(kCcFilename);
119   if ( parser )
120   {
121     vector<string> fipids = NodeParser(parser).Path("fi/prov").Select("name", bank).Path("guid").Text();
122     if ( ! fipids.back().empty() )
123       result.insert(result.end(), fipids.begin(), fipids.end());
124   }
125   parser.parse_file(kInvFilename);
126   if ( parser )
127   {
128     vector<string> fipids = NodeParser(parser).Path("fi/prov").Select("name", bank).Path("guid").Text();
129     if ( ! fipids.back().empty() )
130       result.insert(result.end(), fipids.begin(), fipids.end());
131   }
132 
133   // the fipid for Innovision is 1.
134   if ( bank == "Innovision" )
135     result.push_back("1");
136 
137   sort(result.begin(), result.end());
138   result.erase(unique(result.begin(), result.end()), result.end());
139 
140   return result;
141 }
142 
ServiceInfo(const std::string & fipid)143 OfxFiServiceInfo ServiceInfo(const std::string& fipid)
144 {
145   OfxFiServiceInfo result;
146   memset(&result, 0, sizeof(OfxFiServiceInfo));
147 
148   // Hard-coded values for Innovision test server
149   if ( fipid == "1" )
150   {
151     strncpy(result.fid, "00000", OFX_FID_LENGTH - 1);
152     strncpy(result.org, "ReferenceFI", OFX_ORG_LENGTH - 1);
153     strncpy(result.url, "http://ofx.innovision.com", OFX_URL_LENGTH - 1);
154     result.accountlist = 1;
155     result.statements = 1;
156     result.billpay = 1;
157     result.investments = 1;
158 
159     return result;
160   }
161 
162   string url = "http://moneycentral.msn.com/money/2005/mnynet/service/olsvcupd/OnlSvcBrandInfo.aspx?MSNGUID=&GUID=%1&SKU=3&VER=6";
163   url.replace(url.find("%1"), 2, fipid);
164 
165   // TODO: Check whether this file exists and is recent enough before getting it again
166   string guidfile = "fipid-%1.xml";
167   guidfile.replace(guidfile.find("%1"), 2, fipid);
168 
169   struct stat filestats;
170   if ( stat( guidfile.c_str(), &filestats ) || difftime(time(0), filestats.st_mtime) > 7.0 * 24.0 * 60.0 * 60.0 )
171     post("", url.c_str(), guidfile.c_str());
172 
173   // Print the FI details
174   xmlpp::DomParser parser;
175   parser.set_substitute_entities();
176   parser.parse_file(guidfile);
177   if ( parser )
178   {
179     NodeParser nodes(parser);
180 
181     strncpy(result.fid, nodes.Path("ProviderSettings/FID").Text().back().c_str(), OFX_FID_LENGTH - 1);
182     strncpy(result.org, nodes.Path("ProviderSettings/Org").Text().back().c_str(), OFX_ORG_LENGTH - 1);
183     strncpy(result.url, nodes.Path("ProviderSettings/ProviderURL").Text().back().c_str(), OFX_URL_LENGTH - 1);
184     result.accountlist = (nodes.Path("ProviderSettings/AcctListAvail").Text().back() == "1");
185     result.statements = (nodes.Path("BankingCapabilities/Bank").Text().back() == "1");
186     result.billpay = (nodes.Path("BillPayCapabilities/Pay").Text().back() == "1");
187     result.investments = (nodes.Path("InvestmentCapabilities/BrkStmt").Text().back() == "1");
188   }
189   return result;
190 }
191 
post(const string & request,const string & url,const string & filename)192 bool post(const string& request, const string& url, const string& filename)
193 {
194 #if 1 //#ifdef HAVE_LIBCURL
195   CURL *curl = curl_easy_init();
196   if (! curl)
197     return false;
198 
199   remove(filename.c_str());
200   FILE* file = fopen(filename.c_str(), "wb");
201   if (! file )
202   {
203     curl_easy_cleanup(curl);
204     return false;
205   }
206 
207   curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
208   if ( request.length() )
209     curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request.c_str());
210   curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite);
211   curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)file);
212 
213   /*CURLcode res =*/
214   curl_easy_perform(curl);
215 
216   curl_easy_cleanup(curl);
217 
218   fclose(file);
219 
220   return true;
221 #else
222   request;
223   url;
224   filename;
225   cerr << "ERROR: libox must be configured with libcurl to post this request" << endl;
226   return false;
227 #endif
228 }
229 
230 } // namespace OfxPartner
231 
232 
233 // vim:cin:si:ai:et:ts=2:sw=2:
234