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 {
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...
int cmdline_parser(int argc, char **argv, struct gengetopt_args_info *args_info)
void cmdline_parser_print_help(void)
The header file for the command line option parser generated by GNU Gengetopt version 2....
int main(int argc, char *argv[])
Definition: ofxdump.cpp:1143
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
@ OFX_CREDITCARD
Definition: libofx.h:320
@ OFX_INVESTMENT
Definition: libofx.h:321
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.