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