1 /***************************************************************************
2     begin       : Mon Mar 01 2004
3     copyright   : (C) 2020 by Martin Preuss
4     email       : martin@libchipcard.de
5 
6  ***************************************************************************
7  *          Please see toplevel file COPYING for license details           *
8  ***************************************************************************/
9 
10 #ifdef HAVE_CONFIG_H
11 # include <config.h>
12 #endif
13 
14 #define AO_PROVIDER_HEAVY_DEBUG
15 
16 #include "provider_p.h"
17 
18 #include "aqofxconnect/account.h"
19 #include "aqofxconnect/user.h"
20 #include "aqofxconnect/dialogs/dlg_edituser_l.h"
21 #include "aqofxconnect/dialogs/dlg_newuser_l.h"
22 #include "aqofxconnect/control/control.h"
23 #include "aqofxconnect/v1/r_statements.h"
24 #include "aqofxconnect/v1/r_accounts.h"
25 #include "aqofxconnect/v2/r_statements.h"
26 #include "aqofxconnect/v2/r_accounts.h"
27 
28 #include <aqbanking/backendsupport/account.h>
29 #include <aqbanking/types/transaction.h>
30 #include <aqbanking/types/value.h>
31 #include <aqbanking/backendsupport/httpsession.h>
32 
33 #include <gwenhywfar/debug.h>
34 #include <gwenhywfar/inherit.h>
35 #include <gwenhywfar/misc.h>
36 #include <gwenhywfar/dbio.h>
37 #include <gwenhywfar/text.h>
38 #include <gwenhywfar/process.h>
39 #include <gwenhywfar/directory.h>
40 #include <gwenhywfar/gwentime.h>
41 #include <gwenhywfar/gui.h>
42 #include <gwenhywfar/i18n.h>
43 #include <gwenhywfar/url.h>
44 
45 #include <errno.h>
46 
47 
48 #define I18N(msg) GWEN_I18N_Translate(PACKAGE, msg)
49 #define I18S(msg) msg
50 
51 
52 
53 
54 GWEN_INHERIT(AB_PROVIDER, AO_PROVIDER)
55 
56 
57 
58 
59 static AO_APPINFO _appInfos[]= {
60   /* got this list from https://microsoftmoneyoffline.wordpress.com/appid-appver/ */
61   { I18S("Intuit Quicken Windows 2020"),    "QWIN",       "2900"},
62   { I18S("Intuit Quicken Windows 2017"),    "QWIN",       "2600"},
63   { I18S("Intuit Quicken Windows 2016"),    "QWIN",       "2500"},
64   { I18S("Intuit Quicken Windows 2015"),    "QWIN",       "2400"},
65   { I18S("Intuit Quicken Windows 2014"),    "QWIN",       "2300"},
66   { I18S("Intuit Quicken Windows 2013"),    "QWIN",       "2200"},
67   { I18S("Intuit Quicken Windows 2012"),    "QWIN",       "2100"},
68   { I18S("Intuit Quicken Windows 2011"),    "QWIN",       "2000"},
69   { I18S("Intuit Quicken Windows 2010"),    "QWIN",       "1900"},
70   { I18S("Intuit Quicken Windows 2009"),    "QWIN",       "1800"},
71   { I18S("Intuit Quicken Windows 2008"),    "QWIN",       "1700"},
72   { I18S("Intuit Quicken Windows 2007"),    "QWIN",       "1600"},
73   { I18S("Intuit Quicken Windows 2006"),    "QWIN",       "1500"},
74   { I18S("Intuit Quicken Windows 2005"),    "QWIN",       "1400"},
75 
76   { I18S("Intuit Quicken Mac 2008"),        "QMOFX",      "1700"},
77   { I18S("Intuit Quicken Mac 2007"),        "QMOFX",      "1600"},
78   { I18S("Intuit Quicken Mac 2006"),        "QMOFX",      "1500"},
79   { I18S("Intuit Quicken Mac 2005"),        "QMOFX",      "1400"},
80 
81   { I18S("Intuit QuickBooks Windows 2008"), "QBW",        "1800"},
82   { I18S("Intuit QuickBooks Windows 2007"), "QBW",        "1700"},
83   { I18S("Intuit QuickBooks Windows 2006"), "QBW",        "1600"},
84   { I18S("Intuit QuickBooks Windows 2005"), "QBW",        "1500"},
85 
86   { I18S("Microsoft Money Plus"),           "Money Plus", "1700"},
87   { I18S("Microsoft Money 2007"),           "Money",      "1600"},
88   { I18S("Microsoft Money 2006"),           "Money",      "1500"},
89   { I18S("Microsoft Money 2005"),           "Money",      "1400"},
90   { I18S("Microsoft Money 2004"),           "Money",      "1200"},
91   { I18S("Microsoft Money 2003"),           "Money",      "1100"},
92 
93   { I18S("ProSaldo Money 2013"),            "PROSALDO",   "11005"},
94 
95   { NULL, NULL, NULL}
96 };
97 
98 
99 
100 
101 
AO_Provider_new(AB_BANKING * ab)102 AB_PROVIDER *AO_Provider_new(AB_BANKING *ab)
103 {
104   AB_PROVIDER *pro;
105   AO_PROVIDER *dp;
106 
107   pro=AB_Provider_new(ab, "aqofxconnect");
108   GWEN_NEW_OBJECT(AO_PROVIDER, dp);
109   GWEN_INHERIT_SETDATA(AB_PROVIDER, AO_PROVIDER, pro, dp,
110                        AO_Provider_FreeData);
111 
112   AB_Provider_SetInitFn(pro, AO_Provider_Init);
113   AB_Provider_SetFiniFn(pro, AO_Provider_Fini);
114 
115   AB_Provider_SetSendCommandsFn(pro, AO_Provider_SendCommands);
116   AB_Provider_SetCreateAccountObjectsFn(pro, AO_Provider_CreateAccountObject);
117   AB_Provider_SetCreateUserObjectsFn(pro, AO_Provider_CreateUserObject);
118 
119   AB_Provider_SetGetEditUserDialogFn(pro, AO_Provider_GetEditUserDialog);
120   AB_Provider_AddFlags(pro, AB_PROVIDER_FLAGS_HAS_EDITUSER_DIALOG);
121 
122   AB_Provider_SetCreateAccountObjectsFn(pro, AO_Provider_CreateAccountObject);
123   AB_Provider_SetCreateUserObjectsFn(pro, AO_Provider_CreateUserObject);
124 
125   AB_Provider_SetUpdateAccountSpecFn(pro, AO_Provider_UpdateAccountSpec);
126 
127   AB_Provider_SetControlFn(pro, AO_Control);
128 
129   AB_Provider_SetGetNewUserDialogFn(pro, AO_Provider_GetNewUserDialog);
130   AB_Provider_AddFlags(pro, AB_PROVIDER_FLAGS_HAS_NEWUSER_DIALOG);
131 
132   return pro;
133 }
134 
135 
136 
AO_Provider_FreeData(void * bp,void * p)137 void GWENHYWFAR_CB AO_Provider_FreeData(void *bp, void *p)
138 {
139   AO_PROVIDER *dp;
140 
141   dp=(AO_PROVIDER *)p;
142   assert(dp);
143 
144   GWEN_FREE_OBJECT(dp);
145 }
146 
147 
148 
AO_Provider_Init(AB_PROVIDER * pro,GWEN_DB_NODE * dbData)149 int AO_Provider_Init(AB_PROVIDER *pro, GWEN_DB_NODE *dbData)
150 {
151   AO_PROVIDER *dp;
152   const char *logLevelName;
153   uint32_t currentVersion;
154   uint32_t lastVersion;
155 
156   assert(pro);
157   dp=GWEN_INHERIT_GETDATA(AB_PROVIDER, AO_PROVIDER, pro);
158   assert(dp);
159 
160   /* setup logging */
161   if (!GWEN_Logger_IsOpen(AQOFXCONNECT_LOGDOMAIN)) {
162     GWEN_Logger_Open(AQOFXCONNECT_LOGDOMAIN, "aqofxconnect", 0, GWEN_LoggerType_Console, GWEN_LoggerFacility_User);
163   }
164 
165   logLevelName=getenv("AQOFXCONNECT_LOGLEVEL");
166   if (logLevelName) {
167     GWEN_LOGGER_LEVEL ll;
168 
169     ll=GWEN_Logger_Name2Level(logLevelName);
170     if (ll!=GWEN_LoggerLevel_Unknown) {
171       GWEN_Logger_SetLevel(AQOFXCONNECT_LOGDOMAIN, ll);
172       DBG_WARN(AQOFXCONNECT_LOGDOMAIN, "Overriding loglevel for AqOFXConnect with \"%s\"", logLevelName);
173     }
174     else {
175       DBG_ERROR(AQOFXCONNECT_LOGDOMAIN, "Unknown loglevel \"%s\"", logLevelName);
176     }
177   }
178 
179   DBG_NOTICE(AQOFXCONNECT_LOGDOMAIN, "Initializing AqOfxConnect backend");
180 
181   dp->dbConfig=dbData;
182 
183 
184   /* check whether we need to update */
185   currentVersion=
186     (AQBANKING_VERSION_MAJOR<<24) |
187     (AQBANKING_VERSION_MINOR<<16) |
188     (AQBANKING_VERSION_PATCHLEVEL<<8) |
189     AQBANKING_VERSION_BUILD;
190   lastVersion=GWEN_DB_GetIntValue(dbData, "lastVersion", 0, 0);
191 
192   if (lastVersion<currentVersion) {
193     int rv;
194 
195     DBG_WARN(AQOFXCONNECT_LOGDOMAIN, "Updating configuration for AqOfxConnect (before init)");
196     rv=AO_Provider_UpdatePreInit(pro, lastVersion, currentVersion);
197     if (rv<0) {
198       DBG_INFO(AQOFXCONNECT_LOGDOMAIN, "here (%d)", rv);
199       return rv;
200     }
201   }
202 
203   /* init */
204   dp->lastJobId=GWEN_DB_GetIntValue(dp->dbConfig, "lastJobId", 0, 0);
205   dp->connectTimeout=GWEN_DB_GetIntValue(dp->dbConfig, "connectTimeout", 0, AO_PROVIDER_CONNECT_TIMEOUT);
206   dp->sendTimeout=GWEN_DB_GetIntValue(dp->dbConfig, "sendTimeout", 0, AO_PROVIDER_SEND_TIMEOUT);
207   dp->recvTimeout=GWEN_DB_GetIntValue(dp->dbConfig, "recvTimeout", 0, AO_PROVIDER_RECV_TIMEOUT);
208 
209   /* update post-init */
210   if (lastVersion<currentVersion) {
211     int rv;
212 
213     DBG_WARN(AQOFXCONNECT_LOGDOMAIN, "Updating configuration for AqOfxConnect (after init)");
214     rv=AO_Provider_UpdatePostInit(pro, lastVersion, currentVersion);
215     if (rv<0) {
216       DBG_INFO(AQOFXCONNECT_LOGDOMAIN, "here (%d)", rv);
217       return rv;
218     }
219   }
220 
221 
222   return 0;
223 }
224 
225 
226 
AO_Provider_Fini(AB_PROVIDER * pro,GWEN_DB_NODE * dbData)227 int AO_Provider_Fini(AB_PROVIDER *pro, GWEN_DB_NODE *dbData)
228 {
229   AO_PROVIDER *dp;
230   uint32_t currentVersion;
231 
232   assert(pro);
233   dp=GWEN_INHERIT_GETDATA(AB_PROVIDER, AO_PROVIDER, pro);
234   assert(dp);
235 
236   DBG_INFO(AQOFXCONNECT_LOGDOMAIN, "Deinitializing AqOFXDC backend");
237 
238   currentVersion=
239     (AQBANKING_VERSION_MAJOR<<24) |
240     (AQBANKING_VERSION_MINOR<<16) |
241     (AQBANKING_VERSION_PATCHLEVEL<<8) |
242     AQBANKING_VERSION_BUILD;
243 
244   /* save version */
245   DBG_NOTICE(AQOFXCONNECT_LOGDOMAIN, "Setting version %08x", currentVersion);
246   GWEN_DB_SetIntValue(dbData, GWEN_DB_FLAGS_OVERWRITE_VARS, "lastVersion", currentVersion);
247 
248   /* save vars */
249   GWEN_DB_SetIntValue(dbData, GWEN_DB_FLAGS_OVERWRITE_VARS, "lastJobId", dp->lastJobId);
250   GWEN_DB_SetIntValue(dbData, GWEN_DB_FLAGS_OVERWRITE_VARS, "connectTimeout", dp->connectTimeout);
251   GWEN_DB_SetIntValue(dbData, GWEN_DB_FLAGS_OVERWRITE_VARS, "sendTimeout", dp->sendTimeout);
252   GWEN_DB_SetIntValue(dbData, GWEN_DB_FLAGS_OVERWRITE_VARS, "recvTimeout", dp->recvTimeout);
253 
254   dp->dbConfig=0;
255 
256   DBG_INFO(AQOFXCONNECT_LOGDOMAIN, "Deinit done");
257 
258   return 0;
259 }
260 
261 
262 
AO_Provider_CreateAccountObject(AB_PROVIDER * pro)263 AB_ACCOUNT *AO_Provider_CreateAccountObject(AB_PROVIDER *pro)
264 {
265   return AO_Account_new(pro);
266 }
267 
268 
269 
AO_Provider_CreateUserObject(AB_PROVIDER * pro)270 AB_USER *AO_Provider_CreateUserObject(AB_PROVIDER *pro)
271 {
272   return AO_User_new(pro);
273 }
274 
275 
276 
277 
AO_Provider_GetEditUserDialog(AB_PROVIDER * pro,AB_USER * u)278 GWEN_DIALOG *AO_Provider_GetEditUserDialog(AB_PROVIDER *pro, AB_USER *u)
279 {
280   AO_PROVIDER *xp;
281   GWEN_DIALOG *dlg;
282 
283   assert(pro);
284   xp=GWEN_INHERIT_GETDATA(AB_PROVIDER, AO_PROVIDER, pro);
285   assert(xp);
286 
287   dlg=AO_EditUserDialog_new(pro, u, 1);
288   if (dlg==NULL) {
289     DBG_INFO(AQOFXCONNECT_LOGDOMAIN, "here (no dialog)");
290     return NULL;
291   }
292 
293   return dlg;
294 }
295 
296 
297 
AO_Provider_GetNewUserDialog(AB_PROVIDER * pro,int i)298 GWEN_DIALOG *AO_Provider_GetNewUserDialog(AB_PROVIDER *pro, int i)
299 {
300   AO_PROVIDER *xp;
301   GWEN_DIALOG *dlg;
302 
303   assert(pro);
304   xp=GWEN_INHERIT_GETDATA(AB_PROVIDER, AO_PROVIDER, pro);
305   assert(xp);
306 
307   dlg=AO_NewUserDialog_new(pro);
308   if (dlg==NULL) {
309     DBG_INFO(AQOFXCONNECT_LOGDOMAIN, "here (no dialog)");
310     return NULL;
311   }
312 
313   return dlg;
314 }
315 
316 
317 
AO_Provider_GetAppInfos(AB_PROVIDER * pro)318 const AO_APPINFO *AO_Provider_GetAppInfos(AB_PROVIDER *pro)
319 {
320   return _appInfos;
321 }
322 
323 
324 
AO_Provider_GetCert(AB_PROVIDER * pro,AB_USER * u)325 int AO_Provider_GetCert(AB_PROVIDER *pro, AB_USER *u)
326 {
327   AO_PROVIDER *xp;
328   int rv;
329   const char *url;
330 
331   assert(pro);
332   xp=GWEN_INHERIT_GETDATA(AB_PROVIDER, AO_PROVIDER, pro);
333   assert(xp);
334 
335   url=AO_User_GetServerAddr(u);
336   if (url && *url) {
337     uint32_t uFlags;
338     uint32_t hFlags=0;
339     uint32_t pid;
340 
341     uFlags=AO_User_GetFlags(u);
342     if (uFlags & AO_USER_FLAGS_FORCE_SSL3)
343       hFlags|=GWEN_HTTP_SESSION_FLAGS_FORCE_SSL3;
344 
345     pid=GWEN_Gui_ProgressStart(GWEN_GUI_PROGRESS_ALLOW_EMBED |
346                                GWEN_GUI_PROGRESS_SHOW_PROGRESS |
347                                GWEN_GUI_PROGRESS_SHOW_ABORT,
348                                I18N("Getting Certificate"),
349                                I18N("We are now asking the server for its "
350                                     "SSL certificate"),
351                                GWEN_GUI_PROGRESS_NONE,
352                                0);
353 
354     rv=AB_Banking_GetCert(AB_Provider_GetBanking(pro),
355                           url,
356                           "https", 443, &hFlags, pid);
357     if (rv<0) {
358       GWEN_Gui_ProgressEnd(pid);
359       return rv;
360     }
361 
362     if (hFlags & GWEN_HTTP_SESSION_FLAGS_FORCE_SSL3) {
363       DBG_INFO(AQOFXCONNECT_LOGDOMAIN, "Setting ForceSSLv3 flag");
364       uFlags|=AO_USER_FLAGS_FORCE_SSL3;
365     }
366     else {
367       DBG_INFO(AQOFXCONNECT_LOGDOMAIN, "Clearing ForceSSLv3 flag");
368       uFlags&=~AO_USER_FLAGS_FORCE_SSL3;
369     }
370     AO_User_SetFlags(u, uFlags);
371     GWEN_Gui_ProgressEnd(pid);
372     return 0;
373   }
374   else {
375     DBG_ERROR(AQOFXCONNECT_LOGDOMAIN, "No url");
376     return GWEN_ERROR_INVALID;
377   }
378 }
379 
380 
381 
AO_Provider_RequestStatements(AB_PROVIDER * pro,AB_USER * u,AB_ACCOUNT * a,AB_TRANSACTION * j,AB_IMEXPORTER_CONTEXT * ictx)382 int AO_Provider_RequestStatements(AB_PROVIDER *pro, AB_USER *u, AB_ACCOUNT *a, AB_TRANSACTION *j,
383                                   AB_IMEXPORTER_CONTEXT *ictx)
384 {
385   int rv;
386 
387   if (1) { /* TODO: Select OFX version */
388     rv=AO_V1_RequestStatements(pro, u, a, j, ictx);
389   }
390   else {
391     rv=AO_V2_RequestStatements(pro, u, a, j, ictx);
392   }
393   if (rv<0) {
394     DBG_INFO(AQOFXCONNECT_LOGDOMAIN, "Error adding request element (%d)", rv);
395     return rv;
396   }
397 
398   return 0;
399 }
400 
401 
402 
AO_Provider_RequestAccounts(AB_PROVIDER * pro,AB_USER * u,int keepOpen)403 int AO_Provider_RequestAccounts(AB_PROVIDER *pro, AB_USER *u, int keepOpen)
404 {
405   AO_PROVIDER *dp;
406   int rv;
407   uint32_t pid;
408   AB_IMEXPORTER_CONTEXT *ictx;
409 
410   assert(pro);
411   dp=GWEN_INHERIT_GETDATA(AB_PROVIDER, AO_PROVIDER, pro);
412   assert(dp);
413 
414   pid=GWEN_Gui_ProgressStart(GWEN_GUI_PROGRESS_ALLOW_SUBLEVELS |
415                              GWEN_GUI_PROGRESS_SHOW_PROGRESS |
416                              GWEN_GUI_PROGRESS_SHOW_LOG |
417                              GWEN_GUI_PROGRESS_ALWAYS_SHOW_LOG |
418                              (keepOpen?GWEN_GUI_PROGRESS_KEEP_OPEN:0) |
419                              GWEN_GUI_PROGRESS_SHOW_ABORT,
420                              I18N("Requesting account list"),
421                              I18N("We are now requesting a list of "
422                                   "accounts\n"
423                                   "which can be managed via OFX.\n"
424                                   "<html>"
425                                   "We are now requesting a list of "
426                                   "accounts "
427                                   "which can be managed via <i>OFX</i>.\n"
428                                   "</html>"),
429                              1,
430                              0);
431 
432   ictx=AB_ImExporterContext_new();
433   if (1) { /* TODO: Select OFX version */
434     rv=AO_V1_RequestAccounts(pro, u, ictx);
435   }
436   else {
437     rv=AO_V2_RequestAccounts(pro, u, ictx);
438   }
439   if (rv<0) {
440     DBG_ERROR(AQOFXCONNECT_LOGDOMAIN, "here (%d)", rv);
441     GWEN_Gui_ProgressEnd(pid);
442     AB_ImExporterContext_free(ictx);
443     return rv;
444   }
445 
446   /* create accounts */
447   rv=AO_Provider__ProcessImporterContext(pro, u, ictx);
448   if (rv<0) {
449     DBG_ERROR(AQOFXCONNECT_LOGDOMAIN, "Error importing accounts (%d)", rv);
450     GWEN_Gui_ProgressLog(pid,
451                          GWEN_LoggerLevel_Error,
452                          I18N("Error importing accounts"));
453     AB_ImExporterContext_free(ictx);
454     GWEN_Gui_ProgressEnd(pid);
455     return rv;
456   }
457 
458 
459   AB_ImExporterContext_free(ictx);
460 
461   GWEN_Gui_ProgressEnd(pid);
462   return 0;
463 }
464 
465 
466 
467 
468 
469 #include "provider_accspec.c"
470 #include "provider_sendcmd.c"
471 #include "provider_update.c"
472 
473 
474