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