/* HomeBank -- Free, easy, personal accounting for everyone. * Copyright (C) 1995-2019 Maxime DOYEN * * This file is part of HomeBank. * * HomeBank is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * HomeBank is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "homebank.h" #include "hb-transaction.h" #include "hb-xml.h" #include "ui-dialogs.h" /****************************************************************************/ /* Debug macros */ /****************************************************************************/ #define MYDEBUG 0 #if MYDEBUG #define DB(x) (x); #else #define DB(x); #endif /* our global datas */ extern struct HomeBank *GLOBALS; extern struct Preferences *PREFS; /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */ // v0.1 to v0.2 : we must change account reference by making a +1 to its index references static void homebank_upgrade_to_v02(void) { GList *lst_acc, *lnk_acc; GList *list; GHashTable *h_old_acc; DB( g_print("\n[hb-xml] homebank_upgrade_to_v02\n") ); //keep old hashtable with us h_old_acc = GLOBALS->h_acc; da_acc_new(); lst_acc = g_hash_table_get_values(h_old_acc); lnk_acc = g_list_first(lst_acc); while (lnk_acc != NULL) { Account *acc = lnk_acc->data; acc->key++; acc->pos++; da_acc_insert (acc); list = g_queue_peek_head_link(acc->txn_queue); while (list != NULL) { Transaction *entry = list->data; entry->kacc++; entry->kxferacc++; list = g_list_next(list); } lnk_acc = g_list_next(lnk_acc); } g_list_free(lst_acc); //we loose some small memory here g_hash_table_steal_all(h_old_acc); list = g_list_first(GLOBALS->arc_list); while (list != NULL) { Archive *entry = list->data; entry->kacc++; entry->kxferacc++; list = g_list_next(list); } } // v0.2 to v0.3 : we must assume categories exists : bugs 303886, 303738 static void homebank_upgrade_to_v03(void) { GList *lst_acc, *lnk_acc; GList *list; DB( g_print("\n[hb-xml] homebank_upgrade_to_v03\n") ); lst_acc = g_hash_table_get_values(GLOBALS->h_acc); lnk_acc = g_list_first(lst_acc); while (lnk_acc != NULL) { Account *acc = lnk_acc->data; list = g_queue_peek_head_link(acc->txn_queue); while (list != NULL) { Transaction *entry = list->data; da_transaction_consistency(entry); list = g_list_next(list); } lnk_acc = g_list_next(lnk_acc); } g_list_free(lst_acc); list = g_list_first(GLOBALS->arc_list); while (list != NULL) { Archive *entry = list->data; da_archive_consistency(entry); list = g_list_next(list); } } static void homebank_upgrade_to_v04(void) { DB( g_print("\n[hb-xml] homebank_upgrade_to_v04\n") ); GLOBALS->arc_list = da_archive_sort(GLOBALS->arc_list); } // v0.4 to v0.5 : // we must assume kxferacc exists in archives for internal xfer : bug 528923 // if not, delete automation from the archive static void homebank_upgrade_to_v05(void) { GList *list; DB( g_print("\n[hb-xml] homebank_upgrade_to_v05\n") ); list = g_list_first(GLOBALS->arc_list); while (list != NULL) { Archive *entry = list->data; da_archive_consistency(entry); list = g_list_next(list); } } // v0.5 to v0.6 : we must change kxferacc to 0 on non Xfer transactions //#677351 static void homebank_upgrade_to_v06(void) { GList *lst_acc, *lnk_acc; GList *list; DB( g_print("\n[hb-xml] homebank_upgrade_to_v06\n") ); lst_acc = g_hash_table_get_values(GLOBALS->h_acc); lnk_acc = g_list_first(lst_acc); while (lnk_acc != NULL) { Account *acc = lnk_acc->data; list = g_queue_peek_head_link(acc->txn_queue); while (list != NULL) { Transaction *entry = list->data; da_transaction_consistency(entry); list = g_list_next(list); } lnk_acc = g_list_next(lnk_acc); } g_list_free(lst_acc); list = g_list_first(GLOBALS->arc_list); while (list != NULL) { Archive *entry = list->data; da_archive_consistency(entry); list = g_list_next(list); } } // v0.7 AF_BUDGET deleted instead of AF_NOBUDGET static void homebank_upgrade_to_v07(void) { GList *lacc, *list; DB( g_print("\n[hb-xml] homebank_upgrade_to_v07\n") ); lacc = list = g_hash_table_get_values(GLOBALS->h_acc); while (list != NULL) { Account *acc = list->data; if( acc->flags & AF_OLDBUDGET ) // budget include { acc->flags &= ~(AF_OLDBUDGET); } else { acc->flags |= AF_NOBUDGET; } list = g_list_next(list); } g_list_free(lacc); } static void homebank_upgrade_to_v08(void) { GList *lst_acc, *lnk_acc; GList *list; DB( g_print("\n[hb-xml] homebank_upgrade_to_v08\n") ); lst_acc = g_hash_table_get_values(GLOBALS->h_acc); lnk_acc = g_list_first(lst_acc); while (lnk_acc != NULL) { Account *acc = lnk_acc->data; list = g_queue_peek_head_link(acc->txn_queue); while (list != NULL) { Transaction *entry = list->data; da_transaction_consistency(entry); list = g_list_next(list); } lnk_acc = g_list_next(lnk_acc); } g_list_free(lst_acc); } static void homebank_upgrade_to_v10(void) { GList *lst_acc, *lnk_acc; GList *list; DB( g_print("\n[hb-xml] homebank_upgrade_to_v10\n") ); lst_acc = g_hash_table_get_values(GLOBALS->h_acc); lnk_acc = g_list_first(lst_acc); while (lnk_acc != NULL) { Account *acc = lnk_acc->data; list = g_queue_peek_head_link(acc->txn_queue); while (list != NULL) { Transaction *entry = list->data; entry->status = TXN_STATUS_NONE; if(entry->flags & OF_OLDVALID) entry->status = TXN_STATUS_RECONCILED; else if(entry->flags & OF_OLDREMIND) entry->status = TXN_STATUS_REMIND; //remove those flags entry->flags &= ~(OF_OLDVALID|OF_OLDREMIND); list = g_list_next(list); } lnk_acc = g_list_next(lnk_acc); } g_list_free(lst_acc); } static void homebank_upgrade_to_v11(void) { GList *list; DB( g_print("\n[hb-xml] homebank_upgrade_to_v11\n") ); list = g_list_first(GLOBALS->arc_list); while (list != NULL) { Archive *entry = list->data; entry->status = TXN_STATUS_NONE; if(entry->flags & OF_OLDVALID) entry->status = TXN_STATUS_RECONCILED; else if(entry->flags & OF_OLDREMIND) entry->status = TXN_STATUS_REMIND; //remove those flags entry->flags &= ~(OF_OLDVALID|OF_OLDREMIND); list = g_list_next(list); } } // v0.6 to v0.7 : assign a default currency static void homebank_upgrade_to_v12(void) { DB( g_print("\n[hb-xml] homebank_upgrade_to_v12\n") ); // set a base currency to the hbfile if not DB( g_print("GLOBALS->kcur %d\n", GLOBALS->kcur) ); ui_dialog_upgrade_choose_currency(); } static void homebank_upgrade_to_v12_7(void) { GList *lst_acc, *lnk_acc; DB( g_print("\n[hb-xml] homebank_upgrade_to_v12\n") ); //#1674045 exclude closed account from everywhere to //keep continuity for user that don't want to change this lst_acc = g_hash_table_get_values(GLOBALS->h_acc); lnk_acc = g_list_first(lst_acc); while (lnk_acc != NULL) { Account *acc = lnk_acc->data; if( acc->flags & AF_CLOSED ) { if( !(acc->flags & AF_NOSUMMARY) ) acc->flags |= AF_NOSUMMARY; if( !(acc->flags & AF_NOBUDGET) ) acc->flags |= AF_NOBUDGET; if( !(acc->flags & AF_NOREPORT) ) acc->flags |= AF_NOREPORT; } lnk_acc = g_list_next(lnk_acc); } g_list_free(lst_acc); } static void homebank_upgrade_to_v13(void) { GList *tmplist; guint32 newkey; DB( g_print("\n[hb-xml] homebank_upgrade_to_v13\n") ); //#1008629 assign a key to each archive newkey = 1; tmplist = g_list_first(GLOBALS->arc_list); while (tmplist != NULL) { Archive *item = tmplist->data; item->key = newkey++; tmplist = g_list_next(tmplist); } } // lower v0.6 : we must assume categories/payee exists // and strong link to xfer // #632496 static void homebank_upgrade_lower_v06(void) { GList *lst_acc, *lnk_acc; Category *cat; Payee *pay; GList *lrul, *list; DB( g_print("\n[hb-xml] homebank_upgrade_lower_v06\n") ); lst_acc = g_hash_table_get_values(GLOBALS->h_acc); lnk_acc = g_list_first(lst_acc); while (lnk_acc != NULL) { Account *acc = lnk_acc->data; list = g_queue_peek_head_link(acc->txn_queue); while (list != NULL) { Transaction *entry = list->data; //also strong link internal xfer if(entry->paymode == PAYMODE_INTXFER && entry->kxfer == 0) { Transaction *child = transaction_old_get_child_transfer(entry); if(child != NULL) { transaction_xfer_change_to_child(entry, child); } } da_transaction_consistency(entry); list = g_list_next(list); } lnk_acc = g_list_next(lnk_acc); } g_list_free(lst_acc); lrul = list = g_hash_table_get_values(GLOBALS->h_rul); while (list != NULL) { Assign *entry = list->data; cat = da_cat_get(entry->kcat); if(cat == NULL) { DB( g_print(" !! fixing cat for rul: %d is unknown\n", entry->kcat) ); entry->kcat = 0; } pay = da_pay_get(entry->kpay); if(pay == NULL) { DB( g_print(" !! fixing pay for rul: %d is unknown\n", entry->kpay) ); entry->kpay = 0; } list = g_list_next(list); } g_list_free(lrul); } /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */ static void homebank_load_xml_acc(ParseContext *ctx, const gchar **attribute_names, const gchar **attribute_values) { Account *entry = da_acc_malloc(); gint i; for (i = 0; attribute_names[i] != NULL; i++) { //DB( g_print(" att='%s' val='%s'\n", attribute_names[i], attribute_values[i]) ); if(!strcmp (attribute_names[i], "key" )) { entry->key = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "flags" )) { entry->flags = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "pos" )) { entry->pos = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "type" )) { entry->type = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "curr" )) { entry->kcur = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "name" )) { if(strcmp(attribute_values[i],"(null)") && attribute_values[i] != NULL) entry->name = g_strdup(attribute_values[i]); } else if(!strcmp (attribute_names[i], "number" )) { if(strcmp(attribute_values[i],"(null)") && attribute_values[i] != NULL) entry->number = g_strdup(attribute_values[i]); } else if(!strcmp (attribute_names[i], "bankname")) { if(strcmp(attribute_values[i],"(null)") && attribute_values[i] != NULL) entry->bankname = g_strdup(attribute_values[i]); } else if(!strcmp (attribute_names[i], "initial" )) { entry->initial = g_ascii_strtod(attribute_values[i], NULL); } else if(!strcmp (attribute_names[i], "minimum" )) { entry->minimum = g_ascii_strtod(attribute_values[i], NULL); } else if(!strcmp (attribute_names[i], "cheque1" )) { entry->cheque1 = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "cheque2" )) { entry->cheque2 = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "notes" )) { if(strcmp(attribute_values[i],"(null)") && attribute_values[i] != NULL) entry->notes = g_strdup(attribute_values[i]); } else if(!strcmp (attribute_names[i], "tpl" )) { entry->karc = atoi(attribute_values[i]); } } //all attribute loaded: append da_acc_insert(entry); } static void homebank_load_xml_asg(ParseContext *ctx, const gchar **attribute_names, const gchar **attribute_values) { Assign *entry = da_asg_malloc(); gint exact = 0; gint i; for (i = 0; attribute_names[i] != NULL; i++) { //DB( g_print(" att='%s' val='%s'\n", attribute_names[i], attribute_values[i]) ); if(!strcmp (attribute_names[i], "key" )) { entry->key = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "flags" )) { entry->flags = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "field" )) { entry->field = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "name" )) { if(strcmp(attribute_values[i],"(null)") && attribute_values[i] != NULL) entry->text = g_strdup(attribute_values[i]); } else if(!strcmp (attribute_names[i], "payee" )) { entry->kpay = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "category")) { entry->kcat = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "paymode" )) { entry->paymode = atoi(attribute_values[i]); } // prior v08 else if(!strcmp (attribute_names[i], "exact" )) { exact = atoi(attribute_values[i]); } } /* in v08 exact moved to flag */ if( ctx->file_version <= 0.7) { entry->flags = (ASGF_DOCAT|ASGF_DOPAY); if( exact > 0 ) entry->flags |= ASGF_EXACT; } //all attribute loaded: append da_asg_append(entry); } static void homebank_load_xml_pay(ParseContext *ctx, const gchar **attribute_names, const gchar **attribute_values) { Payee *entry = da_pay_malloc(); gint i; for (i = 0; attribute_names[i] != NULL; i++) { //DB( g_print(" att='%s' val='%s'\n", attribute_names[i], attribute_values[i]) ); if(!strcmp (attribute_names[i], "key" )) { entry->key = atoi(attribute_values[i]); } //else if(!strcmp (attribute_names[i], "flags")) { entry->flags = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "name" )) { entry->name = g_strdup(attribute_values[i]); } else if(!strcmp (attribute_names[i], "category")) { entry->kcat = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "paymode" )) { entry->paymode = atoi(attribute_values[i]); } } //all attribute loaded: append da_pay_insert(entry); } static void homebank_load_xml_prop(ParseContext *ctx, const gchar **attribute_names, const gchar **attribute_values) { gint i; for (i = 0; attribute_names[i] != NULL; i++) { if(!strcmp (attribute_names[i], "title" )) { g_free(GLOBALS->owner); GLOBALS->owner = g_strdup(attribute_values[i]); } else if(!strcmp (attribute_names[i], "curr" )) { GLOBALS->kcur = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "car_category")) { GLOBALS->vehicle_category = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "auto_smode" )) { GLOBALS->auto_smode = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "auto_weekday")) { GLOBALS->auto_weekday = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "auto_nbdays" )) { GLOBALS->auto_nbdays = atoi(attribute_values[i]); } } } static void homebank_load_xml_cat(ParseContext *ctx, const gchar **attribute_names, const gchar **attribute_values) { Category *entry = da_cat_malloc(); gboolean budget; gint i, j; for (i = 0; attribute_names[i] != NULL; i++) { //DB( g_print(" att='%s' val='%s'\n", attribute_names[i], attribute_values[i]) ); if(!strcmp (attribute_names[i], "key" )) { entry->key = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "parent")) { entry->parent = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "flags" )) { entry->flags = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "name" )) { entry->name = g_strdup(attribute_values[i]); } budget = FALSE; for(j=0;j<=12;j++) { gchar *tmpname; tmpname = g_strdup_printf ("b%d", j); if(!(strcmp (attribute_names[i], tmpname))) { entry->budget[j] = g_ascii_strtod(attribute_values[i], NULL); } g_free(tmpname); if(entry->budget[j]) budget = TRUE; } if(budget == TRUE) entry->flags |= GF_BUDGET; } //all attribute loaded: append da_cat_insert( entry); } static void homebank_load_xml_cur(ParseContext *ctx, const gchar **attribute_names, const gchar **attribute_values) { Currency *entry = da_cur_malloc (); gint i; for (i = 0; attribute_names[i] != NULL; i++) { //DB( g_print(" att='%s' val='%s'\n", attribute_names[i], attribute_values[i]) ); if(!strcmp (attribute_names[i], "key" )) { entry->key = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "flags" )) { entry->flags = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "name" )) { entry->name = g_strdup(attribute_values[i]); } else if(!strcmp (attribute_names[i], "iso" )) { entry->iso_code = g_strdup(attribute_values[i]); } else if(!strcmp (attribute_names[i], "symb" )) { entry->symbol = g_strdup(attribute_values[i]); } else if(!strcmp (attribute_names[i], "syprf" )) { entry->sym_prefix = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "dchar" )) { entry->decimal_char = g_strdup(attribute_values[i]); } else if(!strcmp (attribute_names[i], "gchar" )) { entry->grouping_char = g_strdup(attribute_values[i]); } else if(!strcmp (attribute_names[i], "frac" )) { entry->frac_digits = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "rate" )) { entry->rate = g_ascii_strtod(attribute_values[i], NULL); } else if(!strcmp (attribute_names[i], "mdate ")) { entry->mdate = atoi(attribute_values[i]); } } //all attribute loaded: append da_cur_insert (entry); } /*static void homebank_load_xml_tag(ParseContext *ctx, const gchar **attribute_names, const gchar **attribute_values) { Tag *entry = da_tag_malloc(); gint i; for (i = 0; attribute_names[i] != NULL; i++) { //DB( g_print(" att='%s' val='%s'\n", attribute_names[i], attribute_values[i]) ); if(!strcmp (attribute_names[i], "key" )) { entry->key = atoi(attribute_values[i]); } //else if(!strcmp (attribute_names[i], "flags")) { entry->flags = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "name" )) { entry->name = g_strdup(attribute_values[i]); } } //all attribute loaded: append da_tag_insert(entry); }*/ static void homebank_load_xml_fav(ParseContext *ctx, const gchar **attribute_names, const gchar **attribute_values) { Archive *entry = da_archive_malloc(); gchar *scat = NULL; gchar *samt = NULL; gchar *smem = NULL; gboolean split = FALSE; gint i; for (i = 0; attribute_names[i] != NULL; i++) { //DB( g_print(" att='%s' val='%s'\n", attribute_names[i], attribute_values[i]) ); if(!strcmp (attribute_names[i], "key" )) { entry->key = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "amount" )) { entry->amount = g_ascii_strtod(attribute_values[i], NULL); } else if(!strcmp (attribute_names[i], "account" )) { entry->kacc = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "dst_account")) { entry->kxferacc = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "paymode" )) { entry->paymode = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "st" )) { entry->status = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "flags" )) { entry->flags = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "payee" )) { entry->kpay = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "category" )) { entry->kcat = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "wording" )) { if(strcmp(attribute_values[i],"(null)") && attribute_values[i] != NULL) entry->memo = g_strdup(attribute_values[i]); } else if(!strcmp (attribute_names[i], "tags" )) { if(attribute_values[i] != NULL && strlen(attribute_values[i]) > 0 && strcmp(attribute_values[i],"(null)") != 0 ) { entry->tags = tags_parse(attribute_values[i]); } } else if(!strcmp (attribute_names[i], "nextdate" )) { entry->nextdate = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "every" )) { entry->every = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "unit" )) { entry->unit = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "limit" )) { entry->limit = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "weekend" )) { entry->weekend = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "gap" )) { entry->daygap = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "scat" )) { scat = (gchar *)attribute_values[i]; split = TRUE; } else if(!strcmp (attribute_names[i], "samt" )) { samt = (gchar *)attribute_values[i]; split = TRUE; } else if(!strcmp (attribute_names[i], "smem" )) { smem = (gchar *)attribute_values[i]; split = TRUE; } } if(split == TRUE) { entry->splits = da_split_new (); if (da_splits_parse(entry->splits, scat, samt, smem) > 0) { entry->flags |= OF_SPLIT; //Flag that Splits are active } } //all attribute loaded: append //GLOBALS->arc_list = g_list_append(GLOBALS->arc_list, entry); da_archive_append(entry); } static void homebank_load_xml_ope(ParseContext *ctx, const gchar **attribute_names, const gchar **attribute_values) { Transaction *entry = da_transaction_malloc(); gchar *scat = NULL; gchar *samt = NULL; gchar *smem = NULL; gboolean split = FALSE; gint i; for (i = 0; attribute_names[i] != NULL; i++) { //DB( g_print(" att='%s' val='%s'\n", attribute_names[i], attribute_values[i]) ); if(!strcmp (attribute_names[i], "date" )) { entry->date = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "amount" )) { entry->amount = g_ascii_strtod(attribute_values[i], NULL); } else if(!strcmp (attribute_names[i], "account" )) { entry->kacc = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "dst_account")) { entry->kxferacc = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "paymode" )) { entry->paymode = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "st" )) { entry->status = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "flags" )) { entry->flags = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "payee" )) { entry->kpay = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "category" )) { entry->kcat = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "wording" )) { if(strcmp(attribute_values[i],"(null)") && attribute_values[i] != NULL) entry->memo = g_strdup(attribute_values[i]); } else if(!strcmp (attribute_names[i], "info" )) { if(strcmp(attribute_values[i],"(null)") && attribute_values[i] != NULL) entry->info = g_strdup(attribute_values[i]); } else if(!strcmp (attribute_names[i], "tags" )) { if(attribute_values[i] != NULL && strlen(attribute_values[i]) > 0 && strcmp(attribute_values[i],"(null)") != 0 ) { entry->tags = tags_parse(attribute_values[i]); } } else if(!strcmp (attribute_names[i], "kxfer" )) { entry->kxfer = atoi(attribute_values[i]); } else if(!strcmp (attribute_names[i], "scat" )) { scat = (gchar *)attribute_values[i]; split = TRUE; } else if(!strcmp (attribute_names[i], "samt" )) { samt = (gchar *)attribute_values[i]; split = TRUE; } else if(!strcmp (attribute_names[i], "smem" )) { smem = (gchar *)attribute_values[i]; split = TRUE; } } //bugfix 303886 //if(entry->kcat < 0) // entry->kcat = 0; if(split == TRUE) { entry->splits = da_split_new (); if (da_splits_parse(entry->splits, scat, samt, smem) > 0) { entry->flags |= OF_SPLIT; //Flag that Splits are active } } //all attribute loaded: append // for perf reason we use prepend here, the list will be reversed later da_transaction_prepend(entry); } static void start_element_handler (GMarkupParseContext *context, const gchar *element_name, const gchar **attribute_names, const gchar **attribute_values, gpointer user_data, GError **error) { ParseContext *ctx = user_data; //GtkUIManager *self = ctx->self; //DB( g_print("** start element: '%s'\n", element_name) ); switch(element_name[0]) { case 'a': { if(!strcmp (element_name, "account")) //account { homebank_load_xml_acc(ctx, attribute_names, attribute_values); } else if(!strcmp (element_name, "asg")) //assign { homebank_load_xml_asg(ctx, attribute_names, attribute_values); } } break; case 'p': { if(!strcmp (element_name, "pay")) { homebank_load_xml_pay(ctx, attribute_names, attribute_values); } else if(!strcmp (element_name, "properties")) { homebank_load_xml_prop(ctx, attribute_names, attribute_values); } } break; case 'c': { if(!strcmp (element_name, "cat")) { homebank_load_xml_cat(ctx, attribute_names, attribute_values); } else if(!strcmp (element_name, "cur")) { homebank_load_xml_cur(ctx, attribute_names, attribute_values); } } break; //TODO: < 5.2 misstyped here, should be tag without a s //commented > 5.2 useless not loaded, but no side effect /*case 't': { if(!strcmp (element_name, "tags")) { homebank_load_xml_tag(ctx, attribute_names, attribute_values); } } break;*/ case 'f': { if(!strcmp (element_name, "fav")) { homebank_load_xml_fav(ctx, attribute_names, attribute_values); } } break; case 'o': { if(!strcmp (element_name, "ope")) { homebank_load_xml_ope(ctx, attribute_names, attribute_values); } } break; } } /* static void end_element_handler (GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error) { ParseContext *ctx = user_data; //DB( g_print("-- end element: %s\n", element_name) ); } */ static GMarkupParser hb_parser = { start_element_handler, NULL, //end_element_handler, NULL, //text_handler, NULL, NULL //cleanup }; static gboolean hb_xml_get_version(ParseContext *ctx, gchar *buffer) { gchar *v_buffer; ctx->file_version = 0.0; ctx->data_version = 0; /* v3.4 add :: prevent load of future file version */ v_buffer = g_strstr_len(buffer, 50, "file_version = g_ascii_strtod(v_buffer+13, NULL); /* a little hacky, but works ! */ if( ctx->file_version == 0.0 ) ctx->file_version = 0.1; else if( ctx->file_version == 5.0 ) //was a mistake ctx->file_version = 1.0; v_buffer = g_strstr_len(buffer+13, 50, "d="); if( v_buffer ) { DB( g_print(" d=%s)\n\n", v_buffer) ); ctx->data_version = atoi(v_buffer+3); } return TRUE; } /* ** XML load homebank file: hbfile */ gint homebank_load_xml(gchar *filename) { gint retval; gchar *buffer; gsize length; GError *error = NULL; ParseContext ctx; GMarkupParseContext *context; gboolean rc; DB( g_print("\n[hb-xml] homebank_load_xml\n") ); retval = XML_OK; if (!g_file_get_contents (filename, &buffer, &length, &error)) { if(error) { g_warning("unable to load file %s: %s", filename, error->message); g_error_free(error); retval = XML_IO_ERROR; } } else { if( hb_xml_get_version(&ctx, buffer) == FALSE ) { return XML_FILE_ERROR; } if( ctx.file_version > FILE_VERSION ) { DB( g_print("- failed: version %f is not supported (max is %f)\n", ctx.file_version, FILE_VERSION) ); return XML_VERSION_ERROR; } else { DB( g_print("- file ok : v=%.1f data_v=%06d\n", ctx.file_version, ctx.data_version) ); /* 1st: validate the file is well in utf-8 */ DB( g_print("- ensure UTF-8\n") ); buffer = homebank_utf8_ensure(buffer); /* then process the buffer */ #if MYDEBUG == 1 GTimer *t = g_timer_new(); g_print("- start parse\n"); #endif context = g_markup_parse_context_new (&hb_parser, 0, &ctx, NULL); error = NULL; rc = g_markup_parse_context_parse (context, buffer, length, &error); if( error ) { g_print("failed: %s\n", error->message); g_error_free (error); } if( rc == FALSE ) { error = NULL; g_markup_parse_context_end_parse(context, &error); if( error ) { g_print("failed: %s\n", error->message); g_error_free (error); } } g_markup_parse_context_free (context); g_free (buffer); DB( g_print("- end parse : %f sec\n", g_timer_elapsed(t, NULL)) ); DB( g_timer_destroy (t) ); /* file upgrade / bugfix */ // group a test for very old version if( ctx.file_version <= 1.0 ) { if( ctx.file_version <= 0.1 ) homebank_upgrade_to_v02(); if( ctx.file_version <= 0.2 ) homebank_upgrade_to_v03(); if( ctx.file_version <= 0.3 ) homebank_upgrade_to_v04(); if( ctx.file_version <= 0.4 ) homebank_upgrade_to_v05(); if( ctx.file_version <= 0.5 ) { homebank_upgrade_to_v06(); homebank_upgrade_lower_v06(); } if( ctx.file_version <= 0.6 ) { homebank_upgrade_to_v07(); hbfile_sanity_check(); } if( ctx.file_version <= 0.7 ) // <= 4.5 { homebank_upgrade_to_v08(); } if( ctx.file_version <= 0.8 ) // <= 4.6 { hbfile_sanity_check(); } if( ctx.file_version <= 0.9 ) // <= 4.6.3 { hbfile_sanity_check(); homebank_upgrade_to_v10(); } if( ctx.file_version <= 1.0 ) // <= 5.0.0 { hbfile_sanity_check(); homebank_upgrade_to_v11(); } } //starting 5.0.4 data upgrade is done without changing file_version //file version is changed only when the structure change //don't start number below with 0 to avoid octal interpretation if( ctx.data_version <= 50005 ) // <= 5.0.5 { hbfile_sanity_check(); } if( ctx.file_version <= 1.1 ) // <= 5.1.0 { hbfile_sanity_check(); homebank_upgrade_to_v12(); } if( ctx.data_version <= 50106 ) // < 5.1.6 { homebank_upgrade_to_v12_7(); } if( ctx.file_version < 1.3 ) // <= 5.2 { hbfile_sanity_check(); homebank_upgrade_to_v13(); } if( ctx.data_version <= 50203 ) { hbfile_sanity_check(); } // next ? } } return retval; } /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */ /* ** misc xml attributes methods */ static void hb_xml_append_txt(GString *gstring, gchar *attrname, gchar *value) { if(value != NULL && *value != 0) { gchar *escaped = g_markup_escape_text(value, -1); g_string_append_printf(gstring, " %s=\"%s\"", attrname, escaped); g_free(escaped); } } static void append_escaped_text (GString *str, const gchar *text, gssize length) { const gchar *p; const gchar *end; gunichar c; p = text; end = text + length; while (p < end) { const gchar *next; next = g_utf8_next_char (p); switch (*p) { case '&': g_string_append (str, "&"); break; case '<': g_string_append (str, "<"); break; case '>': g_string_append (str, ">"); break; case '\'': g_string_append (str, "'"); break; case '"': g_string_append (str, """); break; default: c = g_utf8_get_char (p); if ((0x1 <= c && c <= 0x8) || (0xa <= c && c <= 0xd) || //chnaged here from b<->c to a<->d (0xe <= c && c <= 0x1f) || (0x7f <= c && c <= 0x84) || (0x86 <= c && c <= 0x9f)) g_string_append_printf (str, "&#x%x;", c); else g_string_append_len (str, p, next - p); break; } p = next; } } // we override g_markup_escape_text from glib to encode \n (LF) & \r (CR) static void hb_xml_append_txt_crlf(GString *gstring, gchar *attrname, gchar *value) { if(value != NULL && *value != 0) { gssize length; GString *escaped; //gchar *escaped = g_markup_escape_text(value, -1); length = strlen (value); escaped = g_string_sized_new (length); append_escaped_text (escaped, value, length); g_string_append_printf(gstring, " %s=\"%s\"", attrname, escaped->str); g_string_free (escaped, TRUE); } } static void hb_xml_append_int0(GString *gstring, gchar *attrname, guint32 value) { g_string_append_printf(gstring, " %s=\"%d\"", attrname, value); } static void hb_xml_append_int(GString *gstring, gchar *attrname, guint32 value) { if(value != 0) { hb_xml_append_int0(gstring, attrname, value); } } static void hb_xml_append_amt(GString *gstring, gchar *attrname, gdouble amount) { char buf[G_ASCII_DTOSTR_BUF_SIZE]; //we must use this, as fprintf use locale decimal settings and not '.' g_ascii_dtostr (buf, sizeof (buf), amount); g_string_append_printf(gstring, " %s=\"%s\"", attrname, buf); } /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */ /* ** XML properties save */ static gint homebank_save_xml_prop(GIOChannel *io) { gchar *title; GString *node; gint retval = XML_OK; GError *error = NULL; title = GLOBALS->owner == NULL ? "" : GLOBALS->owner; node = g_string_sized_new(255); g_string_assign(node, "kcur); hb_xml_append_int(node, "car_category", GLOBALS->vehicle_category); hb_xml_append_int0(node, "auto_smode", GLOBALS->auto_smode); hb_xml_append_int(node, "auto_weekday", GLOBALS->auto_weekday); hb_xml_append_int(node, "auto_nbdays", GLOBALS->auto_nbdays); g_string_append(node, "/>\n"); error = NULL; g_io_channel_write_chars(io, node->str, -1, NULL, &error); if(error) { retval = XML_IO_ERROR; g_error_free(error); } g_string_free(node, TRUE); return retval; } /* ** XML currency save */ static gint homebank_save_xml_cur(GIOChannel *io) { GList *list; gchar *tmpstr; char buf1[G_ASCII_DTOSTR_BUF_SIZE]; gint retval = XML_OK; list = g_hash_table_get_values(GLOBALS->h_cur); while (list != NULL) { Currency *item = list->data; tmpstr = g_markup_printf_escaped( "\n", item->key, item->flags, item->iso_code, item->name, item->symbol, item->sym_prefix, item->decimal_char, item->grouping_char, item->frac_digits, g_ascii_dtostr (buf1, sizeof (buf1), item->rate), item->mdate ); g_io_channel_write_chars(io, tmpstr, -1, NULL, NULL); g_free(tmpstr); list = g_list_next(list); } g_list_free(list); return retval; } /* ** XML account save */ static gint homebank_save_xml_acc(GIOChannel *io) { GList *lacc, *list; GString *node; gint retval = XML_OK; GError *error = NULL; node = g_string_sized_new(255); lacc = list = account_glist_sorted(0); while (list != NULL) { Account *item = list->data; item->flags &= ~(AF_ADDED|AF_CHANGED); //delete flag g_string_assign(node, "key); hb_xml_append_int(node, "flags", item->flags); hb_xml_append_int(node, "pos", item->pos); hb_xml_append_int(node, "type", item->type); hb_xml_append_int(node, "curr", item->kcur); hb_xml_append_txt(node, "name", item->name); hb_xml_append_txt(node, "number", item->number); hb_xml_append_txt(node, "bankname", item->bankname); hb_xml_append_amt(node, "initial", item->initial); hb_xml_append_amt(node, "minimum", item->minimum); hb_xml_append_int(node, "cheque1", item->cheque1); hb_xml_append_int(node, "cheque2", item->cheque2); hb_xml_append_txt_crlf(node, "notes", item->notes); hb_xml_append_int(node, "tpl", item->karc); g_string_append(node, "/>\n"); error = NULL; g_io_channel_write_chars(io, node->str, -1, NULL, &error); if(error) { retval = XML_IO_ERROR; g_error_free(error); } list = g_list_next(list); } g_list_free(lacc); g_string_free(node, TRUE); return retval; } /* ** XML payee save */ static gint homebank_save_xml_pay(GIOChannel *io) { GList *lpay, *list; GString *node; gint retval = XML_OK; GError *error = NULL; node = g_string_sized_new(255); lpay = list = payee_glist_sorted(0); while (list != NULL) { Payee *item = list->data; if(item->key != 0) { g_string_assign(node, "key); hb_xml_append_txt(node, "name", item->name); hb_xml_append_int(node, "category", item->kcat); hb_xml_append_int(node, "paymode" , item->paymode); g_string_append(node, "/>\n"); error = NULL; g_io_channel_write_chars(io, node->str, -1, NULL, &error); if(error) { retval = XML_IO_ERROR; g_error_free(error); } } list = g_list_next(list); } g_list_free(lpay); g_string_free(node, TRUE); return retval; } /* ** XML category save */ static gint homebank_save_xml_cat(GIOChannel *io) { GList *lcat, *list; GString *node; char buf[G_ASCII_DTOSTR_BUF_SIZE]; guint i; gint retval = XML_OK; GError *error = NULL; node = g_string_sized_new(255); lcat = list = category_glist_sorted(0); while (list != NULL) { Category *item = list->data; if(item->key != 0) { g_string_assign(node, "key); hb_xml_append_int(node, "parent", item->parent); hb_xml_append_int(node, "flags", item->flags); hb_xml_append_txt(node, "name", item->name); for(i=0;i<=12;i++) { if(item->budget[i] != 0) { g_string_append_printf(node," b%d=\"%s\"", i, g_ascii_dtostr (buf, sizeof (buf), item->budget[i])); } } g_string_append(node, "/>\n"); error = NULL; g_io_channel_write_chars(io, node->str, -1, NULL, &error); if(error) { retval = XML_IO_ERROR; g_error_free(error); } } list = g_list_next(list); } g_list_free(lcat); g_string_free(node, TRUE); return retval; } /* ** XML tag save */ /*static gint homebank_save_xml_tag(GIOChannel *io) { GList *ltag, *list; gchar *tmpstr; gint retval = XML_OK; GError *error = NULL; ltag = list = tag_glist_sorted(0); while (list != NULL) { Tag *item = list->data; if(item->key != 0) { tmpstr = g_markup_printf_escaped("\n", item->key, item->name ); error = NULL; g_io_channel_write_chars(io, tmpstr, -1, NULL, &error); g_free(tmpstr); if(error) { retval = XML_IO_ERROR; g_error_free(error); } } list = g_list_next(list); } g_list_free(ltag); return retval; }*/ /* ** XML assign save */ static gint homebank_save_xml_asg(GIOChannel *io) { GList *lasg, *list; GString *node; gint retval = XML_OK; GError *error = NULL; node = g_string_sized_new(255); lasg = list = assign_glist_sorted(0); while (list != NULL) { Assign *item = list->data; g_string_assign(node, "key); hb_xml_append_int(node, "flags" , item->flags); hb_xml_append_int(node, "field" , item->field); hb_xml_append_txt(node, "name" , item->text); hb_xml_append_int(node, "payee" , item->kpay); hb_xml_append_int(node, "category", item->kcat); hb_xml_append_int(node, "paymode" , item->paymode); g_string_append(node, "/>\n"); error = NULL; g_io_channel_write_chars(io, node->str, -1, NULL, &error); if(error) { retval = XML_IO_ERROR; g_error_free(error); } list = g_list_next(list); } g_list_free(lasg); g_string_free(node, TRUE); return retval; } /* ** XML archive save */ static gint homebank_save_xml_arc(GIOChannel *io) { GList *list; GString *node; gchar *tagstr; gint retval = XML_OK; GError *error = NULL; node = g_string_sized_new(255); list = g_list_first(GLOBALS->arc_list); while (list != NULL) { Archive *item = list->data; tagstr = tags_tostring(item->tags); g_string_assign(node, "key); hb_xml_append_amt(node, "amount", item->amount); hb_xml_append_int(node, "account", item->kacc); hb_xml_append_int(node, "dst_account", item->kxferacc); hb_xml_append_int(node, "paymode", item->paymode); hb_xml_append_int(node, "st", item->status); hb_xml_append_int(node, "flags", item->flags); hb_xml_append_int(node, "payee", item->kpay); hb_xml_append_int(node, "category", item->kcat); hb_xml_append_txt(node, "wording", item->memo); hb_xml_append_txt(node, "tags", tagstr); hb_xml_append_int(node, "nextdate", item->nextdate); hb_xml_append_int(node, "every", item->every); hb_xml_append_int(node, "unit", item->unit); hb_xml_append_int(node, "limit", item->limit); hb_xml_append_int(node, "weekend", item->weekend); hb_xml_append_int(node, "gap", item->daygap); if(da_splits_length(item->splits) > 0) { gchar *cats, *amounts, *memos; da_splits_tostring(item->splits, &cats, &amounts, &memos); g_string_append_printf(node, " scat=\"%s\"", cats); g_string_append_printf(node, " samt=\"%s\"", amounts); //fix #1173910 gchar *escaped = g_markup_escape_text(memos, -1); g_string_append_printf(node, " smem=\"%s\"", escaped); g_free(escaped); g_free(cats); g_free(amounts); g_free(memos); } g_string_append(node, "/>\n"); g_free(tagstr); error = NULL; g_io_channel_write_chars(io, node->str, -1, NULL, &error); if(error) { retval = XML_IO_ERROR; g_error_free(error); } list = g_list_next(list); } g_string_free(node, TRUE); return retval; } /* ** XML transaction save */ static gint homebank_save_xml_ope(GIOChannel *io) { GList *lst_acc, *lnk_acc; GList *list; GString *node; gchar *tagstr; gint retval = XML_OK; GError *error = NULL; node = g_string_sized_new(255); lst_acc = g_hash_table_get_values(GLOBALS->h_acc); lnk_acc = g_list_first(lst_acc); while (lnk_acc != NULL) { Account *acc = lnk_acc->data; list = g_queue_peek_head_link(acc->txn_queue); while (list != NULL) { Transaction *item = list->data; item->flags &= ~(OF_AUTO|OF_ADDED|OF_CHANGED); //delete flag tagstr = tags_tostring(item->tags); g_string_assign(node, "date); hb_xml_append_amt(node, "amount", item->amount); hb_xml_append_int(node, "account", item->kacc); hb_xml_append_int(node, "dst_account", item->kxferacc); hb_xml_append_int(node, "paymode", item->paymode); hb_xml_append_int(node, "st", item->status); hb_xml_append_int(node, "flags", item->flags); hb_xml_append_int(node, "payee", item->kpay); hb_xml_append_int(node, "category", item->kcat); hb_xml_append_txt(node, "wording", item->memo); hb_xml_append_txt(node, "info", item->info); hb_xml_append_txt(node, "tags", tagstr); hb_xml_append_int(node, "kxfer", item->kxfer); if(da_splits_length(item->splits) > 0) { gchar *cats, *amounts, *memos; da_splits_tostring(item->splits, &cats, &amounts, &memos); g_string_append_printf(node, " scat=\"%s\"", cats); g_string_append_printf(node, " samt=\"%s\"", amounts); //fix #1173910 gchar *escaped = g_markup_escape_text(memos, -1); g_string_append_printf(node, " smem=\"%s\"", escaped); g_free(escaped); g_free(cats); g_free(amounts); g_free(memos); } g_string_append(node, "/>\n"); g_free(tagstr); error = NULL; g_io_channel_write_chars(io, node->str, -1, NULL, &error); if(error) { retval = XML_IO_ERROR; g_error_free(error); } list = g_list_next(list); } lnk_acc = g_list_next(lnk_acc); } g_list_free(lst_acc); g_string_free(node, TRUE); return retval; } /* ** XML save homebank file: hbfile */ gint homebank_save_xml(gchar *filename) { GIOChannel *io; char buf1[G_ASCII_DTOSTR_BUF_SIZE]; gchar *outstr; gint retval = XML_OK; GError *error = NULL; io = g_io_channel_new_file(filename, "w", &error); if(error) { g_warning("unable to save file %s: %s", filename, error->message); g_error_free(error); return(XML_IO_ERROR); } g_io_channel_write_chars(io, "\n", -1, NULL, NULL); outstr = g_strdup_printf("\n", g_ascii_dtostr (buf1, sizeof (buf1), FILE_VERSION), HB_VERSION_NUM); g_io_channel_write_chars(io, outstr, -1, NULL, NULL); g_free(outstr); retval = homebank_save_xml_prop(io); retval = homebank_save_xml_cur(io); retval = homebank_save_xml_acc(io); retval = homebank_save_xml_pay(io); retval = homebank_save_xml_cat(io); //retval = homebank_save_xml_tag(io); retval = homebank_save_xml_asg(io); retval = homebank_save_xml_arc(io); retval = homebank_save_xml_ope(io); g_io_channel_write_chars(io, "\n", -1, NULL, NULL); g_io_channel_unref (io); return retval; }