GnuCash  5.6-150-g038405b370+
gnc-state.c
1 /********************************************************************\
2  * file-utils.c -- simple file utilities *
3  * Copyright (C) 1997 Robin D. Clark <rclark@cs.hmc.edu> *
4  * Copyright (C) 1998 Rob Browning *
5  * Copyright (C) 1998-2000 Linas Vepstas <linas@linas.org> *
6  * *
7  * This program is free software; you can redistribute it and/or *
8  * modify it under the terms of the GNU General Public License as *
9  * published by the Free Software Foundation; either version 2 of *
10  * the License, or (at your option) any later version. *
11  * *
12  * This program is distributed in the hope that it will be useful, *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15  * GNU General Public License for more details. *
16  * *
17  * You should have received a copy of the GNU General Public License*
18  * along with this program; if not, write to the Free Software *
19  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
20 \********************************************************************/
21 
22 #include <config.h>
23 
24 #include <glib.h>
25 //#include <glib/gstdio.h>
26 //#include <errno.h>
27 
28 #include "gnc-state.h"
29 //#include "gnc-engine.h"
30 #include "gnc-filepath-utils.h"
31 #include "gnc-gkeyfile-utils.h"
32 #include "gnc-uri-utils.h"
33 #include "qof.h"
34 
35 /* This static indicates the debugging module that this .o belongs to. */
36 static QofLogModule log_module = G_LOG_DOMAIN;
37 
38 /* Absolute path to the state file for the current book
39  * Before 2.4.1, this file didn't have an extension.
40  * The code will look for such pre 2.4.0 file if no post 2.4.1
41  * version is found. If there is an old style file, save the
42  * name here as well. The old style state file will then be
43  * converted into a new style one the next time state is saved.
44  */
45 static gchar* state_file_name = NULL;
46 static gchar* state_file_name_pre_241 = NULL;
47 /* State file data for current book */
48 static GKeyFile *state_file = NULL;
49 
50 /* Determine which file name to use for the state file. This name is based
51  * the current book's uri and guid.
52  *
53  * The state files will be searched for in the books directory in GnuCash'
54  * private configuration directory. This configuration directory is
55  * platform dependent and can be overridden with environment variable
56  * DOT_GNUCASH_DIR. On linux for example this is ~/.gnucash by default.
57  *
58  * The URL is used to compute the base name of the state file and the
59  * guid is used to differentiate when the user has multiple data files
60  * with the same name.
61  *
62  * As of GnuCash 2.4.1 state files will have their own extension to
63  * differentiate them from data files saved by the user. New state
64  * files will always be created with such an extension. But GnuCash
65  * will continue to search for state files without an extension if
66  * no proper state file with extension is found. */
67 
68 
69 static void
70 gnc_state_set_base (const QofSession *session)
71 {
72  gchar *basename, *original = NULL, *filename, *file_guid;
73  gchar *sf_extension = NULL;
74  const gchar *uri;
75  gchar guid_string[GUID_ENCODING_LENGTH+1];
76  QofBook *book;
77  const GncGUID *guid;
78  GKeyFile *key_file = NULL;
79  gint i;
80 
81  /* Reset filenames possibly found in a previous run */
82  g_free (state_file_name);
83  g_free (state_file_name_pre_241);
84  state_file_name = NULL;
85  state_file_name_pre_241 = NULL;
86 
87  uri = qof_session_get_url (session);
88  ENTER("session %p (%s)", session, uri ? uri : "(null)");
89  if (!strlen (uri))
90  {
91  LEAVE("no uri, nothing to do");
92  return;
93  }
94 
95  /* Get the book GncGUID */
96  book = qof_session_get_book(session);
97  guid = qof_entity_get_guid(QOF_INSTANCE(book));
98  guid_to_string_buff(guid, guid_string);
99 
100  if (gnc_uri_targets_local_fs (uri))
101  {
102  /* The book_uri is a true file, use its basename. */
103  gchar *path = gnc_uri_get_path (uri);
104  basename = g_path_get_basename (path);
105  g_free (path);
106  }
107  else
108  {
109  /* The book_uri is composed of database connection parameters. */
110  gchar* scheme = NULL;
111  gchar* host = NULL;
112  gchar* dbname = NULL;
113  gchar* username = NULL;
114  gchar* password = NULL;
115  gint portnum = 0;
116  gnc_uri_get_components (uri, &scheme, &host, &portnum,
117  &username, &password, &dbname);
118 
119  basename = g_strjoin ("_", scheme, host, username, dbname, NULL);
120  g_free (scheme);
121  g_free (host);
122  g_free (username);
123  g_free (password);
124  g_free (dbname);
125  }
126 
127  DEBUG ("Basename %s", basename);
128  original = gnc_build_book_path (basename);
129  g_free (basename);
130  DEBUG ("Original %s", original);
131 
132  sf_extension = g_strdup (STATE_FILE_EXT);
133  i = 1;
134  while (1)
135  {
136  if (i == 1)
137  filename = g_strconcat (original, sf_extension, NULL);
138  else
139  filename = g_strdup_printf ("%s_%d%s", original, i, sf_extension);
140  DEBUG ("Trying %s", filename);
141  key_file = gnc_key_file_load_from_file (filename, TRUE, FALSE, NULL);
142  DEBUG ("Result %p", key_file);
143 
144  if (!key_file)
145  {
146  DEBUG ("No key file by that name");
147  if (g_strcmp0 (sf_extension, STATE_FILE_EXT) == 0)
148  {
149  DEBUG ("Trying old state file names for compatibility");
150  i = 1;
151  g_free (sf_extension);
152  sf_extension = g_strdup ("");
153 
154  /* Regardless of whether or not an old state file is found,
155  * the currently tested name should be used for the future
156  * state file.
157  */
158  state_file_name = filename;
159  continue;
160  }
161 
162  /* No old style file found. We'll return with the new file name
163  * we set earlier, and no existing key file. */
164  g_free (filename);
165  break;
166  }
167 
168  file_guid = g_key_file_get_string (key_file,
169  STATE_FILE_TOP, STATE_FILE_BOOK_GUID,
170  NULL);
171  DEBUG ("File GncGUID is %s", file_guid ? file_guid : "<not found>");
172  if (g_strcmp0 (guid_string, file_guid) == 0)
173  {
174  DEBUG ("Matched !!!");
175  /* Save the found file for later use. Which name to save to
176  * depends on whether it was an old or new style file name
177  */
178  if (g_strcmp0 (sf_extension, STATE_FILE_EXT) == 0)
179  state_file_name = filename;
180  else
181  state_file_name_pre_241 = filename;
182 
183  g_free (file_guid);
184  break;
185  }
186  DEBUG ("Clean up this pass");
187  g_free (file_guid);
188  g_key_file_free (key_file);
189  g_free (filename);
190  i++;
191  }
192 
193  DEBUG("Clean up");
194  g_free(sf_extension);
195  g_free(original);
196  if (key_file != NULL)
197  g_key_file_free (key_file);
198 
199  LEAVE ();
200 }
201 
202 GKeyFile *gnc_state_load (const QofSession *session)
203 {
204  /* Drop possible previous state_file first */
205  if (state_file)
206  {
207  g_key_file_free (state_file);
208  state_file = NULL;
209  }
210 
211  gnc_state_set_base (session);
212 
213  if (state_file_name_pre_241)
214  state_file = gnc_key_file_load_from_file (state_file_name_pre_241,
215  TRUE, TRUE, NULL);
216  else if (state_file_name)
217  state_file = gnc_key_file_load_from_file (state_file_name,
218  TRUE, TRUE, NULL);
219 
220  return gnc_state_get_current ();
221 }
222 
223 void gnc_state_save (const QofSession *session)
224 {
225  GError *error = NULL;
226 
227  if (!strlen (qof_session_get_url(session)))
228  {
229  DEBUG("No file associated with session - skip state saving");
230  return;
231  }
232 
233  gnc_state_set_base (session);
234 
235  /* Write it all out to disk */
236  if (state_file_name)
237  gnc_key_file_save_to_file(state_file_name, state_file, &error);
238  else
239  PWARN ("No state file name set, can't save state");
240 
241  if (error)
242  {
243  PERR ("Error: Cannot open state file %s", error->message);
244  g_error_free (error);
245  }
246 }
247 
248 GKeyFile *gnc_state_get_current (void)
249 {
250  if (!state_file)
251  {
252  PINFO ("No pre-existing state found, creating new one");
253  state_file = g_key_file_new ();
254  }
255 
256  return state_file;
257 
258 }
259 
260 gint gnc_state_drop_sections_for (const gchar *partial_name)
261 {
262  gchar **groups;
263  gint found_count = 0, dropped_count = 0;
264  gsize i, num_groups;
265  GError *error = NULL;
266 
267  if (!state_file)
268  {
269  PWARN ("No pre-existing state found, ignoring drop request");
270  return 0;
271  }
272 
273  ENTER("");
274 
275  groups = g_key_file_get_groups (state_file, &num_groups);
276  for (i = 0; i < num_groups; i++)
277  {
278  if (g_strstr_len (groups[i], -1, partial_name))
279  {
280  DEBUG ("Section \"%s\" matches \"%s\", removing", groups[i], partial_name);
281  found_count++;
282  if (!g_key_file_remove_group (state_file, groups[i], &error))
283  {
284  PWARN ("Warning: unable to remove section %s.\n %s",
285  groups[i],
286  error->message);
287  g_error_free (error);
288  }
289  else
290  dropped_count++;
291 
292  }
293  }
294  g_strfreev (groups);
295 
296  LEAVE("Found %i sections matching \"%s\", successfully removed %i",
297  found_count, partial_name, dropped_count);
298  return dropped_count;
299 
300 }
301 
Functions to load, save and get gui state.
gchar * gnc_build_book_path(const gchar *filename)
Make a path to filename in the book subdirectory of the user&#39;s configuration directory.
#define G_LOG_DOMAIN
Functions providing the SX List as a plugin page.
void gnc_state_save(const QofSession *session)
Save the state to a state file on disk for the given session.
Definition: gnc-state.c:223
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
gint gnc_state_drop_sections_for(const gchar *partial_name)
Drop all sections from the state file whose name contains partial_name.
Definition: gnc-state.c:260
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
GKeyFile helper routines.
gchar * gnc_uri_get_path(const gchar *uri)
Extracts the path part from a uri.
gchar * guid_to_string_buff(const GncGUID *guid, gchar *str)
The guid_to_string_buff() routine puts a null-terminated string encoding of the id into the memory po...
Definition: guid.cpp:173
void gnc_uri_get_components(const gchar *uri, gchar **scheme, gchar **hostname, gint32 *port, gchar **username, gchar **password, gchar **path)
Converts a uri in separate components.
#define PERR(format, args...)
Log a serious error.
Definition: qoflog.h:244
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
GKeyFile * gnc_state_get_current(void)
Returns a pointer to the most recently loaded state.
Definition: gnc-state.c:248
gboolean gnc_key_file_save_to_file(const gchar *filename, GKeyFile *key_file, GError **error)
Write a key/value file from memory to disk.
#define PWARN(format, args...)
Log a warning.
Definition: qoflog.h:250
QofBook * qof_session_get_book(const QofSession *session)
Returns the QofBook of this session.
Definition: qofsession.cpp:574
#define GUID_ENCODING_LENGTH
Number of characters needed to encode a guid as a string not including the null terminator.
Definition: guid.h:84
const GncGUID * qof_entity_get_guid(gconstpointer ent)
gboolean gnc_uri_targets_local_fs(const gchar *uri)
Checks if the given uri is either a valid file uri or a local filesystem path.
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
Utility functions for convert uri in separate components and back.
File path resolution utility functions.
GKeyFile * gnc_state_load(const QofSession *session)
Load the state from a state file on disk for the given session.
Definition: gnc-state.c:202
GKeyFile * gnc_key_file_load_from_file(const gchar *filename, gboolean ignore_error, gboolean return_empty_struct, GError **caller_error)
Open and read a key/value file from disk into memory.
The type used to store guids in C.
Definition: guid.h:75