LibOFX
ofxconnect.cpp
Go to the documentation of this file.
1/***************************************************************************
2 ofx_connect.cpp
3 -------------------
4 copyright : (C) 2005 by Ace Jones
5 email : acejones@users.sourceforge.net
6***************************************************************************/
23/***************************************************************************
24 * *
25 * This program is free software; you can redistribute it and/or modify *
26 * it under the terms of the GNU General Public License as published by *
27 * the Free Software Foundation; either version 2 of the License, or *
28 * (at your option) any later version. *
29 * *
30 ***************************************************************************/
31#include <iostream>
32#include <fstream>
33#include <string>
34#include "libofx.h"
35#include <config.h> /* Include config constants, e.g., VERSION TF */
36#include <stdio.h>
37#include <stdlib.h>
38#include <unistd.h>
39#include <cstring>
40#include <cstdlib>
41#include <string.h>
42#ifdef HAVE_LIBCURL
43#include <curl/curl.h>
44#endif
45
46#include "cmdline.h" /* Gengetopt generated parser */
47
48#include "nodeparser.h"
49#include "ofxpartner.h"
50
51using namespace std;
52
53#ifdef HAVE_LIBCURL
54bool post(const char* request, const char* url, const char* filename)
55{
56 CURL *curl = curl_easy_init();
57 if (! curl)
58 return false;
59
60 remove("tmpout");
61 FILE* file = fopen(filename, "wb");
62 if (! file )
63 {
64 curl_easy_cleanup(curl);
65 return false;
66 }
67
68 curl_easy_setopt(curl, CURLOPT_URL, url);
69 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request);
70
71 struct curl_slist *headerlist = NULL;
72 headerlist = curl_slist_append(headerlist, "Content-type: application/x-ofx");
73 headerlist = curl_slist_append(headerlist, "Accept: */*, application/x-ofx");
74
75 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
76 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite);
77 curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)file);
78
79 CURLcode res = curl_easy_perform(curl);
80
81 curl_easy_cleanup(curl);
82 curl_slist_free_all (headerlist);
83
84 fclose(file);
85
86 return true;
87}
88#else
89bool post(const char*, const char*, const char*)
90{
91 cerr << "ERROR: libox must be configured with libcurl to post this request directly" << endl;
92 return false;
93}
94#endif
95
96ostream& operator<<(ostream& os, const vector<string>& strvect)
97{
98 for ( vector<string>::const_iterator it = strvect.begin(); it != strvect.end(); ++it)
99 {
100 os << (*it) << endl;
101 }
102 return os;
103}
104
105int main (int argc, char *argv[])
106{
107 gengetopt_args_info args_info;
108
109 if (cmdline_parser (argc, argv, &args_info) != 0)
110 exit(1) ;
111
112 if ( argc == 1 )
113 {
114 cmdline_parser_print_help();
115 exit(1);
116 }
117
118 if ( args_info.statement_req_given || args_info.accountinfo_req_given )
119 {
120 if ( (args_info.inputs_num > 0) )
121 {
122 cout << "file " << args_info.inputs[0] << endl;
123 }
124 else
125 {
126 cerr << "ERROR: You must specify an output file" << endl;
127 exit(1);
128 }
129 }
130 else if ( args_info.bank_fipid_given || args_info.bank_services_given )
131 {
132 if ( (args_info.inputs_num > 0) )
133 {
134 cout << "bank " << args_info.inputs[0] << endl;
135 }
136 else
137 {
138 cerr << "ERROR: You must specify an bank" << endl;
139 exit(1);
140 }
141 }
142
143 OfxFiLogin fi;
144 memset(&fi, 0, sizeof(OfxFiLogin));
145 bool ok = true;
146 string url;
147
148 if ( args_info.statement_req_given || args_info.accountinfo_req_given || args_info.payment_req_given || args_info.paymentinquiry_req_given )
149 {
150 // Get the FI Login information
151 //
152
153 if ( args_info.fipid_given )
154 {
155 cerr << "fipid " << args_info.fipid_arg << endl;
156 cerr << "contacting partner server..." << endl;
157 OfxFiServiceInfo svcinfo = OfxPartner::ServiceInfo(args_info.fipid_arg);
158 cout << "fid " << svcinfo.fid << endl;
159 strncpy(fi.fid, svcinfo.fid, OFX_FID_LENGTH - 1);
160 cout << "org " << svcinfo.org << endl;
161 strncpy(fi.org, svcinfo.org, OFX_ORG_LENGTH - 1);
162 cout << "url " << svcinfo.url << endl;
163 url = svcinfo.url;
164 }
165 if ( args_info.fid_given )
166 {
167 cerr << "fid " << args_info.fid_arg << endl;
168 strncpy(fi.fid, args_info.fid_arg, OFX_FID_LENGTH - 1);
169 }
170 else if ( ! args_info.fipid_given )
171 {
172 cerr << "ERROR: --fid is required" << endl;
173 ok = false;
174 }
175
176 if ( args_info.org_given )
177 {
178 cerr << "org " << args_info.org_arg << endl;
179 strncpy(fi.org, args_info.org_arg, OFX_ORG_LENGTH - 1);
180 }
181 else if ( ! args_info.fipid_given )
182 {
183 cerr << "ERROR: --org is required" << endl;
184 ok = false;
185 }
186
187 if ( args_info.user_given )
188 {
189 cerr << "user " << args_info.user_arg << endl;
190 strncpy(fi.userid, args_info.user_arg, OFX_USERID_LENGTH - 1);
191 }
192 else
193 {
194 cerr << "ERROR: --user is required" << endl;
195 ok = false;
196 }
197
198 if ( args_info.pass_given )
199 {
200 cerr << "pass " << args_info.pass_arg << endl;
201 strncpy(fi.userpass, args_info.pass_arg, OFX_USERPASS_LENGTH - 1);
202 }
203 else
204 {
205 cerr << "ERROR: --pass is required" << endl;
206 ok = false;
207 }
208
209 if ( args_info.url_given )
210 url = args_info.url_arg;
211 }
212
213 if ( args_info.statement_req_given )
214 {
215 cerr << "Statement request" << endl;
216
217 OfxAccountData account;
218 memset(&account, 0, sizeof(OfxAccountData));
219
220 if ( args_info.bank_given )
221 {
222 cerr << "bank " << args_info.bank_arg << endl;
223 strncpy(account.bank_id, args_info.bank_arg, OFX_BANKID_LENGTH - 1);
224 }
225 else
226 {
227 if ( args_info.type_given && args_info.type_arg == 1 )
228 {
229 cerr << "ERROR: --bank is required for a bank request" << endl;
230 ok = false;
231 }
232 }
233
234 if ( args_info.broker_given )
235 {
236 cerr << "broker " << args_info.broker_arg << endl;
237 strncpy(account.broker_id, args_info.broker_arg, OFX_BROKERID_LENGTH - 1);
238 }
239 else
240 {
241 if ( args_info.type_given && args_info.type_arg == 2 )
242 {
243 cerr << "ERROR: --broker is required for an investment statement request" << endl;
244 ok = false;
245 }
246 }
247
248 if ( args_info.acct_given )
249 {
250 cerr << "acct " << args_info.acct_arg << endl;
251 strncpy(account.account_number, args_info.acct_arg, OFX_ACCTID_LENGTH - 1);
252 }
253 else
254 {
255 cerr << "ERROR: --acct is required for a statement request" << endl;
256 ok = false;
257 }
258
259 if ( args_info.type_given )
260 {
261 cerr << "type " << args_info.type_arg << endl;
262 switch (args_info.type_arg)
263 {
264 case 1:
265 account.account_type = account.OFX_CHECKING;
266 break;
267 case 2:
268 account.account_type = account.OFX_INVESTMENT;
269 break;
270 case 3:
271 account.account_type = account.OFX_CREDITCARD ;
272 break;
273 default:
274 cerr << "ERROR: --type is not valid. Must be between 1 and 3" << endl;
275 ok = false;
276 }
277 }
278 else
279 {
280 cerr << "ERROR: --type is required for a statement request" << endl;
281 ok = false;
282 }
283
284 if ( args_info.past_given )
285 {
286 cerr << "past " << args_info.past_arg << endl;
287 }
288 else
289 {
290 cerr << "ERROR: --past is required for a statement request" << endl;
291 ok = false;
292 }
293
294 if ( ok )
295 {
296 char* request = libofx_request_statement( &fi, &account, time(NULL) - args_info.past_arg * 86400L );
297
298 if ( url.length() && args_info.inputs_num > 0 )
299 post(request, url.c_str(), args_info.inputs[0]);
300 else
301 cout << request;
302
303 free(request);
304 }
305 }
306
307 if ( args_info.paymentinquiry_req_given )
308 {
309 char tridstr[33];
310 memset(tridstr, 0, 33);
311
312 bool ok = true;
313
314 if ( args_info.trid_given )
315 {
316 cerr << "trid " << args_info.trid_arg << endl;
317 snprintf(tridstr, 32, "%i", args_info.trid_arg);
318 }
319 else
320 {
321 cerr << "ERROR: --trid is required for a payment inquiry request" << endl;
322 ok = false;
323 }
324
325 if ( ok )
326 {
327 char* request = libofx_request_payment_status( &fi, tridstr );
328
329 filebuf fb;
330 fb.open ("query", ios::out);
331 ostream os(&fb);
332 os << request;
333 fb.close();
334
335 if ( url.length() && args_info.inputs_num > 0 )
336 post(request, url.c_str(), args_info.inputs[0]);
337 else
338 cout << request;
339
340 free(request);
341 }
342 }
343
344 if ( args_info.payment_req_given )
345 {
346 OfxAccountData account;
347 memset(&account, 0, sizeof(OfxAccountData));
348 OfxPayee payee;
349 memset(&payee, 0, sizeof(OfxPayee));
350 OfxPayment payment;
351 memset(&payment, 0, sizeof(OfxPayment));
352
353 strcpy(payee.name, "MARTIN PREUSS");
354 strcpy(payee.address1, "1 LAUREL ST");
355 strcpy(payee.city, "SAN CARLOS");
356 strcpy(payee.state, "CA");
357 strcpy(payee.postalcode, "94070");
358 strcpy(payee.phone, "866-555-1212");
359
360 strcpy(payment.amount, "200.00");
361 strcpy(payment.account, "1234");
362 strcpy(payment.datedue, "20060301");
363 strcpy(payment.memo, "This is a test");
364
365 bool ok = true;
366
367 if ( args_info.bank_given )
368 {
369 cerr << "bank " << args_info.bank_arg << endl;
370 strncpy(account.bank_id, args_info.bank_arg, OFX_BANKID_LENGTH - 1);
371 }
372 else
373 {
374 if ( args_info.type_given && args_info.type_arg == 1 )
375 {
376 cerr << "ERROR: --bank is required for a bank request" << endl;
377 ok = false;
378 }
379 }
380
381 if ( args_info.broker_given )
382 {
383 cerr << "broker " << args_info.broker_arg << endl;
384 strncpy(account.broker_id, args_info.broker_arg, OFX_BROKERID_LENGTH - 1);
385 }
386 else
387 {
388 if ( args_info.type_given && args_info.type_arg == 2 )
389 {
390 cerr << "ERROR: --broker is required for an investment statement request" << endl;
391 ok = false;
392 }
393 }
394
395 if ( args_info.acct_given )
396 {
397 cerr << "acct " << args_info.acct_arg << endl;
398 strncpy(account.account_number, args_info.acct_arg, OFX_ACCTID_LENGTH - 1);
399 }
400 else
401 {
402 cerr << "ERROR: --acct is required for a statement request" << endl;
403 ok = false;
404 }
405
406 if ( args_info.type_given )
407 {
408 cerr << "type " << args_info.type_arg << endl;
409 switch (args_info.type_arg)
410 {
411 case 1:
412 account.account_type = account.OFX_CHECKING;
413 break;
414 case 2:
415 account.account_type = account.OFX_INVESTMENT;
416 break;
417 case 3:
418 account.account_type = account.OFX_CREDITCARD ;
419 break;
420 default:
421 cerr << "ERROR: --type is not valid. Must be between 1 and 3" << endl;
422 ok = false;
423 }
424 }
425 else
426 {
427 cerr << "ERROR: --type is required for a statement request" << endl;
428 ok = false;
429 }
430
431 if ( ok )
432 {
433 char* request = libofx_request_payment( &fi, &account, &payee, &payment );
434
435 filebuf fb;
436 fb.open ("query", ios::out);
437 ostream os(&fb);
438 os << request;
439 fb.close();
440
441 if ( url.length() && args_info.inputs_num > 0 )
442 post(request, url.c_str(), args_info.inputs[0]);
443 else
444 cout << request;
445
446 free(request);
447 }
448
449 }
450
451 if ( args_info.accountinfo_req_given )
452 {
453 if ( ok )
454 {
455 char* request = libofx_request_accountinfo( &fi );
456
457 if ( url.length() && args_info.inputs_num > 0 )
458 post(request, url.c_str(), args_info.inputs[0]);
459 else
460 cout << request;
461
462 free(request);
463 }
464 }
465
466 if ( args_info.bank_list_given )
467 {
468 cout << OfxPartner::BankNames();
469 }
470
471 if ( args_info.bank_fipid_given && args_info.inputs_num > 0 )
472 {
473 cout << OfxPartner::FipidForBank(args_info.inputs[0]);
474 }
475
476 if ( args_info.bank_services_given && args_info.inputs_num > 0 )
477 {
478 OfxFiServiceInfo svcinfo = OfxPartner::ServiceInfo(args_info.inputs[0]);
479 cout << "Account List? " << (svcinfo.accountlist ? "Yes" : "No") << endl;
480 cout << "Statements? " << (svcinfo.statements ? "Yes" : "No") << endl;
481 cout << "Billpay? " << (svcinfo.billpay ? "Yes" : "No") << endl;
482 cout << "Investments? " << (svcinfo.investments ? "Yes" : "No") << endl;
483 }
484
485 if ( args_info.allsupport_given )
486 {
487 vector<string> banks = OfxPartner::BankNames();
488 vector<string>::const_iterator it_bank = banks.begin();
489 while ( it_bank != banks.end() )
490 {
491 vector<string> fipids = OfxPartner::FipidForBank(*it_bank);
492 vector<string>::const_iterator it_fipid = fipids.begin();
493 while ( it_fipid != fipids.end() )
494 {
495 if ( OfxPartner::ServiceInfo(*it_fipid).accountlist )
496 cout << *it_bank << endl;
497 ++it_fipid;
498 }
499 ++it_bank;
500 }
501 }
502
503 return 0;
504}
505
506
507// vim:cin:si:ai:et:ts=2:sw=2:
508
Main header file containing the LibOfx API.
char * libofx_request_accountinfo(const struct OfxFiLogin *login)
Creates an OFX account info (list) request in string form.
char * libofx_request_statement(const struct OfxFiLogin *fi, const struct OfxAccountData *account, time_t date_from)
Creates an OFX statement request in string form.
Declaration of nodeparser object, which facilitates searching for nodes in an XML file using a notati...
ostream & operator<<(ostream &os, SGMLApplication::CharString s)
Convert OpenSP CharString to a C++ stream.
The header file for the command line option parser generated by GNU Gengetopt version 2....
Methods for connecting to the OFX partner server to retrieve OFX server information.
An abstraction of an account.
Definition libofx.h:277
char bank_id[OFX_BANKID_LENGTH]
Definition libofx.h:324
char account_number[OFX_ACCTID_LENGTH]
Definition libofx.h:320
Information sufficient to log into an financial institution.
Definition libofx.h:1373
Information returned by the OFX Partner Server about a financial institution.
Definition libofx.h:1353
Where the command line options are stored.
unsigned int fid_given
Whether fid was given.
unsigned int paymentinquiry_req_given
Whether paymentinquiry-req was given.
unsigned int type_given
Whether type was given.
int type_arg
Account Type 1=checking 2=invest 3=ccard.
char * user_arg
User name.
char * pass_arg
Password.
unsigned inputs_num
unnamed options number
char * bank_arg
IBAN bank identifier.
unsigned int statement_req_given
Whether statement-req was given.
char ** inputs
unnamed options (options without names)
unsigned int user_given
Whether user was given.
char * fid_arg
FI identifier.
unsigned int allsupport_given
Whether allsupport was given.
unsigned int acct_given
Whether acct was given.
unsigned int bank_fipid_given
Whether bank-fipid was given.
unsigned int accountinfo_req_given
Whether accountinfo-req was given.
int trid_arg
Transaction id.
char * acct_arg
Account ID.
char * fipid_arg
FI partner identifier (looks up fid, org & url from partner server).
long past_arg
How far back to look from today (in days).
unsigned int broker_given
Whether broker was given.
unsigned int bank_list_given
Whether bank-list was given.
char * org_arg
FI org tag.
char * broker_arg
Broker identifier.
unsigned int bank_services_given
Whether bank-services was given.
unsigned int bank_given
Whether bank was given.
unsigned int org_given
Whether org was given.
unsigned int url_given
Whether url was given.
unsigned int fipid_given
Whether fipid was given.
char * url_arg
Url to POST the data to (otherwise goes to stdout).
unsigned int past_given
Whether past was given.
unsigned int pass_given
Whether pass was given.
unsigned int payment_req_given
Whether payment-req was given.
unsigned int trid_given
Whether trid was given.