1 /**
2 * This file is a part of the Cairo-Dock project
3 *
4 * Copyright : (C) see the 'copyright' file.
5 * E-mail : see the 'copyright' file.
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
9 * as published by the Free Software Foundation; either version 3
10 * of 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 * 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 #include <string.h>
21 #include <stdlib.h>
22
23 #include "cairo-dock-log.h"
24 #include "cairo-dock-keyfile-utilities.h"
25
26
cairo_dock_open_key_file(const gchar * cConfFilePath)27 GKeyFile *cairo_dock_open_key_file (const gchar *cConfFilePath)
28 {
29 GKeyFile *pKeyFile = g_key_file_new ();
30 GError *erreur = NULL;
31 g_key_file_load_from_file (pKeyFile, cConfFilePath, G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, &erreur);
32 if (erreur != NULL)
33 {
34 cd_debug ("while trying to load %s : %s", cConfFilePath, erreur->message); // on ne met pas de warning car un fichier de conf peut ne pas exister la 1ere fois.
35 g_error_free (erreur);
36 g_key_file_free (pKeyFile);
37 return NULL;
38 }
39 return pKeyFile;
40 }
41
cairo_dock_write_keys_to_file(GKeyFile * pKeyFile,const gchar * cConfFilePath)42 void cairo_dock_write_keys_to_file (GKeyFile *pKeyFile, const gchar *cConfFilePath)
43 {
44 cd_debug ("%s (%s)", __func__, cConfFilePath);
45 GError *erreur = NULL;
46
47 gchar *cDirectory = g_path_get_dirname (cConfFilePath);
48 if (! g_file_test (cDirectory, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_EXECUTABLE))
49 {
50 g_mkdir_with_parents (cDirectory, 7*8*8+7*8+5);
51 }
52 g_free (cDirectory);
53
54
55 gsize length=0;
56 gchar *cNewConfFileContent = g_key_file_to_data (pKeyFile, &length, &erreur);
57 if (erreur != NULL)
58 {
59 cd_warning ("Error while fetching data : %s", erreur->message);
60 g_error_free (erreur);
61 return ;
62 }
63 g_return_if_fail (cNewConfFileContent != NULL && *cNewConfFileContent != '\0');
64
65 g_file_set_contents (cConfFilePath, cNewConfFileContent, length, &erreur);
66 if (erreur != NULL)
67 {
68 cd_warning ("Error while writing data to %s : %s", cConfFilePath, erreur->message);
69 g_error_free (erreur);
70 return ;
71 }
72 g_free (cNewConfFileContent);
73 }
74
75
76 // pOriginalKeyFile is an up-to-date key-file
77 // pReplacementKeyFile is an old key-file containing values we want to use
78 // keys are filtered by the identifier on the original key-file.
79 // old keys not present in pOriginalKeyFile are added
80 // new keys in pOriginalKeyFile not present in pReplacementKeyFile and having valid comment are removed
cairo_dock_merge_key_files(GKeyFile * pOriginalKeyFile,GKeyFile * pReplacementKeyFile,gchar iIdentifier)81 static void cairo_dock_merge_key_files (GKeyFile *pOriginalKeyFile, GKeyFile *pReplacementKeyFile, gchar iIdentifier)
82 {
83 // get the groups of the remplacement key-file.
84 GError *erreur = NULL;
85 gsize length = 0;
86 gchar **pKeyList;
87 gchar **pGroupList = g_key_file_get_groups (pReplacementKeyFile, &length);
88 g_return_if_fail (pGroupList != NULL);
89 gchar *cGroupName, *cKeyName, *cKeyValue, *cComment;
90 int i, j;
91
92 for (i = 0; pGroupList[i] != NULL; i ++)
93 {
94 cGroupName = pGroupList[i];
95
96 // get the keys of the remplacement key-file.
97 length = 0;
98 pKeyList = g_key_file_get_keys (pReplacementKeyFile, cGroupName, NULL, NULL);
99 g_return_if_fail (pKeyList != NULL);
100
101 for (j = 0; pKeyList[j] != NULL; j ++)
102 {
103 cKeyName = pKeyList[j];
104
105 // check that the original identifier matches with the provided one.
106 if (iIdentifier != 0)
107 {
108 if (g_key_file_has_key (pOriginalKeyFile, cGroupName, cKeyName, NULL)) // if the key doesn't exist in the original key-file, don't check the identifier, and add it to the key-file; it probably means it's an old key that will be taken care of by the applet.
109 {
110 cComment = g_key_file_get_comment (pOriginalKeyFile, cGroupName, cKeyName, NULL);
111 if (cComment == NULL || cComment[0] == '\0' || cComment[1] != iIdentifier)
112 {
113 g_free (cComment);
114 continue ;
115 }
116 g_free (cComment);
117 }
118 }
119
120 // get the replacement value and set it to the key-file, creating it if it didn't exist (in this case, no need to add the comment, since the key will be removed again by the applet).
121 cKeyValue = g_key_file_get_string (pReplacementKeyFile, cGroupName, cKeyName, &erreur);
122 if (erreur != NULL)
123 {
124 cd_warning (erreur->message);
125 g_error_free (erreur);
126 erreur = NULL;
127 }
128 else
129 {
130 if (cKeyValue && cKeyValue[strlen(cKeyValue) - 1] == '\n')
131 cKeyValue[strlen(cKeyValue) - 1] = '\0';
132 g_key_file_set_string (pOriginalKeyFile, cGroupName, cKeyName, (cKeyValue != NULL ? cKeyValue : ""));
133 }
134 g_free (cKeyValue);
135 }
136 g_strfreev (pKeyList);
137 }
138 g_strfreev (pGroupList);
139
140 // remove keys from the original key-file which are not in the remplacement key-file, except hidden and persistent keys.
141 pGroupList = g_key_file_get_groups (pOriginalKeyFile, &length);
142 g_return_if_fail (pGroupList != NULL);
143 for (i = 0; pGroupList[i] != NULL; i ++)
144 {
145 cGroupName = pGroupList[i];
146
147 // get the keys of the original key-file.
148 length = 0;
149 pKeyList = g_key_file_get_keys (pOriginalKeyFile, cGroupName, NULL, NULL);
150 g_return_if_fail (pKeyList != NULL);
151
152 for (j = 0; pKeyList[j] != NULL; j ++)
153 {
154 cKeyName = pKeyList[j];
155 if (! g_key_file_has_key (pReplacementKeyFile, cGroupName, cKeyName, NULL))
156 {
157 cComment = g_key_file_get_comment (pOriginalKeyFile, cGroupName, cKeyName, NULL);
158 if (cComment != NULL && cComment[0] != '\0' && cComment[1] != '0') // not hidden nor peristent
159 {
160 g_key_file_remove_comment (pOriginalKeyFile, cGroupName, cKeyName, NULL);
161 g_key_file_remove_key (pOriginalKeyFile, cGroupName, cKeyName, NULL);
162 }
163 }
164 }
165 g_strfreev (pKeyList);
166 }
167 g_strfreev (pGroupList);
168 }
169
cairo_dock_merge_conf_files(const gchar * cConfFilePath,gchar * cReplacementConfFilePath,gchar iIdentifier)170 void cairo_dock_merge_conf_files (const gchar *cConfFilePath, gchar *cReplacementConfFilePath, gchar iIdentifier)
171 {
172 GKeyFile *pOriginalKeyFile = cairo_dock_open_key_file (cConfFilePath);
173 g_return_if_fail (pOriginalKeyFile != NULL);
174 GKeyFile *pReplacementKeyFile = cairo_dock_open_key_file (cReplacementConfFilePath);
175 g_return_if_fail (pReplacementKeyFile != NULL);
176
177 cairo_dock_merge_key_files (pOriginalKeyFile, pReplacementKeyFile, iIdentifier);
178 cairo_dock_write_keys_to_file (pOriginalKeyFile, cConfFilePath);
179
180 g_key_file_free (pOriginalKeyFile);
181 g_key_file_free (pReplacementKeyFile);
182 }
183
184
185 // update launcher key-file: use ukf keys (= template) => remove old, add news
186 // update applet key-file: use vkf keys (= user) if exist in template or NULL/0 comment => keep user keys.
187
188 // pValuesKeyFile is a key-file with correct values, but old comments and possibly missing or old keys.
189 // pUptodateKeyFile is a template key-file with default values.
190 // bUpdateKeys is TRUE to use up-to-date keys.
_cairo_dock_replace_key_values(GKeyFile * pValuesKeyFile,GKeyFile * pUptodateKeyFile,gboolean bUpdateKeys)191 static void _cairo_dock_replace_key_values (GKeyFile *pValuesKeyFile, GKeyFile *pUptodateKeyFile, gboolean bUpdateKeys)
192 {
193 GKeyFile *pKeysKeyFile = (bUpdateKeys ? pUptodateKeyFile : pValuesKeyFile);
194
195 // get the groups.
196 GError *erreur = NULL;
197 gsize length = 0;
198 gchar **pKeyList;
199 gchar **pGroupList = g_key_file_get_groups (pKeysKeyFile, &length);
200 g_return_if_fail (pGroupList != NULL);
201 gchar *cGroupName, *cKeyName, *cKeyValue, *cComment;
202 int i, j;
203
204 for (i = 0; pGroupList[i] != NULL; i ++)
205 {
206 cGroupName = pGroupList[i];
207
208 // get the keys.
209 length = 0;
210 pKeyList = g_key_file_get_keys (pKeysKeyFile, cGroupName, NULL, NULL);
211 g_return_if_fail (pKeyList != NULL);
212
213 for (j = 0; pKeyList[j] != NULL; j ++)
214 {
215 cKeyName = pKeyList[j];
216 cComment = NULL;
217
218 // don't add old keys, except if they are hidden or persistent.
219 if (!g_key_file_has_key (pUptodateKeyFile, cGroupName, cKeyName, NULL)) // old key
220 {
221 cComment = g_key_file_get_comment (pValuesKeyFile, cGroupName, cKeyName, NULL);
222 if (cComment != NULL && cComment[0] != '\0' && cComment[1] != '0') // not hidden nor persistent => skip it.
223 {
224 g_free (cComment);
225 continue;
226 }
227 }
228
229 // get the replacement value and set it to the key-file, creating it if it didn't exist.
230 cKeyValue = g_key_file_get_string (pValuesKeyFile, cGroupName, cKeyName, &erreur);
231 if (erreur != NULL) // key doesn't exist
232 {
233 cd_warning (erreur->message);
234 g_error_free (erreur);
235 erreur = NULL;
236 }
237 else
238 {
239 g_key_file_set_string (pUptodateKeyFile, cGroupName, cKeyName, (cKeyValue != NULL ? cKeyValue : ""));
240 if (cComment != NULL) // if we got the comment, it means the key doesn't exist in the up-to-date key-file, so add it.
241 g_key_file_set_comment (pUptodateKeyFile, cGroupName, cKeyName, cComment, NULL);
242 }
243 g_free (cKeyValue);
244 g_free (cComment);
245 }
246
247 g_strfreev (pKeyList);
248 }
249 g_strfreev (pGroupList);
250
251 }
252
cairo_dock_upgrade_conf_file_full(const gchar * cConfFilePath,GKeyFile * pKeyFile,const gchar * cDefaultConfFilePath,gboolean bUpdateKeys)253 void cairo_dock_upgrade_conf_file_full (const gchar *cConfFilePath, GKeyFile *pKeyFile, const gchar *cDefaultConfFilePath, gboolean bUpdateKeys)
254 {
255 GKeyFile *pUptodateKeyFile = cairo_dock_open_key_file (cDefaultConfFilePath);
256 g_return_if_fail (pUptodateKeyFile != NULL);
257
258 _cairo_dock_replace_key_values (pKeyFile, pUptodateKeyFile, bUpdateKeys);
259
260 cairo_dock_write_keys_to_file (pUptodateKeyFile, cConfFilePath);
261
262 g_key_file_free (pUptodateKeyFile);
263 }
264
265
cairo_dock_get_conf_file_version(GKeyFile * pKeyFile,gchar ** cConfFileVersion)266 void cairo_dock_get_conf_file_version (GKeyFile *pKeyFile, gchar **cConfFileVersion)
267 {
268 *cConfFileVersion = NULL;
269
270 gchar *cFirstComment = g_key_file_get_comment (pKeyFile, NULL, NULL, NULL);
271
272 if (cFirstComment != NULL && *cFirstComment != '\0')
273 {
274 gchar *str = strchr (cFirstComment, '\n');
275 if (str != NULL)
276 *str = '\0';
277
278 str = strchr (cFirstComment, ';'); // le 1er est pour la langue (obsolete).
279 if (str != NULL)
280 {
281 *cConfFileVersion = g_strdup (str+1);
282 }
283 else
284 {
285 *cConfFileVersion = g_strdup (cFirstComment + (*cFirstComment == '!')); // le '!' est obsolete.
286 }
287 }
288 g_free (cFirstComment);
289 }
290
cairo_dock_conf_file_needs_update(GKeyFile * pKeyFile,const gchar * cVersion)291 gboolean cairo_dock_conf_file_needs_update (GKeyFile *pKeyFile, const gchar *cVersion)
292 {
293 gchar *cPreviousVersion = NULL;
294 cairo_dock_get_conf_file_version (pKeyFile, &cPreviousVersion);
295 gboolean bNeedsUpdate = (cPreviousVersion == NULL || strcmp (cPreviousVersion, cVersion) != 0);
296
297 g_free (cPreviousVersion);
298 return bNeedsUpdate;
299 }
300
301
cairo_dock_add_remove_element_to_key(const gchar * cConfFilePath,const gchar * cGroupName,const gchar * cKeyName,gchar * cElementName,gboolean bAdd)302 void cairo_dock_add_remove_element_to_key (const gchar *cConfFilePath, const gchar *cGroupName, const gchar *cKeyName, gchar *cElementName, gboolean bAdd)
303 {
304 GKeyFile *pKeyFile = cairo_dock_open_key_file (cConfFilePath);
305 if (pKeyFile == NULL)
306 return ;
307
308 gchar *cElementList = g_key_file_get_string (pKeyFile, cGroupName, cKeyName, NULL), *cNewElementList = NULL;
309 if (cElementList != NULL && *cElementList == '\0')
310 {
311 g_free (cElementList);
312 cElementList= NULL;
313 }
314
315 if (bAdd)
316 {
317 //g_print ("on rajoute %s\n", cElementName);
318 if (cElementList != NULL)
319 cNewElementList = g_strdup_printf ("%s;%s", cElementList, cElementName);
320 else
321 cNewElementList = g_strdup (cElementName);
322 }
323 else
324 {
325 //g_print ("on enleve %s\n", cElementName);
326 gchar *str = g_strstr_len (cElementList, strlen (cElementList), cElementName);
327 g_return_if_fail (str != NULL);
328 if (str == cElementList)
329 {
330 if (str[strlen (cElementName)] == '\0')
331 cNewElementList = g_strdup ("");
332 else
333 cNewElementList = g_strdup (str + strlen (cElementName) + 1);
334 }
335 else
336 {
337 *(str-1) = '\0';
338 if (str[strlen (cElementName)] == '\0')
339 cNewElementList = g_strdup (cElementList);
340 else
341 cNewElementList = g_strdup_printf ("%s;%s", cElementList, str + strlen (cElementName) + 1);
342 }
343 }
344 g_key_file_set_string (pKeyFile, cGroupName, cKeyName, cNewElementList);
345 cairo_dock_write_keys_to_file (pKeyFile, cConfFilePath);
346 g_free (cElementList);
347 g_free (cNewElementList);
348 g_key_file_free (pKeyFile);
349 }
350
351
cairo_dock_add_widget_to_conf_file(GKeyFile * pKeyFile,const gchar * cGroupName,const gchar * ckeyName,const gchar * cInitialValue,CairoDockGUIWidgetType iWidgetType,const gchar * cAuthorizedValues,const gchar * cDescription,const gchar * cTooltip)352 void cairo_dock_add_widget_to_conf_file (GKeyFile *pKeyFile, const gchar *cGroupName, const gchar *ckeyName, const gchar *cInitialValue, CairoDockGUIWidgetType iWidgetType, const gchar *cAuthorizedValues, const gchar *cDescription, const gchar *cTooltip)
353 {
354 g_key_file_set_string (pKeyFile, cGroupName, ckeyName, cInitialValue);
355 gchar *Comment = g_strdup_printf ("%c0%s %s%s%s%s", iWidgetType, cAuthorizedValues ? cAuthorizedValues : "", cDescription, cTooltip ? "\n{" : "", cTooltip ? cTooltip : "", cTooltip ? "}" : "");
356 g_key_file_set_comment (pKeyFile, cGroupName, ckeyName, Comment, NULL);
357 g_free (Comment);
358 }
359
cairo_dock_remove_group_key_from_conf_file(GKeyFile * pKeyFile,const gchar * cGroupName,const gchar * ckeyName)360 void cairo_dock_remove_group_key_from_conf_file (GKeyFile *pKeyFile, const gchar *cGroupName, const gchar *ckeyName)
361 {
362 g_key_file_remove_comment (pKeyFile, cGroupName, ckeyName, NULL);
363 g_key_file_remove_key (pKeyFile, cGroupName, ckeyName, NULL);
364 }
365
cairo_dock_rename_group_in_conf_file(GKeyFile * pKeyFile,const gchar * cGroupName,const gchar * cNewGroupName)366 gboolean cairo_dock_rename_group_in_conf_file (GKeyFile *pKeyFile, const gchar *cGroupName, const gchar *cNewGroupName)
367 {
368 if (! g_key_file_has_group (pKeyFile, cGroupName))
369 return FALSE;
370
371 gchar **pKeyList = g_key_file_get_keys (pKeyFile, cGroupName, NULL, NULL);
372 g_return_val_if_fail (pKeyList != NULL, FALSE);
373 gchar *cValue;
374 int i;
375 for (i = 0; pKeyList[i] != NULL; i ++)
376 {
377 cValue = g_key_file_get_value (pKeyFile, cGroupName, pKeyList[i], NULL);
378 g_key_file_set_value (pKeyFile, cNewGroupName, pKeyList[i], cValue);
379 g_free (cValue);
380 }
381 g_strfreev (pKeyList);
382
383 g_key_file_remove_group (pKeyFile, cGroupName, NULL);
384
385 return TRUE;
386 }
387
cairo_dock_get_locale_string_from_conf_file(GKeyFile * pKeyFile,const gchar * cGroupName,const gchar * cKeyName,const gchar * cLocale)388 gchar * cairo_dock_get_locale_string_from_conf_file (GKeyFile *pKeyFile, const gchar *cGroupName, const gchar *cKeyName, const gchar *cLocale)
389 {
390 gchar *cKeyValue = g_key_file_get_string (pKeyFile, cGroupName, cKeyName, NULL);
391 // if the string is empty, gettext mays return a non empty string (e.g. on OpenSUSE we get the .po header)
392 if (cKeyValue == NULL || *cKeyValue == '\0')
393 {
394 g_free (cKeyValue);
395 return NULL;
396 }
397
398 g_free (cKeyValue);
399 return g_key_file_get_locale_string (pKeyFile, cGroupName, cKeyName, cLocale, NULL);
400 }
401
cairo_dock_update_keyfile_va_args(const gchar * cConfFilePath,GType iFirstDataType,va_list args)402 void cairo_dock_update_keyfile_va_args (const gchar *cConfFilePath, GType iFirstDataType, va_list args)
403 {
404 cd_message ("%s (%s)", __func__, cConfFilePath);
405
406 GKeyFile *pKeyFile = g_key_file_new (); // if the key-file doesn't exist, it will be created.
407 g_key_file_load_from_file (pKeyFile, cConfFilePath, G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, NULL);
408
409 GType iType = iFirstDataType;
410 gboolean bValue;
411 gint iValue;
412 double fValue;
413 gchar *cValue;
414 gchar *cGroupName, *cGroupKey;
415 while (iType != G_TYPE_INVALID)
416 {
417 cGroupName = va_arg (args, gchar *);
418 cGroupKey = va_arg (args, gchar *);
419
420 switch (iType)
421 {
422 case G_TYPE_BOOLEAN :
423 bValue = va_arg (args, gboolean);
424 g_key_file_set_boolean (pKeyFile, cGroupName, cGroupKey, bValue);
425 break ;
426 case G_TYPE_INT :
427 iValue = va_arg (args, gint);
428 g_key_file_set_integer (pKeyFile, cGroupName, cGroupKey, iValue);
429 break ;
430 case G_TYPE_DOUBLE :
431 fValue = va_arg (args, gdouble);
432 g_key_file_set_double (pKeyFile, cGroupName, cGroupKey, fValue);
433 break ;
434 case G_TYPE_STRING :
435 cValue = va_arg (args, gchar *);
436 g_key_file_set_string (pKeyFile, cGroupName, cGroupKey, cValue);
437 break ;
438 default :
439 break ;
440 }
441
442 iType = va_arg (args, GType);
443 }
444
445 cairo_dock_write_keys_to_file (pKeyFile, cConfFilePath);
446 g_key_file_free (pKeyFile);
447 }
448
cairo_dock_update_keyfile(const gchar * cConfFilePath,GType iFirstDataType,...)449 void cairo_dock_update_keyfile (const gchar *cConfFilePath, GType iFirstDataType, ...) // type, groupe, cle, valeur, etc. finir par G_TYPE_INVALID.
450 {
451 va_list args;
452 va_start (args, iFirstDataType);
453 cairo_dock_update_keyfile_va_args (cConfFilePath, iFirstDataType, args);
454 va_end (args);
455 }
456
457