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