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 using namespace std;
52 
53 #ifdef HAVE_LIBCURL
54 bool 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
89 bool 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 
96 ostream& 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 
105 int 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  {
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  }
128  }
129  else if ( args_info.bank_fipid_given || args_info.bank_services_given )
130  {
131  if ( (args_info.inputs_num > 0) )
132  {
133  cout << "bank " << args_info.inputs[0] << endl;
134  }
135  else
136  {
137  cerr << "ERROR: You must specify an bank" << endl;
138  }
139  }
140 
141  OfxFiLogin fi;
142  memset(&fi, 0, sizeof(OfxFiLogin));
143  bool ok = true;
144  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  cerr << "fipid " << args_info.fipid_arg << endl;
154  cerr << "contacting partner server..." << endl;
155  OfxFiServiceInfo svcinfo = OfxPartner::ServiceInfo(args_info.fipid_arg);
156  cout << "fid " << svcinfo.fid << endl;
157  strncpy(fi.fid, svcinfo.fid, OFX_FID_LENGTH - 1);
158  cout << "org " << svcinfo.org << endl;
159  strncpy(fi.org, svcinfo.org, OFX_ORG_LENGTH - 1);
160  cout << "url " << svcinfo.url << endl;
161  url = svcinfo.url;
162  }
163  if ( args_info.fid_given )
164  {
165  cerr << "fid " << args_info.fid_arg << endl;
166  strncpy(fi.fid, args_info.fid_arg, OFX_FID_LENGTH - 1);
167  }
168  else if ( ! args_info.fipid_given )
169  {
170  cerr << "ERROR: --fid is required" << endl;
171  ok = false;
172  }
173 
174  if ( args_info.org_given )
175  {
176  cerr << "org " << args_info.org_arg << endl;
177  strncpy(fi.org, args_info.org_arg, OFX_ORG_LENGTH - 1);
178  }
179  else if ( ! args_info.fipid_given )
180  {
181  cerr << "ERROR: --org is required" << endl;
182  ok = false;
183  }
184 
185  if ( args_info.user_given )
186  {
187  cerr << "user " << args_info.user_arg << endl;
188  strncpy(fi.userid, args_info.user_arg, OFX_USERID_LENGTH - 1);
189  }
190  else
191  {
192  cerr << "ERROR: --user is required" << endl;
193  ok = false;
194  }
195 
196  if ( args_info.pass_given )
197  {
198  cerr << "pass " << args_info.pass_arg << endl;
199  strncpy(fi.userpass, args_info.pass_arg, OFX_USERPASS_LENGTH - 1);
200  }
201  else
202  {
203  cerr << "ERROR: --pass is required" << 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  cerr << "Statement request" << endl;
214 
215  OfxAccountData account;
216  memset(&account, 0, sizeof(OfxAccountData));
217 
218  if ( args_info.bank_given )
219  {
220  cerr << "bank " << args_info.bank_arg << 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  cerr << "ERROR: --bank is required for a bank request" << endl;
228  ok = false;
229  }
230  }
231 
232  if ( args_info.broker_given )
233  {
234  cerr << "broker " << args_info.broker_arg << 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  cerr << "ERROR: --broker is required for an investment statement request" << endl;
242  ok = false;
243  }
244  }
245 
246  if ( args_info.acct_given )
247  {
248  cerr << "acct " << args_info.acct_arg << endl;
249  strncpy(account.account_number, args_info.acct_arg, OFX_ACCTID_LENGTH - 1);
250  }
251  else
252  {
253  cerr << "ERROR: --acct is required for a statement request" << endl;
254  ok = false;
255  }
256 
257  if ( args_info.type_given )
258  {
259  cerr << "type " << args_info.type_arg << 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  cerr << "ERROR: --type is not valid. Must be between 1 and 3" << endl;
273  ok = false;
274  }
275  }
276  else
277  {
278  cerr << "ERROR: --type is required for a statement request" << endl;
279  ok = false;
280  }
281 
282  if ( args_info.past_given )
283  {
284  cerr << "past " << args_info.past_arg << endl;
285  }
286  else
287  {
288  cerr << "ERROR: --past is required for a statement request" << 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() )
297  post(request, url.c_str(), args_info.inputs[0]);
298  else
299  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 ok = true;
311 
312  if ( args_info.trid_given )
313  {
314  cerr << "trid " << args_info.trid_arg << endl;
315  snprintf(tridstr, 32, "%i", args_info.trid_arg);
316  }
317  else
318  {
319  cerr << "ERROR: --trid is required for a payment inquiry request" << endl;
320  ok = false;
321  }
322 
323  if ( ok )
324  {
325  char* request = libofx_request_payment_status( &fi, tridstr );
326 
327  filebuf fb;
328  fb.open ("query", ios::out);
329  ostream os(&fb);
330  os << request;
331  fb.close();
332 
333  if ( url.length() )
334  post(request, url.c_str(), args_info.inputs[0]);
335  else
336  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 ok = true;
364 
365  if ( args_info.bank_given )
366  {
367  cerr << "bank " << args_info.bank_arg << 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  cerr << "ERROR: --bank is required for a bank request" << endl;
375  ok = false;
376  }
377  }
378 
379  if ( args_info.broker_given )
380  {
381  cerr << "broker " << args_info.broker_arg << 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  cerr << "ERROR: --broker is required for an investment statement request" << endl;
389  ok = false;
390  }
391  }
392 
393  if ( args_info.acct_given )
394  {
395  cerr << "acct " << args_info.acct_arg << endl;
396  strncpy(account.account_number, args_info.acct_arg, OFX_ACCTID_LENGTH - 1);
397  }
398  else
399  {
400  cerr << "ERROR: --acct is required for a statement request" << endl;
401  ok = false;
402  }
403 
404  if ( args_info.type_given )
405  {
406  cerr << "type " << args_info.type_arg << 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  cerr << "ERROR: --type is not valid. Must be between 1 and 3" << endl;
420  ok = false;
421  }
422  }
423  else
424  {
425  cerr << "ERROR: --type is required for a statement request" << endl;
426  ok = false;
427  }
428 
429  if ( ok )
430  {
431  char* request = libofx_request_payment( &fi, &account, &payee, &payment );
432 
433  filebuf fb;
434  fb.open ("query", ios::out);
435  ostream os(&fb);
436  os << request;
437  fb.close();
438 
439  if ( url.length() )
440  post(request, url.c_str(), args_info.inputs[0]);
441  else
442  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() )
456  post(request, url.c_str(), args_info.inputs[0]);
457  else
458  cout << request;
459 
460  free(request);
461  }
462  }
463 
464  if ( args_info.bank_list_given )
465  {
466  cout << OfxPartner::BankNames();
467  }
468 
469  if ( args_info.bank_fipid_given )
470  {
471  cout << OfxPartner::FipidForBank(args_info.inputs[0]);
472  }
473 
474  if ( args_info.bank_services_given )
475  {
476  OfxFiServiceInfo svcinfo = OfxPartner::ServiceInfo(args_info.inputs[0]);
477  cout << "Account List? " << (svcinfo.accountlist ? "Yes" : "No") << endl;
478  cout << "Statements? " << (svcinfo.statements ? "Yes" : "No") << endl;
479  cout << "Billpay? " << (svcinfo.billpay ? "Yes" : "No") << endl;
480  cout << "Investments? " << (svcinfo.investments ? "Yes" : "No") << endl;
481  }
482 
483  if ( args_info.allsupport_given )
484  {
485  vector<string> banks = OfxPartner::BankNames();
486  vector<string>::const_iterator it_bank = banks.begin();
487  while ( it_bank != banks.end() )
488  {
489  vector<string> fipids = OfxPartner::FipidForBank(*it_bank);
490  vector<string>::const_iterator it_fipid = fipids.begin();
491  while ( it_fipid != fipids.end() )
492  {
493  if ( OfxPartner::ServiceInfo(*it_fipid).accountlist )
494  cout << *it_bank << 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 
An abstraction of an account.
Definition: inc/libofx.h:270
Where the command line options are stored.
Definition: cmdline.h:42
char * libofx_request_statement(const struct OfxFiLogin *fi, const struct OfxAccountData *account, time_t date_from)
Creates an OFX statement request in string form.
Methods for connecting to the OFX partner server to retrieve OFX server information.
char * libofx_request_accountinfo(const struct OfxFiLogin *login)
Creates an OFX account info (list) request in string form.
char bank_id[OFX_BANKID_LENGTH]
Definition: inc/libofx.h:317
int cmdline_parser(int argc, char **argv, struct gengetopt_args_info *args_info)
Definition: cmdline.c:304
void cmdline_parser_print_help(void)
Definition: cmdline.c:143
unsigned inputs_num
unamed options number
Definition: cmdline.h:75
Information returned by the OFX Partner Server about a financial institution.
Definition: inc/libofx.h:785
int main(int argc, char *argv[])
Definition: ofxdump.cpp:434
The header file for the command line option parser generated by GNU Gengetopt version 2...
char account_number[OFX_ACCTID_LENGTH]
Definition: inc/libofx.h:313
Declaration of nodeparser object, which facilitiates searching for nodes in an XML file using a notat...
char ** inputs
unamed options (options without names)
Definition: cmdline.h:74
Information sufficient to log into an financial institution.
Definition: inc/libofx.h:805