1 /*  HomeBank -- Free, easy, personal accounting for everyone.
2  *  Copyright (C) 1995-2021 Maxime DOYEN
3  *
4  *  This file is part of HomeBank.
5  *
6  *  HomeBank is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  HomeBank is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 
21 #include "homebank.h"
22 
23 #include "hb-transaction.h"
24 #include "hb-xml.h"
25 
26 #include "ui-dialogs.h"
27 
28 /****************************************************************************/
29 /* Debug macros                                                             */
30 /****************************************************************************/
31 #define MYDEBUG 0
32 
33 #if MYDEBUG
34 #define DB(x) (x);
35 #else
36 #define DB(x);
37 #endif
38 
39 /* our global datas */
40 extern struct HomeBank *GLOBALS;
41 extern struct Preferences *PREFS;
42 
43 
44 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
45 
46 
47 // v0.1 to v0.2 : we must change account reference by making a +1 to its index references
homebank_upgrade_to_v02(void)48 static void homebank_upgrade_to_v02(void)
49 {
50 GList *lst_acc, *lnk_acc;
51 GList *list;
52 GHashTable *h_old_acc;
53 
54 
55 	DB( g_print("\n[hb-xml] homebank_upgrade_to_v02\n") );
56 
57 	//keep old hashtable with us
58 	h_old_acc = GLOBALS->h_acc;
59 	da_acc_new();
60 
61 	lst_acc = g_hash_table_get_values(h_old_acc);
62 	lnk_acc = g_list_first(lst_acc);
63 	while (lnk_acc != NULL)
64 	{
65 	Account *acc = lnk_acc->data;
66 
67 		acc->key++;
68 		acc->pos++;
69 		da_acc_insert (acc);
70 
71 		list = g_queue_peek_head_link(acc->txn_queue);
72 		while (list != NULL)
73 		{
74 		Transaction *entry = list->data;
75 			entry->kacc++;
76 			entry->kxferacc++;
77 			list = g_list_next(list);
78 		}
79 		lnk_acc = g_list_next(lnk_acc);
80 	}
81 	g_list_free(lst_acc);
82 
83 	//we loose some small memory here
84 	g_hash_table_steal_all(h_old_acc);
85 
86 	list = g_list_first(GLOBALS->arc_list);
87 	while (list != NULL)
88 	{
89 	Archive *entry = list->data;
90 		entry->kacc++;
91 		entry->kxferacc++;
92 		list = g_list_next(list);
93 	}
94 }
95 
96 // v0.2 to v0.3 : we must assume categories exists : bugs 303886, 303738
homebank_upgrade_to_v03(void)97 static void homebank_upgrade_to_v03(void)
98 {
99 GList *lst_acc, *lnk_acc;
100 GList *list;
101 
102 	DB( g_print("\n[hb-xml] homebank_upgrade_to_v03\n") );
103 
104 	lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
105 	lnk_acc = g_list_first(lst_acc);
106 	while (lnk_acc != NULL)
107 	{
108 	Account *acc = lnk_acc->data;
109 
110 		list = g_queue_peek_head_link(acc->txn_queue);
111 		while (list != NULL)
112 		{
113 		Transaction *entry = list->data;
114 
115 			da_transaction_consistency(entry);
116 			list = g_list_next(list);
117 		}
118 		lnk_acc = g_list_next(lnk_acc);
119 	}
120 	g_list_free(lst_acc);
121 
122 
123 	list = g_list_first(GLOBALS->arc_list);
124 	while (list != NULL)
125 	{
126 	Archive *entry = list->data;
127 
128 		da_archive_consistency(entry);
129 		list = g_list_next(list);
130 	}
131 }
132 
homebank_upgrade_to_v04(void)133 static void homebank_upgrade_to_v04(void)
134 {
135 	DB( g_print("\n[hb-xml] homebank_upgrade_to_v04\n") );
136 
137 	da_archive_glist_sorted(1);
138 }
139 
140 
141 // v0.4 to v0.5 :
142 // we must assume kxferacc exists in archives for internal xfer : bug 528923
143 // if not, delete automation from the archive
homebank_upgrade_to_v05(void)144 static void homebank_upgrade_to_v05(void)
145 {
146 GList *list;
147 
148 	DB( g_print("\n[hb-xml] homebank_upgrade_to_v05\n") );
149 
150 	list = g_list_first(GLOBALS->arc_list);
151 	while (list != NULL)
152 	{
153 	Archive *entry = list->data;
154 
155 		da_archive_consistency(entry);
156 		list = g_list_next(list);
157 	}
158 }
159 
160 
161 // v0.5 to v0.6 : we must change kxferacc to 0 on non Xfer transactions
162 //#677351
homebank_upgrade_to_v06(void)163 static void homebank_upgrade_to_v06(void)
164 {
165 GList *lst_acc, *lnk_acc;
166 GList *list;
167 
168 	DB( g_print("\n[hb-xml] homebank_upgrade_to_v06\n") );
169 
170 	lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
171 	lnk_acc = g_list_first(lst_acc);
172 	while (lnk_acc != NULL)
173 	{
174 	Account *acc = lnk_acc->data;
175 
176 		list = g_queue_peek_head_link(acc->txn_queue);
177 		while (list != NULL)
178 		{
179 		Transaction *entry = list->data;
180 			da_transaction_consistency(entry);
181 			list = g_list_next(list);
182 		}
183 		lnk_acc = g_list_next(lnk_acc);
184 	}
185 	g_list_free(lst_acc);
186 
187 
188 	list = g_list_first(GLOBALS->arc_list);
189 	while (list != NULL)
190 	{
191 	Archive *entry = list->data;
192 		da_archive_consistency(entry);
193 		list = g_list_next(list);
194 	}
195 }
196 
197 
198 // v0.7 AF_BUDGET deleted instead of AF_NOBUDGET
homebank_upgrade_to_v07(void)199 static void homebank_upgrade_to_v07(void)
200 {
201 GList *lacc, *list;
202 
203 	DB( g_print("\n[hb-xml] homebank_upgrade_to_v07\n") );
204 
205 	lacc = list = g_hash_table_get_values(GLOBALS->h_acc);
206 	while (list != NULL)
207 	{
208 	Account *acc = list->data;
209 
210 		if( acc->flags & AF_OLDBUDGET )	// budget include
211 		{
212 			acc->flags &= ~(AF_OLDBUDGET);
213 		}
214 		else
215 		{
216 			acc->flags |= AF_NOBUDGET;
217 		}
218 
219 		list = g_list_next(list);
220 	}
221 	g_list_free(lacc);
222 
223 }
224 
homebank_upgrade_to_v08(void)225 static void homebank_upgrade_to_v08(void)
226 {
227 GList *lst_acc, *lnk_acc;
228 GList *list;
229 
230 	DB( g_print("\n[hb-xml] homebank_upgrade_to_v08\n") );
231 
232 	lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
233 	lnk_acc = g_list_first(lst_acc);
234 	while (lnk_acc != NULL)
235 	{
236 	Account *acc = lnk_acc->data;
237 
238 		list = g_queue_peek_head_link(acc->txn_queue);
239 		while (list != NULL)
240 		{
241 		Transaction *entry = list->data;
242 			da_transaction_consistency(entry);
243 			list = g_list_next(list);
244 		}
245 		lnk_acc = g_list_next(lnk_acc);
246 	}
247 	g_list_free(lst_acc);
248 
249 
250 }
251 
252 
homebank_upgrade_to_v10(void)253 static void homebank_upgrade_to_v10(void)
254 {
255 GList *lst_acc, *lnk_acc;
256 GList *list;
257 
258 	DB( g_print("\n[hb-xml] homebank_upgrade_to_v10\n") );
259 
260 	lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
261 	lnk_acc = g_list_first(lst_acc);
262 	while (lnk_acc != NULL)
263 	{
264 	Account *acc = lnk_acc->data;
265 
266 		list = g_queue_peek_head_link(acc->txn_queue);
267 		while (list != NULL)
268 		{
269 		Transaction *entry = list->data;
270 
271 			entry->status = TXN_STATUS_NONE;
272 			if(entry->flags & OLDF_VALID)
273 				entry->status = TXN_STATUS_RECONCILED;
274 			else
275 				if(entry->flags & OLDF_REMIND)
276 					entry->status = TXN_STATUS_REMIND;
277 
278 			//remove those flags
279 			entry->flags &= ~(OLDF_VALID|OLDF_REMIND);
280 
281 			list = g_list_next(list);
282 		}
283 		lnk_acc = g_list_next(lnk_acc);
284 	}
285 	g_list_free(lst_acc);
286 
287 }
288 
289 
homebank_upgrade_to_v11(void)290 static void homebank_upgrade_to_v11(void)
291 {
292 GList *list;
293 
294 	DB( g_print("\n[hb-xml] homebank_upgrade_to_v11\n") );
295 
296 	list = g_list_first(GLOBALS->arc_list);
297 	while (list != NULL)
298 	{
299 	Archive *entry = list->data;
300 
301 		entry->status = TXN_STATUS_NONE;
302 		if(entry->flags & OLDF_VALID)
303 			entry->status = TXN_STATUS_RECONCILED;
304 		else
305 			if(entry->flags & OLDF_REMIND)
306 				entry->status = TXN_STATUS_REMIND;
307 
308 		//remove those flags
309 		entry->flags &= ~(OLDF_VALID|OLDF_REMIND);
310 
311 		list = g_list_next(list);
312 	}
313 
314 }
315 
316 
317 // v0.6 to v0.7 : assign a default currency
homebank_upgrade_to_v12(void)318 static void homebank_upgrade_to_v12(void)
319 {
320 	DB( g_print("\n[hb-xml] homebank_upgrade_to_v12\n") );
321 
322 	// set a base currency to the hbfile if not
323 	DB( g_print("GLOBALS->kcur %d\n", GLOBALS->kcur) );
324 
325 	ui_dialog_upgrade_choose_currency();
326 }
327 
328 
homebank_upgrade_to_v12_7(void)329 static void homebank_upgrade_to_v12_7(void)
330 {
331 GList *lst_acc, *lnk_acc;
332 
333 	DB( g_print("\n[hb-xml] homebank_upgrade_to_v12\n") );
334 
335 	//#1674045 exclude closed account from everywhere to
336 	//keep continuity for user that don't want to change this
337 	lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
338 	lnk_acc = g_list_first(lst_acc);
339 	while (lnk_acc != NULL)
340 	{
341 	Account *acc = lnk_acc->data;
342 
343 		if( acc->flags & AF_CLOSED )
344 		{
345 			if( !(acc->flags & AF_NOSUMMARY) )
346 				acc->flags |= AF_NOSUMMARY;
347 			if( !(acc->flags & AF_NOBUDGET) )
348 				acc->flags |= AF_NOBUDGET;
349 			if( !(acc->flags & AF_NOREPORT) )
350 				acc->flags |= AF_NOREPORT;
351 		}
352 		lnk_acc = g_list_next(lnk_acc);
353 	}
354 	g_list_free(lst_acc);
355 }
356 
357 
homebank_upgrade_to_v13(void)358 static void homebank_upgrade_to_v13(void)
359 {
360 GList *list;
361 guint32 newkey;
362 
363 	DB( g_print("\n[hb-xml] homebank_upgrade_to_v13\n") );
364 
365 	//#1008629 assign a key to each archive
366 	newkey = 1;
367 	list = g_list_first(GLOBALS->arc_list);
368 	while (list != NULL)
369 	{
370 	Archive *item = list->data;
371 
372 		item->key = newkey++;
373 		list = g_list_next(list);
374 	}
375 
376 }
377 
378 
homebank_upgrade_to_v14(void)379 static void homebank_upgrade_to_v14(void)
380 {
381 GList *lst_acc, *lnk_acc, *lasg;
382 GList *list;
383 
384 	DB( g_print("\n[hb-xml] homebank_upgrade_to_v14\n") );
385 
386 	//internal xfer no more a payment => goto a flags
387 	//update every txn/arc
388 	lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
389 	lnk_acc = g_list_first(lst_acc);
390 	while (lnk_acc != NULL)
391 	{
392 	Account *acc = lnk_acc->data;
393 
394 		list = g_queue_peek_head_link(acc->txn_queue);
395 		while (list != NULL)
396 		{
397 		Transaction *item = list->data;
398 
399 			if( item->paymode == OLDPAYMODE_INTXFER )
400 			{
401 				item->flags |= OF_INTXFER;
402 				item->paymode = PAYMODE_NONE;
403 			}
404 			list = g_list_next(list);
405 		}
406 		lnk_acc = g_list_next(lnk_acc);
407 	}
408 	g_list_free(lst_acc);
409 
410 	list = g_list_first(GLOBALS->arc_list);
411 	while (list != NULL)
412 	{
413 	Archive *item = list->data;
414 
415 		if( item->paymode == OLDPAYMODE_INTXFER )
416 		{
417 			item->flags |= OF_INTXFER;
418 			item->paymode = PAYMODE_NONE;
419 		}
420 		list = g_list_next(list);
421 	}
422 
423 	//assignment now have position+name, so initiate it
424 	lasg = list = g_hash_table_get_values(GLOBALS->h_rul);
425 	while (list != NULL)
426 	{
427 	Assign *item = list->data;
428 
429 		item->pos  = item->key;
430 		list = g_list_next(list);
431 	}
432 	g_list_free(lasg);
433 
434 }
435 
436 
437 
438 // lower v0.6 : we must assume categories/payee exists
439 // and strong link to xfer
440 // #632496
homebank_upgrade_lower_v06(void)441 static void homebank_upgrade_lower_v06(void)
442 {
443 GList *lst_acc, *lnk_acc;
444 Category *cat;
445 Payee *pay;
446 GList *lrul, *list;
447 
448 	DB( g_print("\n[hb-xml] homebank_upgrade_lower_v06\n") );
449 
450 	lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
451 	lnk_acc = g_list_first(lst_acc);
452 	while (lnk_acc != NULL)
453 	{
454 	Account *acc = lnk_acc->data;
455 
456 		list = g_queue_peek_head_link(acc->txn_queue);
457 		while (list != NULL)
458 		{
459 		Transaction *entry = list->data;
460 
461 			//also strong link internal xfer
462 			if(entry->paymode == OLDPAYMODE_INTXFER && entry->kxfer == 0)
463 			{
464 			Transaction *child = transaction_old_get_child_transfer(entry);
465 				if(child != NULL)
466 				{
467 					transaction_xfer_change_to_child(entry, child);
468 				}
469 			}
470 
471 			da_transaction_consistency(entry);
472 
473 			list = g_list_next(list);
474 		}
475 		lnk_acc = g_list_next(lnk_acc);
476 	}
477 	g_list_free(lst_acc);
478 
479 
480 	lrul = list = g_hash_table_get_values(GLOBALS->h_rul);
481 	while (list != NULL)
482 	{
483 	Assign *entry = list->data;
484 
485 		cat = da_cat_get(entry->kcat);
486 		if(cat == NULL)
487 		{
488 			DB( g_print(" !! fixing cat for rul: %d is unknown\n", entry->kcat) );
489 			entry->kcat = 0;
490 		}
491 
492 		pay = da_pay_get(entry->kpay);
493 		if(pay == NULL)
494 		{
495 			DB( g_print(" !! fixing pay for rul: %d is unknown\n", entry->kpay) );
496 			entry->kpay = 0;
497 		}
498 
499 
500 		list = g_list_next(list);
501 	}
502 	g_list_free(lrul);
503 }
504 
505 
506 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
507 
508 
homebank_load_xml_acc(ParseContext * ctx,const gchar ** attribute_names,const gchar ** attribute_values)509 static void homebank_load_xml_acc(ParseContext *ctx, const gchar **attribute_names, const gchar **attribute_values)
510 {
511 Account *entry = da_acc_malloc();
512 gint i;
513 
514 	for (i = 0; attribute_names[i] != NULL; i++)
515 	{
516 		//DB( g_print(" att='%s' val='%s'\n", attribute_names[i], attribute_values[i]) );
517 
518 		     if(!strcmp (attribute_names[i], "key"     )) { entry->key   = atoi(attribute_values[i]); }
519 		else if(!strcmp (attribute_names[i], "flags"   )) { entry->flags = atoi(attribute_values[i]); }
520 		else if(!strcmp (attribute_names[i], "pos"     )) { entry->pos   = atoi(attribute_values[i]); }
521 		else if(!strcmp (attribute_names[i], "type"    )) { entry->type = atoi(attribute_values[i]); }
522 		else if(!strcmp (attribute_names[i], "curr"    )) { entry->kcur = atoi(attribute_values[i]); }
523 		else if(!strcmp (attribute_names[i], "name"    )) { if(strcmp(attribute_values[i],"(null)") && attribute_values[i] != NULL) entry->name = g_strdup(attribute_values[i]); }
524 		else if(!strcmp (attribute_names[i], "number"  )) { if(strcmp(attribute_values[i],"(null)") && attribute_values[i] != NULL) entry->number = g_strdup(attribute_values[i]); }
525 		else if(!strcmp (attribute_names[i], "bankname")) { if(strcmp(attribute_values[i],"(null)") && attribute_values[i] != NULL) entry->bankname = g_strdup(attribute_values[i]); }
526 		else if(!strcmp (attribute_names[i], "initial" )) { entry->initial = g_ascii_strtod(attribute_values[i], NULL); }
527 
528 		else if(!strcmp (attribute_names[i], "minimum" )) { entry->minimum = g_ascii_strtod(attribute_values[i], NULL); }
529 		else if(!strcmp (attribute_names[i], "maximum" )) { entry->maximum = g_ascii_strtod(attribute_values[i], NULL); }
530 		else if(!strcmp (attribute_names[i], "cheque1" )) { entry->cheque1 = atoi(attribute_values[i]); }
531 		else if(!strcmp (attribute_names[i], "cheque2" )) { entry->cheque2 = atoi(attribute_values[i]); }
532 		else if(!strcmp (attribute_names[i], "notes"   )) { if(strcmp(attribute_values[i],"(null)") && attribute_values[i] != NULL) entry->notes = g_strdup(attribute_values[i]); }
533 		else if(!strcmp (attribute_names[i], "tpl"     )) { entry->karc = atoi(attribute_values[i]); }
534 		else if(!strcmp (attribute_names[i], "grp"     )) { entry->kgrp = atoi(attribute_values[i]); }
535 		//5.5
536 		else if(!strcmp (attribute_names[i], "ccday"   )) { entry->cccday = atoi(attribute_values[i]); }
537 		else if(!strcmp (attribute_names[i], "rdate"   )) { entry->rdate = atoi(attribute_values[i]); }
538 	}
539 
540 	//all attribute loaded: append
541 	da_acc_insert(entry);
542 }
543 
544 
homebank_load_xml_asg(ParseContext * ctx,const gchar ** attribute_names,const gchar ** attribute_values)545 static void homebank_load_xml_asg(ParseContext *ctx, const gchar **attribute_names, const gchar **attribute_values)
546 {
547 Assign *entry = da_asg_malloc();
548 gint exact = 0;
549 gint i;
550 
551 	for (i = 0; attribute_names[i] != NULL; i++)
552 	{
553 		//DB( g_print(" att='%s' val='%s'\n", attribute_names[i], attribute_values[i]) );
554 
555 		     if(!strcmp (attribute_names[i], "key"     )) { entry->key   = atoi(attribute_values[i]); }
556 		else if(!strcmp (attribute_names[i], "flags"   )) { entry->flags = atoi(attribute_values[i]); }
557 		else if(!strcmp (attribute_names[i], "pos"     )) { entry->pos   = atoi(attribute_values[i]); }
558 		else if(!strcmp (attribute_names[i], "field"   )) { entry->field = atoi(attribute_values[i]); }
559 		else if(!strcmp (attribute_names[i], "name"    )) { if(strcmp(attribute_values[i],"(null)") && attribute_values[i] != NULL) entry->search = g_strdup(attribute_values[i]); }
560 		else if(!strcmp (attribute_names[i], "notes"   )) { if(strcmp(attribute_values[i],"(null)") && attribute_values[i] != NULL) entry->notes = g_strdup(attribute_values[i]); }
561 		else if(!strcmp (attribute_names[i], "payee"   )) { entry->kpay = atoi(attribute_values[i]); }
562 		else if(!strcmp (attribute_names[i], "category")) { entry->kcat = atoi(attribute_values[i]); }
563 		else if(!strcmp (attribute_names[i], "paymode" )) { entry->paymode = atoi(attribute_values[i]); }
564 		// prior v08
565 		else if(!strcmp (attribute_names[i], "exact" )) { exact = atoi(attribute_values[i]); }
566 	}
567 
568 	/* in v08 exact moved to flag */
569 	if( ctx->file_version <= 0.7)
570 	{
571 		entry->flags = (ASGF_DOCAT|ASGF_DOPAY);
572 		if( exact > 0 )
573 		   entry->flags |= ASGF_EXACT;
574 	}
575 
576 	//all attribute loaded: append
577 	//#1892828 append change the pos...
578 	//da_asg_append(entry);
579 	da_asg_insert(entry);
580 
581 }
582 
583 
homebank_load_xml_pay(ParseContext * ctx,const gchar ** attribute_names,const gchar ** attribute_values)584 static void homebank_load_xml_pay(ParseContext *ctx, const gchar **attribute_names, const gchar **attribute_values)
585 {
586 Payee *entry = da_pay_malloc();
587 gint i;
588 
589 	for (i = 0; attribute_names[i] != NULL; i++)
590 	{
591 		//DB( g_print(" att='%s' val='%s'\n", attribute_names[i], attribute_values[i]) );
592 
593 			 if(!strcmp (attribute_names[i], "key"  )) { entry->key = atoi(attribute_values[i]); }
594 		//else if(!strcmp (attribute_names[i], "flags")) { entry->flags = atoi(attribute_values[i]); }
595 		else if(!strcmp (attribute_names[i], "name" )) { entry->name = g_strdup(attribute_values[i]); }
596 		else if(!strcmp (attribute_names[i], "category")) { entry->kcat = atoi(attribute_values[i]); }
597 		else if(!strcmp (attribute_names[i], "paymode" )) { entry->paymode = atoi(attribute_values[i]); }
598 	}
599 
600 	//all attribute loaded: append
601 	da_pay_insert(entry);
602 }
603 
604 
homebank_load_xml_prop(ParseContext * ctx,const gchar ** attribute_names,const gchar ** attribute_values)605 static void homebank_load_xml_prop(ParseContext *ctx, const gchar **attribute_names, const gchar **attribute_values)
606 {
607 gint i;
608 
609 	for (i = 0; attribute_names[i] != NULL; i++)
610 	{
611 		     if(!strcmp (attribute_names[i], "title"       )) { g_free(GLOBALS->owner); GLOBALS->owner = g_strdup(attribute_values[i]); }
612 		else if(!strcmp (attribute_names[i], "curr"        )) { GLOBALS->kcur = atoi(attribute_values[i]); }
613 		else if(!strcmp (attribute_names[i], "car_category")) { GLOBALS->vehicle_category = atoi(attribute_values[i]); }
614 		else if(!strcmp (attribute_names[i], "auto_smode"  )) { GLOBALS->auto_smode = atoi(attribute_values[i]); }
615 		else if(!strcmp (attribute_names[i], "auto_weekday")) { GLOBALS->auto_weekday = atoi(attribute_values[i]); }
616 		else if(!strcmp (attribute_names[i], "auto_nbdays" )) { GLOBALS->auto_nbdays = atoi(attribute_values[i]); }
617 	}
618 }
619 
620 
homebank_load_xml_cat(ParseContext * ctx,const gchar ** attribute_names,const gchar ** attribute_values)621 static void homebank_load_xml_cat(ParseContext *ctx, const gchar **attribute_names, const gchar **attribute_values)
622 {
623 Category *entry = da_cat_malloc();
624 gboolean budget;
625 gint i, j;
626 
627 	for (i = 0; attribute_names[i] != NULL; i++)
628 	{
629 		//DB( g_print(" att='%s' val='%s'\n", attribute_names[i], attribute_values[i]) );
630 
631 		     if(!strcmp (attribute_names[i], "key"   )) { entry->key = atoi(attribute_values[i]); }
632 		else if(!strcmp (attribute_names[i], "parent")) { entry->parent = atoi(attribute_values[i]); }
633 		else if(!strcmp (attribute_names[i], "flags" )) { entry->flags = atoi(attribute_values[i]); }
634 		else if(!strcmp (attribute_names[i], "name"  )) { entry->name = g_strdup(attribute_values[i]); }
635 
636 		budget = FALSE;
637 		for(j=0;j<=12;j++)
638 		{
639 		gchar *tmpname;
640 
641 			tmpname = g_strdup_printf ("b%d", j);
642 			if(!(strcmp (attribute_names[i], tmpname))) { entry->budget[j] = g_ascii_strtod(attribute_values[i], NULL); }
643 			g_free(tmpname);
644 
645 			if(entry->budget[j]) budget = TRUE;
646 		}
647 		if(budget == TRUE)
648 			entry->flags |= GF_BUDGET;
649 
650 	}
651 
652 	//all attribute loaded: append
653 	da_cat_insert( entry);
654 }
655 
656 
homebank_load_xml_cur(ParseContext * ctx,const gchar ** attribute_names,const gchar ** attribute_values)657 static void homebank_load_xml_cur(ParseContext *ctx, const gchar **attribute_names, const gchar **attribute_values)
658 {
659 Currency *entry = da_cur_malloc ();
660 gint i;
661 
662 	for (i = 0; attribute_names[i] != NULL; i++)
663 	{
664 		//DB( g_print(" att='%s' val='%s'\n", attribute_names[i], attribute_values[i]) );
665 
666 			 if(!strcmp (attribute_names[i], "key"   )) { entry->key = atoi(attribute_values[i]); }
667 		else if(!strcmp (attribute_names[i], "flags" )) { entry->flags = atoi(attribute_values[i]); }
668 		else if(!strcmp (attribute_names[i], "name"  )) { entry->name = g_strdup(attribute_values[i]); }
669 		else if(!strcmp (attribute_names[i], "iso"   )) { entry->iso_code = g_strdup(attribute_values[i]); }
670 		else if(!strcmp (attribute_names[i], "symb"  )) { entry->symbol = g_strdup(attribute_values[i]); }
671 		else if(!strcmp (attribute_names[i], "syprf" )) { entry->sym_prefix = atoi(attribute_values[i]); }
672 		else if(!strcmp (attribute_names[i], "dchar" )) { entry->decimal_char = g_strdup(attribute_values[i]); }
673 		else if(!strcmp (attribute_names[i], "gchar" )) { entry->grouping_char = g_strdup(attribute_values[i]); }
674 		else if(!strcmp (attribute_names[i], "frac"  )) { entry->frac_digits = atoi(attribute_values[i]); }
675 		else if(!strcmp (attribute_names[i], "rate"  )) { entry->rate = g_ascii_strtod(attribute_values[i], NULL); }
676 		else if(!strcmp (attribute_names[i], "mdate ")) { entry->mdate = atoi(attribute_values[i]); }
677 
678 	}
679 
680 	//all attribute loaded: append
681 	da_cur_insert (entry);
682 }
683 
684 
homebank_load_xml_grp(ParseContext * ctx,const gchar ** attribute_names,const gchar ** attribute_values)685 static void homebank_load_xml_grp(ParseContext *ctx, const gchar **attribute_names, const gchar **attribute_values)
686 {
687 Group *entry = da_grp_malloc();
688 gint i;
689 
690 	for (i = 0; attribute_names[i] != NULL; i++)
691 	{
692 		DB( g_print(" att='%s' val='%s'\n", attribute_names[i], attribute_values[i]) );
693 
694 		     if(!strcmp (attribute_names[i], "key"  )) { entry->key = atoi(attribute_values[i]); }
695 		//else if(!strcmp (attribute_names[i], "type")) { entry->type = atoi(attribute_values[i]); }
696 		//else if(!strcmp (attribute_names[i], "flags")) { entry->flags = atoi(attribute_values[i]); }
697 		else if(!strcmp (attribute_names[i], "name" )) { entry->name = g_strdup(attribute_values[i]); }
698 	}
699 
700 	//all attribute loaded: append
701 	da_grp_insert(entry);
702 }
703 
704 
705 /*static void homebank_load_xml_tag(ParseContext *ctx, const gchar **attribute_names, const gchar **attribute_values)
706 {
707 Tag *entry = da_tag_malloc();
708 gint i;
709 
710 	for (i = 0; attribute_names[i] != NULL; i++)
711 	{
712 		//DB( g_print(" att='%s' val='%s'\n", attribute_names[i], attribute_values[i]) );
713 
714 		     if(!strcmp (attribute_names[i], "key"  )) { entry->key = atoi(attribute_values[i]); }
715 		//else if(!strcmp (attribute_names[i], "flags")) { entry->flags = atoi(attribute_values[i]); }
716 		else if(!strcmp (attribute_names[i], "name" )) { entry->name = g_strdup(attribute_values[i]); }
717 	}
718 
719 	//all attribute loaded: append
720 	da_tag_insert(entry);
721 }*/
722 
723 
homebank_load_xml_fav(ParseContext * ctx,const gchar ** attribute_names,const gchar ** attribute_values)724 static void homebank_load_xml_fav(ParseContext *ctx, const gchar **attribute_names, const gchar **attribute_values)
725 {
726 Archive *entry = da_archive_malloc();
727 gchar *scat = NULL;
728 gchar *samt = NULL;
729 gchar *smem = NULL;
730 gboolean split = FALSE;
731 gint i;
732 
733 	for (i = 0; attribute_names[i] != NULL; i++)
734 	{
735 		//DB( g_print(" att='%s' val='%s'\n", attribute_names[i], attribute_values[i]) );
736 
737 		     if(!strcmp (attribute_names[i], "key"        )) { entry->key = atoi(attribute_values[i]); }
738 		else if(!strcmp (attribute_names[i], "amount"     )) { entry->amount = g_ascii_strtod(attribute_values[i], NULL); }
739 		else if(!strcmp (attribute_names[i], "account"    )) { entry->kacc = atoi(attribute_values[i]); }
740 		else if(!strcmp (attribute_names[i], "dst_account")) { entry->kxferacc = atoi(attribute_values[i]); }
741 		else if(!strcmp (attribute_names[i], "paymode"    )) { entry->paymode = atoi(attribute_values[i]); }
742 		else if(!strcmp (attribute_names[i], "st"         )) { entry->status = atoi(attribute_values[i]); }
743 		else if(!strcmp (attribute_names[i], "flags"      )) { entry->flags = atoi(attribute_values[i]); }
744 		else if(!strcmp (attribute_names[i], "payee"      )) { entry->kpay = atoi(attribute_values[i]); }
745 		else if(!strcmp (attribute_names[i], "category"   )) { entry->kcat = atoi(attribute_values[i]); }
746 		else if(!strcmp (attribute_names[i], "wording"    )) { if(strcmp(attribute_values[i],"(null)") && attribute_values[i] != NULL) entry->memo = g_strdup(attribute_values[i]); }
747 		else if(!strcmp (attribute_names[i], "info"       )) { if(strcmp(attribute_values[i],"(null)") && attribute_values[i] != NULL) entry->info = g_strdup(attribute_values[i]); }
748 		else if(!strcmp (attribute_names[i], "tags"       ))
749 		{
750 			if(attribute_values[i] != NULL && strlen(attribute_values[i]) > 0 && strcmp(attribute_values[i],"(null)") != 0 )
751 			{
752 				entry->tags = tags_parse(attribute_values[i]);
753 			}
754 		}
755 		else if(!strcmp (attribute_names[i], "nextdate"   )) { entry->nextdate = atoi(attribute_values[i]); }
756 		else if(!strcmp (attribute_names[i], "every"      )) { entry->every = atoi(attribute_values[i]); }
757 		else if(!strcmp (attribute_names[i], "unit"       )) { entry->unit = atoi(attribute_values[i]); }
758 		else if(!strcmp (attribute_names[i], "limit"      )) { entry->limit = atoi(attribute_values[i]); }
759 		else if(!strcmp (attribute_names[i], "weekend"    )) { entry->weekend = atoi(attribute_values[i]); }
760 		else if(!strcmp (attribute_names[i], "gap"        )) { entry->daygap = atoi(attribute_values[i]); }
761 		else if(!strcmp (attribute_names[i], "scat" 	  )) { scat = (gchar *)attribute_values[i]; split = TRUE; }
762 		else if(!strcmp (attribute_names[i], "samt"       )) { samt = (gchar *)attribute_values[i]; split = TRUE; }
763 		else if(!strcmp (attribute_names[i], "smem"       )) { smem = (gchar *)attribute_values[i]; split = TRUE; }
764 
765 	}
766 
767 	if(split == TRUE)
768 	{
769 		entry->splits = da_split_new ();
770 		if (da_splits_parse(entry->splits, scat, samt, smem) > 0)
771 		{
772 			entry->flags |= OF_SPLIT; //Flag that Splits are active
773 		}
774 	}
775 
776 	//all attribute loaded: append
777 	//GLOBALS->arc_list = g_list_append(GLOBALS->arc_list, entry);
778 	da_archive_append(entry);
779 }
780 
781 
homebank_load_xml_ope(ParseContext * ctx,const gchar ** attribute_names,const gchar ** attribute_values)782 static void homebank_load_xml_ope(ParseContext *ctx, const gchar **attribute_names, const gchar **attribute_values)
783 {
784 Transaction *entry = da_transaction_malloc();
785 gchar *scat = NULL;
786 gchar *samt = NULL;
787 gchar *smem = NULL;
788 gboolean split = FALSE;
789 gint i;
790 
791 	for (i = 0; attribute_names[i] != NULL; i++)
792 	{
793 		//DB( g_print(" att='%s' val='%s'\n", attribute_names[i], attribute_values[i]) );
794 
795 		     if(!strcmp (attribute_names[i], "date"       )) { entry->date = atoi(attribute_values[i]); }
796 		else if(!strcmp (attribute_names[i], "amount"     )) { entry->amount = g_ascii_strtod(attribute_values[i], NULL); }
797 		else if(!strcmp (attribute_names[i], "account"    )) { entry->kacc = atoi(attribute_values[i]); }
798 		else if(!strcmp (attribute_names[i], "dst_account")) { entry->kxferacc = atoi(attribute_values[i]); }
799 		else if(!strcmp (attribute_names[i], "paymode"    )) { entry->paymode = atoi(attribute_values[i]); }
800 		else if(!strcmp (attribute_names[i], "st"         )) { entry->status = atoi(attribute_values[i]); }
801 		else if(!strcmp (attribute_names[i], "flags"      )) { entry->flags = atoi(attribute_values[i]); }
802 		else if(!strcmp (attribute_names[i], "payee"      )) { entry->kpay = atoi(attribute_values[i]); }
803 		else if(!strcmp (attribute_names[i], "category"   )) { entry->kcat = atoi(attribute_values[i]); }
804 		else if(!strcmp (attribute_names[i], "wording"    )) { if(strcmp(attribute_values[i],"(null)") && attribute_values[i] != NULL) entry->memo = g_strdup(attribute_values[i]); }
805 		else if(!strcmp (attribute_names[i], "info"       )) { if(strcmp(attribute_values[i],"(null)") && attribute_values[i] != NULL) entry->info = g_strdup(attribute_values[i]); }
806 		else if(!strcmp (attribute_names[i], "tags"       ))
807 		{
808 			if(attribute_values[i] != NULL && strlen(attribute_values[i]) > 0 && strcmp(attribute_values[i],"(null)") != 0 )
809 			{
810 				entry->tags = tags_parse(attribute_values[i]);
811 			}
812 		}
813 		else if(!strcmp (attribute_names[i], "kxfer"    )) { entry->kxfer = atoi(attribute_values[i]); }
814 		else if(!strcmp (attribute_names[i], "scat"     )) { scat = (gchar *)attribute_values[i]; split = TRUE; }
815 		else if(!strcmp (attribute_names[i], "samt"     )) { samt = (gchar *)attribute_values[i]; split = TRUE; }
816 		else if(!strcmp (attribute_names[i], "smem"     )) { smem = (gchar *)attribute_values[i]; split = TRUE; }
817 	}
818 
819 	//bugfix 303886
820 	//if(entry->kcat < 0)
821 	//	entry->kcat = 0;
822 
823 	if(split == TRUE)
824 	{
825 		entry->splits = da_split_new ();
826 		if (da_splits_parse(entry->splits, scat, samt, smem) > 0)
827 		{
828 			entry->flags |= OF_SPLIT; //Flag that Splits are active
829 		}
830 	}
831 
832 	//all attribute loaded: append
833 	// for perf reason we use prepend here, the list will be reversed later
834 	da_transaction_prepend(entry);
835 }
836 
837 
838 
839 
840 static void
start_element_handler(GMarkupParseContext * context,const gchar * element_name,const gchar ** attribute_names,const gchar ** attribute_values,gpointer user_data,GError ** error)841 start_element_handler (GMarkupParseContext *context,
842 		       const gchar         *element_name,
843 		       const gchar        **attribute_names,
844 		       const gchar        **attribute_values,
845 		       gpointer             user_data,
846 		       GError             **error)
847 {
848 ParseContext *ctx = user_data;
849 //GtkUIManager *self = ctx->self;
850 
851 	//DB( g_print("** start element: '%s'\n", element_name) );
852 
853 	switch(element_name[0])
854 	{
855 		case 'a':
856 		{
857 			if(!strcmp (element_name, "account"))   //account
858 			{
859 				homebank_load_xml_acc(ctx, attribute_names, attribute_values);
860 			}
861 			else if(!strcmp (element_name, "asg"))  //assign
862 			{
863 				homebank_load_xml_asg(ctx, attribute_names, attribute_values);
864 			}
865 		}
866 		break;
867 
868 		case 'p':
869 		{
870 			if(!strcmp (element_name, "pay"))
871 			{
872 				homebank_load_xml_pay(ctx, attribute_names, attribute_values);
873 			}
874 			else if(!strcmp (element_name, "properties"))
875 			{
876 				homebank_load_xml_prop(ctx, attribute_names, attribute_values);
877 			}
878 		}
879 		break;
880 
881 		case 'g':
882 		{
883 			if(!strcmp (element_name, "grp"))
884 			{
885 				homebank_load_xml_grp(ctx, attribute_names, attribute_values);
886 			}
887 		}
888 		break;
889 
890 		case 'c':
891 		{
892 			if(!strcmp (element_name, "cat"))
893 			{
894 				homebank_load_xml_cat(ctx, attribute_names, attribute_values);
895 			}
896 			else if(!strcmp (element_name, "cur"))
897 			{
898 				homebank_load_xml_cur(ctx, attribute_names, attribute_values);
899 			}
900 		}
901 		break;
902 
903 		//TODO: < 5.2 misstyped here, should be tag without a s
904 		//commented > 5.2 useless not loaded, but no side effect
905 		/*case 't':
906 		{
907 			if(!strcmp (element_name, "tags"))
908 			{
909 				homebank_load_xml_tag(ctx, attribute_names, attribute_values);
910 			}
911 		}
912 		break;*/
913 
914 		case 'f':
915 		{
916 			if(!strcmp (element_name, "fav"))
917 			{
918 				homebank_load_xml_fav(ctx, attribute_names, attribute_values);
919 			}
920 		}
921 		break;
922 
923 		case 'o':
924 		{
925 			if(!strcmp (element_name, "ope"))
926 			{
927 				homebank_load_xml_ope(ctx, attribute_names, attribute_values);
928 			}
929 		}
930 		break;
931 	}
932 }
933 
934 
935 /*
936 static void
937 end_element_handler (GMarkupParseContext *context,
938 		     const gchar         *element_name,
939 		     gpointer             user_data,
940 		     GError             **error)
941 {
942   ParseContext *ctx = user_data;
943 
944 	//DB( g_print("-- end element: %s\n", element_name) );
945 
946 
947 }
948 */
949 
950 static GMarkupParser hb_parser = {
951 	start_element_handler,
952 	NULL,	//end_element_handler,
953 	NULL, //text_handler,
954 	NULL,
955 	NULL  //cleanup
956 };
957 
958 
hb_xml_get_version(ParseContext * ctx,gchar * buffer)959 static gboolean hb_xml_get_version(ParseContext *ctx, gchar *buffer)
960 {
961 gchar *v_buffer;
962 
963 	ctx->file_version = 0.0;
964 	ctx->data_version = 0;
965 
966 	/* v3.4 add :: prevent load of future file version */
967 	v_buffer = g_strstr_len(buffer, 50, "<homebank v=");
968 	if( v_buffer == NULL )
969 		return FALSE;
970 
971 	DB( g_print("- id line: --(%.50s)\n\n", v_buffer) );
972 
973 	ctx->file_version = g_ascii_strtod(v_buffer+13, NULL);	/* a little hacky, but works ! */
974 	if( ctx->file_version == 0.0 )
975 		ctx->file_version = 0.1;
976 	else if( ctx->file_version == 5.0 ) //was a mistake
977 		ctx->file_version = 1.0;
978 
979 	v_buffer = g_strstr_len(buffer+13, 50, "d=");
980 	if( v_buffer )
981 	{
982 		//TODO: beware here of we display all the file...
983 		DB( g_print(" d=%.25s)\n\n", v_buffer) );
984 
985 		ctx->data_version = atoi(v_buffer+3);
986 	}
987 	return TRUE;
988 }
989 
990 
991 /*
992 ** XML load homebank file: hbfile
993 */
homebank_load_xml(gchar * filename)994 gint homebank_load_xml(gchar *filename)
995 {
996 gint retval;
997 gchar *buffer;
998 gsize length;
999 GError *error = NULL;
1000 ParseContext ctx;
1001 GMarkupParseContext *context;
1002 gboolean rc, dosanity;
1003 
1004 	DB( g_print("\n[hb-xml] homebank_load_xml\n") );
1005 
1006 	retval = XML_OK;
1007 	if (!g_file_get_contents (filename, &buffer, &length, &error))
1008 	{
1009 		if(error)
1010 		{
1011 			g_warning("unable to load file %s: %s", filename, error->message);
1012 			g_error_free(error);
1013 			retval = XML_IO_ERROR;
1014 		}
1015 	}
1016 	else
1017 	{
1018 		if( hb_xml_get_version(&ctx, buffer) == FALSE )
1019 			return XML_FILE_ERROR;
1020 
1021 		if( ctx.file_version > FILE_VERSION )
1022 			return XML_VERSION_ERROR;
1023 
1024 
1025 		DB( g_print("- file ok : v=%.1f data_v=%06d\n", ctx.file_version, ctx.data_version) );
1026 
1027 		/* 1st: validate the file is well in utf-8 */
1028 		DB( g_print("- ensure UTF-8\n") );
1029 		buffer = homebank_utf8_ensure(buffer);
1030 
1031 		/* then process the buffer */
1032 		#if MYDEBUG == 1
1033 			GTimer *t = g_timer_new();
1034 			g_print("- start parse\n");
1035 		#endif
1036 
1037 		context = g_markup_parse_context_new (&hb_parser, 0, &ctx, NULL);
1038 
1039 		error = NULL;
1040 		rc = g_markup_parse_context_parse (context, buffer, length, &error);
1041 
1042 		if( error )
1043 		{
1044 			g_print("failed: %s\n", error->message);
1045 			g_error_free (error);
1046 		}
1047 
1048 
1049 		if( rc == FALSE )
1050 		{
1051 			error = NULL;
1052 			g_markup_parse_context_end_parse(context, &error);
1053 
1054 			if( error )
1055 			{
1056 				g_print("failed: %s\n", error->message);
1057 				g_error_free (error);
1058 			}
1059 		}
1060 
1061 		g_markup_parse_context_free (context);
1062 		g_free (buffer);
1063 
1064 		DB( g_print("- end parse : %f sec\n", g_timer_elapsed(t, NULL)) );
1065 		DB( g_timer_destroy (t) );
1066 
1067 		/* file upgrade / bugfix */
1068 		dosanity = FALSE;
1069 		// group a test for very old version
1070 		if( ctx.file_version <= 1.0 )
1071 		{
1072 			if( ctx.file_version <= 0.1 )
1073 				homebank_upgrade_to_v02();
1074 			if( ctx.file_version <= 0.2 )
1075 				homebank_upgrade_to_v03();
1076 			if( ctx.file_version <= 0.3 )
1077 				homebank_upgrade_to_v04();
1078 			if( ctx.file_version <= 0.4 )
1079 				homebank_upgrade_to_v05();
1080 			if( ctx.file_version <= 0.5 )
1081 			{
1082 				homebank_upgrade_to_v06();
1083 				homebank_upgrade_lower_v06();
1084 			}
1085 			if( ctx.file_version <= 0.6 )
1086 			{
1087 				homebank_upgrade_to_v07();
1088 				hbfile_sanity_check();
1089 			}
1090 			if( ctx.file_version <= 0.7 )	// <= 4.5
1091 			{
1092 				homebank_upgrade_to_v08();
1093 			}
1094 			if( ctx.file_version <= 0.8 )	// <= 4.6
1095 			{
1096 				dosanity = TRUE;
1097 			}
1098 			if( ctx.file_version <= 0.9 )	// <= 4.6.3 - 2014-08-09
1099 			{
1100 				homebank_upgrade_to_v10();
1101 				dosanity = TRUE;
1102 			}
1103 			if( ctx.file_version <= 1.0 )	// <= 5.0.0
1104 			{
1105 				homebank_upgrade_to_v11();
1106 				dosanity = TRUE;
1107 			}
1108 		}
1109 
1110 		//starting 5.0.4 data upgrade is done without changing file_version
1111 		//file version is changed only when the structure change
1112 		//don't start number below with 0 to avoid octal interpretation
1113 		if( ctx.data_version <= 50005 )	// <= 5.0.5
1114 		{
1115 			dosanity = TRUE;
1116 		}
1117 		if( ctx.file_version <= 1.1 )	// <= 5.1.0
1118 		{
1119 			homebank_upgrade_to_v12();
1120 			dosanity = TRUE;
1121 		}
1122 		if( ctx.data_version <= 50106 )	// < 5.1.6
1123 		{
1124 			homebank_upgrade_to_v12_7();
1125 		}
1126 		if( ctx.file_version < 1.3 )	// <= 5.2
1127 		{
1128 			homebank_upgrade_to_v13();
1129 			dosanity = TRUE;
1130 		}
1131 		if( ctx.data_version <= 50203 )
1132 		{
1133 			//fix payee defaut payment to int xfer from 5.1
1134 			dosanity = TRUE;
1135 		}
1136 		if( ctx.file_version < 1.4 )	// <= 5.3
1137 		{
1138 			homebank_upgrade_to_v14();
1139 			dosanity = TRUE;
1140 		}
1141 		if( ctx.data_version < 50402 )
1142 		{
1143 			//fix income txn flag that may be incorrect (multiple edit)
1144 			dosanity = TRUE;
1145 		}
1146 		// next ?
1147 
1148 
1149 		// sanity check at last
1150 		if( dosanity == TRUE )
1151 			hbfile_sanity_check();
1152 
1153 	}
1154 
1155 	return retval;
1156 }
1157 
1158 
1159 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
1160 
1161 /*
1162 ** misc xml attributes methods
1163 */
hb_xml_append_txt(GString * gstring,gchar * attrname,gchar * value)1164 static void hb_xml_append_txt(GString *gstring, gchar *attrname, gchar *value)
1165 {
1166 	if(value != NULL && *value != 0)
1167 	{
1168 		gchar *escaped = g_markup_escape_text(value, -1);
1169 		g_string_append_printf(gstring, " %s=\"%s\"", attrname, escaped);
1170 		g_free(escaped);
1171 	}
1172 }
1173 
1174 static void
append_escaped_text(GString * str,const gchar * text,gssize length)1175 append_escaped_text (GString     *str,
1176                      const gchar *text,
1177                      gssize       length)
1178 {
1179   const gchar *p;
1180   const gchar *end;
1181   gunichar c;
1182 
1183   p = text;
1184   end = text + length;
1185 
1186   while (p < end)
1187     {
1188       const gchar *next;
1189       next = g_utf8_next_char (p);
1190 
1191       switch (*p)
1192         {
1193         case '&':
1194           g_string_append (str, "&amp;");
1195           break;
1196 
1197         case '<':
1198           g_string_append (str, "&lt;");
1199           break;
1200 
1201         case '>':
1202           g_string_append (str, "&gt;");
1203           break;
1204 
1205         case '\'':
1206           g_string_append (str, "&apos;");
1207           break;
1208 
1209         case '"':
1210           g_string_append (str, "&quot;");
1211           break;
1212 
1213         default:
1214           c = g_utf8_get_char (p);
1215           if ((0x1 <= c && c <= 0x8) ||
1216               (0xa <= c && c  <= 0xd) ||	//chnaged here from b<->c to a<->d
1217               (0xe <= c && c <= 0x1f) ||
1218               (0x7f <= c && c <= 0x84) ||
1219               (0x86 <= c && c <= 0x9f))
1220             g_string_append_printf (str, "&#x%x;", c);
1221           else
1222             g_string_append_len (str, p, next - p);
1223           break;
1224         }
1225 
1226       p = next;
1227     }
1228 }
1229 
1230 // we override g_markup_escape_text from glib to encode \n (LF) & \r (CR)
hb_xml_append_txt_crlf(GString * gstring,gchar * attrname,gchar * value)1231 static void hb_xml_append_txt_crlf(GString *gstring, gchar *attrname, gchar *value)
1232 {
1233 	if(value != NULL && *value != 0)
1234 	{
1235 	gssize length;
1236 	GString *escaped;
1237 
1238 		//gchar *escaped = g_markup_escape_text(value, -1);
1239 		length = strlen (value);
1240 		escaped = g_string_sized_new (length);
1241 		append_escaped_text (escaped, value, length);
1242 		g_string_append_printf(gstring, " %s=\"%s\"", attrname, escaped->str);
1243 		g_string_free (escaped, TRUE);
1244 	}
1245 }
1246 
hb_xml_append_int0(GString * gstring,gchar * attrname,guint32 value)1247 static void hb_xml_append_int0(GString *gstring, gchar *attrname, guint32 value)
1248 {
1249 	g_string_append_printf(gstring, " %s=\"%d\"", attrname, value);
1250 }
1251 
hb_xml_append_int(GString * gstring,gchar * attrname,guint32 value)1252 static void hb_xml_append_int(GString *gstring, gchar *attrname, guint32 value)
1253 {
1254 	if(value != 0)
1255 	{
1256 		hb_xml_append_int0(gstring, attrname, value);
1257 	}
1258 }
1259 
hb_xml_append_amt(GString * gstring,gchar * attrname,gdouble amount)1260 static void hb_xml_append_amt(GString *gstring, gchar *attrname, gdouble amount)
1261 {
1262 char buf[G_ASCII_DTOSTR_BUF_SIZE];
1263 
1264 	//we must use this, as fprintf use locale decimal settings and not '.'
1265 	g_ascii_dtostr (buf, sizeof (buf), amount);
1266 	g_string_append_printf(gstring, " %s=\"%s\"", attrname, buf);
1267 }
1268 
1269 
1270 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
1271 
1272 
1273 /*
1274 ** XML properties save
1275 */
homebank_save_xml_prop(GIOChannel * io)1276 static gint homebank_save_xml_prop(GIOChannel *io)
1277 {
1278 gchar *title;
1279 GString *node;
1280 gint retval = XML_OK;
1281 GError *error = NULL;
1282 
1283 	title = GLOBALS->owner == NULL ? "" : GLOBALS->owner;
1284 
1285 	node = g_string_sized_new(255);
1286 
1287 	g_string_assign(node, "<properties");
1288 
1289 	hb_xml_append_txt(node, "title", title);
1290 	hb_xml_append_int(node, "curr", GLOBALS->kcur);
1291 	hb_xml_append_int(node, "car_category", GLOBALS->vehicle_category);
1292 	hb_xml_append_int0(node, "auto_smode", GLOBALS->auto_smode);
1293 	hb_xml_append_int(node, "auto_weekday", GLOBALS->auto_weekday);
1294 	hb_xml_append_int(node, "auto_nbdays", GLOBALS->auto_nbdays);
1295 
1296 	g_string_append(node, "/>\n");
1297 
1298 	error = NULL;
1299 	g_io_channel_write_chars(io, node->str, -1, NULL, &error);
1300 	if(error)
1301 	{
1302 		retval = XML_IO_ERROR;
1303 		g_error_free(error);
1304 	}
1305 
1306 	g_string_free(node, TRUE);
1307 	return retval;
1308 }
1309 
1310 
1311 /*
1312 ** XML currency save
1313 */
homebank_save_xml_cur(GIOChannel * io)1314 static gint homebank_save_xml_cur(GIOChannel *io)
1315 {
1316 GList *list;
1317 gchar *tmpstr;
1318 char buf1[G_ASCII_DTOSTR_BUF_SIZE];
1319 gint retval = XML_OK;
1320 
1321 	list = g_hash_table_get_values(GLOBALS->h_cur);
1322 	while (list != NULL)
1323 	{
1324 	Currency *item = list->data;
1325 
1326 		tmpstr = g_markup_printf_escaped(
1327 		    "<cur key=\"%d\" flags=\"%d\" iso=\"%s\" name=\"%s\" symb=\"%s\" syprf=\"%d\" dchar=\"%s\" gchar=\"%s\" frac=\"%d\" rate=\"%s\" mdate=\"%d\"/>\n",
1328 			item->key,
1329 			item->flags,
1330 			item->iso_code,
1331 		    item->name,
1332 		    item->symbol,
1333 		    item->sym_prefix,
1334 		    item->decimal_char,
1335 		    item->grouping_char,
1336 		    item->frac_digits,
1337 		    g_ascii_dtostr (buf1, sizeof (buf1), item->rate),
1338 		    item->mdate
1339 		);
1340 
1341 		g_io_channel_write_chars(io, tmpstr, -1, NULL, NULL);
1342 		g_free(tmpstr);
1343 
1344 		list = g_list_next(list);
1345 	}
1346 	g_list_free(list);
1347 	return retval;
1348 }
1349 
1350 
1351 /*
1352 ** XML account save
1353 */
homebank_save_xml_acc(GIOChannel * io)1354 static gint homebank_save_xml_acc(GIOChannel *io)
1355 {
1356 GList *lacc, *list;
1357 GString *node;
1358 gint retval = XML_OK;
1359 GError *error = NULL;
1360 
1361 	node = g_string_sized_new(255);
1362 
1363 	lacc = list = account_glist_sorted(0);
1364 	while (list != NULL)
1365 	{
1366 	Account *item = list->data;
1367 
1368 		item->flags &= ~(AF_ADDED|AF_CHANGED);	//delete flag
1369 
1370 		g_string_assign(node, "<account");
1371 
1372 		hb_xml_append_int(node, "key", item->key);
1373 		hb_xml_append_int(node, "flags", item->flags);
1374 		hb_xml_append_int(node, "pos", item->pos);
1375 		hb_xml_append_int(node, "type", item->type);
1376 		hb_xml_append_int(node, "curr", item->kcur);
1377 		hb_xml_append_txt(node, "name", item->name);
1378 		hb_xml_append_txt(node, "number", item->number);
1379 		hb_xml_append_txt(node, "bankname", item->bankname);
1380 		hb_xml_append_amt(node, "initial", item->initial);
1381 
1382 		hb_xml_append_amt(node, "minimum", item->minimum);
1383 		hb_xml_append_amt(node, "maximum", item->maximum);
1384 		hb_xml_append_int(node, "cheque1", item->cheque1);
1385 		hb_xml_append_int(node, "cheque2", item->cheque2);
1386 		hb_xml_append_txt_crlf(node, "notes", item->notes);
1387 		hb_xml_append_int(node, "tpl", item->karc);
1388 		hb_xml_append_int(node, "grp", item->kgrp);
1389 		//5.5
1390 		hb_xml_append_int(node, "ccday", item->cccday);
1391 		hb_xml_append_int(node, "rdate", item->rdate);
1392 
1393 		g_string_append(node, "/>\n");
1394 
1395 		error = NULL;
1396 		g_io_channel_write_chars(io, node->str, -1, NULL, &error);
1397 
1398 		if(error)
1399 		{
1400 			retval = XML_IO_ERROR;
1401 			g_error_free(error);
1402 		}
1403 
1404 		list = g_list_next(list);
1405 	}
1406 	g_list_free(lacc);
1407 	g_string_free(node, TRUE);
1408 	return retval;
1409 }
1410 
1411 /*
1412 ** XML payee save
1413 */
homebank_save_xml_pay(GIOChannel * io)1414 static gint homebank_save_xml_pay(GIOChannel *io)
1415 {
1416 GList *lpay, *list;
1417 GString *node;
1418 gint retval = XML_OK;
1419 GError *error = NULL;
1420 
1421 	node = g_string_sized_new(255);
1422 
1423 	lpay = list = payee_glist_sorted(0);
1424 	while (list != NULL)
1425 	{
1426 	Payee *item = list->data;
1427 
1428 		if(item->key != 0)
1429 		{
1430 			g_string_assign(node, "<pay");
1431 
1432 			hb_xml_append_int(node, "key", item->key);
1433 			hb_xml_append_txt(node, "name", item->name);
1434 			hb_xml_append_int(node, "category", item->kcat);
1435 			hb_xml_append_int(node, "paymode" , item->paymode);
1436 
1437 			g_string_append(node, "/>\n");
1438 
1439 			error = NULL;
1440 			g_io_channel_write_chars(io, node->str, -1, NULL, &error);
1441 
1442 			if(error)
1443 			{
1444 				retval = XML_IO_ERROR;
1445 				g_error_free(error);
1446 			}
1447 
1448 		}
1449 		list = g_list_next(list);
1450 	}
1451 	g_list_free(lpay);
1452 	g_string_free(node, TRUE);
1453 	return retval;
1454 }
1455 
1456 /*
1457 ** XML category save
1458 */
homebank_save_xml_cat(GIOChannel * io)1459 static gint homebank_save_xml_cat(GIOChannel *io)
1460 {
1461 GList *lcat, *list;
1462 GString *node;
1463 char buf[G_ASCII_DTOSTR_BUF_SIZE];
1464 guint i;
1465 gint retval = XML_OK;
1466 GError *error = NULL;
1467 
1468 	node = g_string_sized_new(255);
1469 
1470 	lcat = list = category_glist_sorted(0);
1471 	while (list != NULL)
1472 	{
1473 	Category *item = list->data;
1474 
1475 		if(item->key != 0)
1476 		{
1477 			g_string_assign(node, "<cat");
1478 
1479 			hb_xml_append_int(node, "key", item->key);
1480 			hb_xml_append_int(node, "parent", item->parent);
1481 			hb_xml_append_int(node, "flags", item->flags);
1482 			hb_xml_append_txt(node, "name", item->name);
1483 
1484 			for(i=0;i<=12;i++)
1485 			{
1486 				if(item->budget[i] != 0)
1487 				{
1488 					g_string_append_printf(node," b%d=\"%s\"", i, g_ascii_dtostr (buf, sizeof (buf), item->budget[i]));
1489 				}
1490 			}
1491 
1492 			g_string_append(node, "/>\n");
1493 
1494 			error = NULL;
1495 			g_io_channel_write_chars(io, node->str, -1, NULL, &error);
1496 
1497 			if(error)
1498 			{
1499 				retval = XML_IO_ERROR;
1500 				g_error_free(error);
1501 			}
1502 
1503 		}
1504 		list = g_list_next(list);
1505 	}
1506 	g_list_free(lcat);
1507 	g_string_free(node, TRUE);
1508 	return retval;
1509 }
1510 
1511 
1512 /*
1513 ** XML grp save
1514 */
homebank_save_xml_grp(GIOChannel * io)1515 static gint homebank_save_xml_grp(GIOChannel *io)
1516 {
1517 GList *lgrp, *list;
1518 gchar *tmpstr;
1519 gint retval = XML_OK;
1520 GError *error = NULL;
1521 
1522 	lgrp = list = group_glist_sorted(0);
1523 	while (list != NULL)
1524 	{
1525 	Group *item = list->data;
1526 
1527 		if(item->key != 0)
1528 		{
1529 			//tmpstr = g_markup_printf_escaped("<grp key=\"%d\" type=\"%d\" name=\"%s\"/>\n",
1530 			tmpstr = g_markup_printf_escaped("<grp key=\"%d\" name=\"%s\"/>\n",
1531 				item->key,
1532 			    //item->type,
1533 				item->name
1534 			);
1535 
1536 			error = NULL;
1537 			g_io_channel_write_chars(io, tmpstr, -1, NULL, &error);
1538 			g_free(tmpstr);
1539 
1540 			if(error)
1541 			{
1542 				retval = XML_IO_ERROR;
1543 				g_error_free(error);
1544 			}
1545 		}
1546 		list = g_list_next(list);
1547 	}
1548 	g_list_free(lgrp);
1549 	return retval;
1550 }
1551 
1552 
1553 /*
1554 ** XML tag save
1555 */
1556 /*static gint homebank_save_xml_tag(GIOChannel *io)
1557 {
1558 GList *ltag, *list;
1559 gchar *tmpstr;
1560 gint retval = XML_OK;
1561 GError *error = NULL;
1562 
1563 	ltag = list = tag_glist_sorted(0);
1564 	while (list != NULL)
1565 	{
1566 	Tag *item = list->data;
1567 
1568 		if(item->key != 0)
1569 		{
1570 			tmpstr = g_markup_printf_escaped("<tag key=\"%d\" name=\"%s\"/>\n",
1571 				item->key,
1572 				item->name
1573 			);
1574 
1575 			error = NULL;
1576 			g_io_channel_write_chars(io, tmpstr, -1, NULL, &error);
1577 			g_free(tmpstr);
1578 
1579 			if(error)
1580 			{
1581 				retval = XML_IO_ERROR;
1582 				g_error_free(error);
1583 			}
1584 		}
1585 		list = g_list_next(list);
1586 	}
1587 	g_list_free(ltag);
1588 	return retval;
1589 }*/
1590 
1591 
1592 /*
1593 ** XML assign save
1594 */
homebank_save_xml_asg(GIOChannel * io)1595 static gint homebank_save_xml_asg(GIOChannel *io)
1596 {
1597 GList *lasg, *list;
1598 GString *node;
1599 gint retval = XML_OK;
1600 GError *error = NULL;
1601 
1602 	node = g_string_sized_new(255);
1603 
1604 	lasg = list = assign_glist_sorted(HB_GLIST_SORT_KEY);
1605 	while (list != NULL)
1606 	{
1607 	Assign *item = list->data;
1608 
1609 		g_string_assign(node, "<asg");
1610 
1611 		hb_xml_append_int(node, "key"     , item->key);
1612 		hb_xml_append_int(node, "flags"   , item->flags);
1613 		hb_xml_append_int(node, "pos"     , item->pos);
1614 		hb_xml_append_int(node, "field"   , item->field);
1615 		hb_xml_append_txt(node, "name"    , item->search);
1616 		hb_xml_append_txt(node, "notes"   , item->notes);
1617 		hb_xml_append_int(node, "payee"   , item->kpay);
1618 		hb_xml_append_int(node, "category", item->kcat);
1619 		hb_xml_append_int(node, "paymode" , item->paymode);
1620 
1621 		g_string_append(node, "/>\n");
1622 
1623 		error = NULL;
1624 		g_io_channel_write_chars(io, node->str, -1, NULL, &error);
1625 
1626 		if(error)
1627 		{
1628 			retval = XML_IO_ERROR;
1629 			g_error_free(error);
1630 		}
1631 
1632 		list = g_list_next(list);
1633 	}
1634 	g_list_free(lasg);
1635 	g_string_free(node, TRUE);
1636 	return retval;
1637 }
1638 
1639 
1640 
1641 /*
1642 ** XML archive save
1643 */
homebank_save_xml_arc(GIOChannel * io)1644 static gint homebank_save_xml_arc(GIOChannel *io)
1645 {
1646 GList *list;
1647 GString *node;
1648 gchar *tagstr;
1649 gint retval = XML_OK;
1650 GError *error = NULL;
1651 
1652 	node = g_string_sized_new(255);
1653 
1654 	list = da_archive_glist_sorted(0);
1655 	while (list != NULL)
1656 	{
1657 	Archive *item = list->data;
1658 
1659 		tagstr = tags_tostring(item->tags);
1660 
1661 		g_string_assign(node, "<fav");
1662 
1663 		hb_xml_append_int(node, "key", item->key);
1664 		hb_xml_append_amt(node, "amount", item->amount);
1665 		hb_xml_append_int(node, "account", item->kacc);
1666 		hb_xml_append_int(node, "dst_account", item->kxferacc);
1667 		hb_xml_append_int(node, "paymode", item->paymode);
1668 		hb_xml_append_int(node, "st", item->status);
1669 		hb_xml_append_int(node, "flags", item->flags);
1670 		hb_xml_append_int(node, "payee", item->kpay);
1671 		hb_xml_append_int(node, "category", item->kcat);
1672 		hb_xml_append_txt(node, "wording", item->memo);
1673 		hb_xml_append_txt(node, "info", item->info);
1674 		hb_xml_append_txt(node, "tags", tagstr);
1675 		hb_xml_append_int(node, "nextdate", item->nextdate);
1676 		hb_xml_append_int(node, "every", item->every);
1677 		hb_xml_append_int(node, "unit", item->unit);
1678 		hb_xml_append_int(node, "limit", item->limit);
1679 		hb_xml_append_int(node, "weekend", item->weekend);
1680 		hb_xml_append_int(node, "gap", item->daygap);
1681 
1682 		if(da_splits_length(item->splits) > 0)
1683 		{
1684 		gchar *cats, *amounts, *memos;
1685 
1686 			da_splits_tostring(item->splits, &cats, &amounts, &memos);
1687 			g_string_append_printf(node, " scat=\"%s\"", cats);
1688 			g_string_append_printf(node, " samt=\"%s\"", amounts);
1689 
1690 			//fix #1173910
1691 			gchar *escaped = g_markup_escape_text(memos, -1);
1692 			g_string_append_printf(node, " smem=\"%s\"", escaped);
1693 			g_free(escaped);
1694 
1695 			g_free(cats);
1696 			g_free(amounts);
1697 			g_free(memos);
1698 		}
1699 
1700 		g_string_append(node, "/>\n");
1701 
1702 		g_free(tagstr);
1703 
1704 		error = NULL;
1705 		g_io_channel_write_chars(io, node->str, -1, NULL, &error);
1706 		if(error)
1707 		{
1708 			retval = XML_IO_ERROR;
1709 			g_error_free(error);
1710 		}
1711 
1712 		list = g_list_next(list);
1713 	}
1714 	g_string_free(node, TRUE);
1715 	return retval;
1716 }
1717 
1718 
1719 /*
1720 ** XML transaction save
1721 */
homebank_save_xml_ope(GIOChannel * io)1722 static gint homebank_save_xml_ope(GIOChannel *io)
1723 {
1724 GList *lst_acc, *lnk_acc;
1725 GList *list;
1726 GString *node;
1727 gchar *tagstr;
1728 gint retval = XML_OK;
1729 GError *error = NULL;
1730 
1731 	node = g_string_sized_new(255);
1732 
1733 	lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
1734 	lnk_acc = g_list_first(lst_acc);
1735 	while (lnk_acc != NULL)
1736 	{
1737 	Account *acc = lnk_acc->data;
1738 
1739 		list = g_queue_peek_head_link(acc->txn_queue);
1740 		while (list != NULL)
1741 		{
1742 		Transaction *item = list->data;
1743 
1744 			item->flags &= ~(OF_AUTO|OF_ADDED|OF_CHANGED);	//delete flag
1745 			tagstr = tags_tostring(item->tags);
1746 
1747 			g_string_assign(node, "<ope");
1748 
1749 			hb_xml_append_int(node, "date", item->date);
1750 			hb_xml_append_amt(node, "amount", item->amount);
1751 			hb_xml_append_int(node, "account", item->kacc);
1752 			hb_xml_append_int(node, "dst_account", item->kxferacc);
1753 			hb_xml_append_int(node, "paymode", item->paymode);
1754 			hb_xml_append_int(node, "st", item->status);
1755 			hb_xml_append_int(node, "flags", item->flags);
1756 			hb_xml_append_int(node, "payee", item->kpay);
1757 			hb_xml_append_int(node, "category", item->kcat);
1758 			hb_xml_append_txt(node, "wording", item->memo);
1759 			hb_xml_append_txt(node, "info", item->info);
1760 			hb_xml_append_txt(node, "tags", tagstr);
1761 			hb_xml_append_int(node, "kxfer", item->kxfer);
1762 
1763 			if(da_splits_length(item->splits) > 0)
1764 			{
1765 			gchar *cats, *amounts, *memos;
1766 
1767 				da_splits_tostring(item->splits, &cats, &amounts, &memos);
1768 				g_string_append_printf(node, " scat=\"%s\"", cats);
1769 				g_string_append_printf(node, " samt=\"%s\"", amounts);
1770 
1771 				//fix #1173910
1772 				gchar *escaped = g_markup_escape_text(memos, -1);
1773 				g_string_append_printf(node, " smem=\"%s\"", escaped);
1774 				g_free(escaped);
1775 
1776 				g_free(cats);
1777 				g_free(amounts);
1778 				g_free(memos);
1779 			}
1780 
1781 			g_string_append(node, "/>\n");
1782 
1783 			g_free(tagstr);
1784 
1785 			error = NULL;
1786 			g_io_channel_write_chars(io, node->str, -1, NULL, &error);
1787 
1788 			if(error)
1789 			{
1790 				retval = XML_IO_ERROR;
1791 				g_error_free(error);
1792 			}
1793 
1794 			list = g_list_next(list);
1795 		}
1796 
1797 		lnk_acc = g_list_next(lnk_acc);
1798 	}
1799 	g_list_free(lst_acc);
1800 
1801 	g_string_free(node, TRUE);
1802 	return retval;
1803 }
1804 
1805 /*
1806 ** XML save homebank file: hbfile
1807 */
homebank_save_xml(gchar * filename)1808 gint homebank_save_xml(gchar *filename)
1809 {
1810 GIOChannel *io;
1811 char buf1[G_ASCII_DTOSTR_BUF_SIZE];
1812 gchar *outstr;
1813 gint retval = XML_OK;
1814 GError *error = NULL;
1815 
1816 	io = g_io_channel_new_file(filename, "w", &error);
1817 	//The default encoding for the external file is UTF-8.
1818 	if(error)
1819 	{
1820 		g_warning("unable to save file %s: %s", filename, error->message);
1821 		g_error_free(error);
1822 		return(XML_IO_ERROR);
1823 	}
1824 
1825 	g_io_channel_write_chars(io, "<?xml version=\"1.0\"?>\n", -1, NULL, NULL);
1826 
1827 	outstr = g_strdup_printf("<homebank v=\"%s\" d=\"%06d\">\n", g_ascii_dtostr (buf1, sizeof (buf1), FILE_VERSION), HB_VERSION_NUM);
1828 	g_io_channel_write_chars(io, outstr, -1, NULL, NULL);
1829 	g_free(outstr);
1830 
1831 	retval = homebank_save_xml_prop(io);
1832 	retval = homebank_save_xml_cur(io);
1833 	retval = homebank_save_xml_grp(io);
1834 	retval = homebank_save_xml_acc(io);
1835 	retval = homebank_save_xml_pay(io);
1836 	retval = homebank_save_xml_cat(io);
1837 	//retval = homebank_save_xml_tag(io);
1838 	retval = homebank_save_xml_asg(io);
1839 	retval = homebank_save_xml_arc(io);
1840 	retval = homebank_save_xml_ope(io);
1841 
1842 	g_io_channel_write_chars(io, "</homebank>\n", -1, NULL, NULL);
1843 
1844 	g_io_channel_unref (io);
1845 
1846 	return retval;
1847 }
1848 
1849