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