1 /*
2    Provides a serialize/unserialize functionality for INI-like formats.
3 
4    Copyright (C) 2011-2021
5    Free Software Foundation, Inc.
6 
7    Written by:
8    Slava Zanko <slavazanko@gmail.com>, 2011
9 
10    This file is part of the Midnight Commander.
11 
12    The Midnight Commander is free software: you can redistribute it
13    and/or modify it under the terms of the GNU General Public License as
14    published by the Free Software Foundation, either version 3 of the License,
15    or (at your option) any later version.
16 
17    The Midnight Commander is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20    GNU General Public License for more details.
21 
22    You should have received a copy of the GNU General Public License
23    along with this program.  If not, see <http://www.gnu.org/licenses/>.
24  */
25 
26 /** \file lib/serialize.c
27  *  \brief Source: serialize/unserialize functionality for INI-like formats.
28  */
29 
30 #include <config.h>
31 
32 #include <string.h>
33 #include <stdlib.h>
34 #include <stdarg.h>
35 
36 #include "lib/global.h"
37 
38 #include "lib/serialize.h"
39 
40 /*** global variables ****************************************************************************/
41 
42 /*** file scope macro definitions ****************************************************************/
43 
44 #define SRLZ_DELIM_C ':'
45 #define SRLZ_DELIM_S ":"
46 
47 /*** file scope type declarations ****************************************************************/
48 
49 /*** file scope variables ************************************************************************/
50 
51 /*** file scope functions ************************************************************************/
52 /* --------------------------------------------------------------------------------------------- */
53 
54 static void
55 G_GNUC_PRINTF (2, 3)
prepend_error_message(GError ** error,const char * format,...)56 prepend_error_message (GError ** error, const char *format, ...)
57 {
58     char *prepend_str;
59     char *split_str;
60     va_list ap;
61 
62     if ((error == NULL) || (*error == NULL))
63         return;
64 
65     va_start (ap, format);
66     prepend_str = g_strdup_vprintf (format, ap);
67     va_end (ap);
68 
69     split_str = g_strdup_printf ("%s: %s", prepend_str, (*error)->message);
70     g_free (prepend_str);
71     g_free ((*error)->message);
72     (*error)->message = split_str;
73 }
74 
75 /* --------------------------------------------------------------------------------------------- */
76 
77 static const char *
go_to_end_of_serialized_string(const char * non_serialized_data,const char * already_serialized_part,size_t * offset)78 go_to_end_of_serialized_string (const char *non_serialized_data,
79                                 const char *already_serialized_part, size_t * offset)
80 {
81     size_t calculated_offset;
82     const char *semi_ptr = strchr (non_serialized_data + 1, SRLZ_DELIM_C);
83 
84     calculated_offset = (semi_ptr - non_serialized_data) + 1 + strlen (already_serialized_part);
85     if (calculated_offset >= strlen (non_serialized_data))
86         return NULL;
87 
88     non_serialized_data += calculated_offset;
89     *offset += calculated_offset;
90 
91     return non_serialized_data;
92 }
93 
94 /* --------------------------------------------------------------------------------------------- */
95 /*** public functions ****************************************************************************/
96 /* --------------------------------------------------------------------------------------------- */
97 
98 /**
99  * Serialize some string object to string
100  *
101  * @param prefix prefix for serailization
102  * @param data data for serialization
103  * @param error contain pointer to object for handle error code and message
104  *
105  * @return serialized data as newly allocated string
106  */
107 
108 char *
mc_serialize_str(const char prefix,const char * data,GError ** error)109 mc_serialize_str (const char prefix, const char *data, GError ** error)
110 {
111     if (data == NULL)
112     {
113         g_set_error (error, MC_ERROR, 0, "mc_serialize_str(): Input data is NULL.");
114         return NULL;
115     }
116     return g_strdup_printf ("%c%zu" SRLZ_DELIM_S "%s", prefix, strlen (data), data);
117 }
118 
119 /* --------------------------------------------------------------------------------------------- */
120 /**
121  * Deserialize string to string object
122  *
123  * @param prefix prefix for deserailization
124  * @param data data for deserialization
125  * @param error contain pointer to object for handle error code and message
126  *
127  * @return newly allocated string
128  */
129 
130 #define FUNC_NAME "mc_serialize_str()"
131 char *
mc_deserialize_str(const char prefix,const char * data,GError ** error)132 mc_deserialize_str (const char prefix, const char *data, GError ** error)
133 {
134     size_t data_len;
135 
136     if ((data == NULL) || (*data == '\0'))
137     {
138         g_set_error (error, MC_ERROR, 0, FUNC_NAME ": Input data is NULL or empty.");
139         return NULL;
140     }
141 
142     if (*data != prefix)
143     {
144         g_set_error (error, MC_ERROR, 0, FUNC_NAME ": String prefix doesn't equal to '%c'", prefix);
145         return NULL;
146     }
147 
148     {
149         char buffer[BUF_TINY];
150         char *semi_ptr;
151         size_t semi_offset;
152 
153         semi_ptr = strchr (data + 1, SRLZ_DELIM_C);
154         if (semi_ptr == NULL)
155         {
156             g_set_error (error, MC_ERROR, 0,
157                          FUNC_NAME ": Length delimiter '%c' doesn't exists", SRLZ_DELIM_C);
158             return NULL;
159         }
160         semi_offset = semi_ptr - (data + 1);
161         if (semi_offset >= BUF_TINY)
162         {
163             g_set_error (error, MC_ERROR, 0, FUNC_NAME ": Too big string length");
164             return NULL;
165         }
166         strncpy (buffer, data + 1, semi_offset);
167         buffer[semi_offset] = '\0';
168         data_len = atol (buffer);
169         data += semi_offset + 2;
170     }
171 
172     if (data_len > strlen (data))
173     {
174         g_set_error (error, MC_ERROR, 0,
175                      FUNC_NAME
176                      ": Specified data length (%zu) is greater than actual data length (%zu)",
177                      data_len, strlen (data));
178         return NULL;
179     }
180     return g_strndup (data, data_len);
181 }
182 
183 #undef FUNC_NAME
184 
185 /* --------------------------------------------------------------------------------------------- */
186 /**
187  * Serialize mc_config_t object to string
188  *
189  * @param data data for serialization
190  * @param error contain pointer to object for handle error code and message
191  *
192  * @return serialized data as newly allocated string
193  */
194 
195 char *
mc_serialize_config(mc_config_t * data,GError ** error)196 mc_serialize_config (mc_config_t * data, GError ** error)
197 {
198     gchar **groups, **group_iterator;
199     GString *buffer;
200 
201     buffer = g_string_new ("");
202     groups = mc_config_get_groups (data, NULL);
203 
204     for (group_iterator = groups; *group_iterator != NULL; group_iterator++)
205     {
206         char *serialized_str;
207         gchar **params, **param_iterator;
208 
209         serialized_str = mc_serialize_str ('g', *group_iterator, error);
210         if (serialized_str == NULL)
211         {
212             g_string_free (buffer, TRUE);
213             g_strfreev (groups);
214             return NULL;
215         }
216         g_string_append (buffer, serialized_str);
217         g_free (serialized_str);
218 
219         params = mc_config_get_keys (data, *group_iterator, NULL);
220 
221         for (param_iterator = params; *param_iterator != NULL; param_iterator++)
222         {
223             char *value;
224 
225             serialized_str = mc_serialize_str ('p', *param_iterator, error);
226             if (serialized_str == NULL)
227             {
228                 g_string_free (buffer, TRUE);
229                 g_strfreev (params);
230                 g_strfreev (groups);
231                 return NULL;
232             }
233             g_string_append (buffer, serialized_str);
234             g_free (serialized_str);
235 
236             value = mc_config_get_string_raw (data, *group_iterator, *param_iterator, "");
237             serialized_str = mc_serialize_str ('v', value, error);
238             g_free (value);
239 
240             if (serialized_str == NULL)
241             {
242                 g_string_free (buffer, TRUE);
243                 g_strfreev (params);
244                 g_strfreev (groups);
245                 return NULL;
246             }
247 
248             g_string_append (buffer, serialized_str);
249             g_free (serialized_str);
250         }
251 
252         g_strfreev (params);
253     }
254 
255     g_strfreev (groups);
256 
257     return g_string_free (buffer, FALSE);
258 }
259 
260 /* --------------------------------------------------------------------------------------------- */
261 /**
262  * Deserialize string to mc_config_t object
263  *
264  * @param data data for serialization
265  * @param error contain pointer to object for handle error code and message
266  *
267  * @return newly allocated mc_config_t object
268  */
269 
270 #define FUNC_NAME "mc_deserialize_config()"
271 #define prepend_error_and_exit() { \
272     prepend_error_message (error, FUNC_NAME " at %zu", current_position + 1); \
273                 mc_config_deinit (ret_data); \
274                 return NULL; \
275 }
276 
277 mc_config_t *
mc_deserialize_config(const char * data,GError ** error)278 mc_deserialize_config (const char *data, GError ** error)
279 {
280     char *current_group = NULL, *current_param = NULL, *current_value = NULL;
281     size_t current_position = 0;
282     mc_config_t *ret_data;
283     enum automat_status
284     {
285         WAIT_GROUP,
286         WAIT_PARAM,
287         WAIT_VALUE
288     } current_status = WAIT_GROUP;
289 
290     ret_data = mc_config_init (NULL, FALSE);
291 
292     while (data != NULL)
293     {
294         if ((current_status == WAIT_GROUP) && (*data == 'p') && (current_group != NULL))
295             current_status = WAIT_PARAM;
296 
297         switch (current_status)
298         {
299         case WAIT_GROUP:
300             g_free (current_group);
301 
302             current_group = mc_deserialize_str ('g', data, error);
303             if (current_group == NULL)
304                 prepend_error_and_exit ();
305 
306             data = go_to_end_of_serialized_string (data, current_group, &current_position);
307             current_status = WAIT_PARAM;
308             break;
309         case WAIT_PARAM:
310             g_free (current_param);
311 
312             current_param = mc_deserialize_str ('p', data, error);
313             if (current_param == NULL)
314             {
315                 g_free (current_group);
316                 prepend_error_and_exit ();
317             }
318 
319             data = go_to_end_of_serialized_string (data, current_param, &current_position);
320             current_status = WAIT_VALUE;
321             break;
322         case WAIT_VALUE:
323             current_value = mc_deserialize_str ('v', data, error);
324             if (current_value == NULL)
325             {
326                 g_free (current_group);
327                 g_free (current_param);
328                 prepend_error_and_exit ();
329             }
330             mc_config_set_string (ret_data, current_group, current_param, current_value);
331 
332             data = go_to_end_of_serialized_string (data, current_value, &current_position);
333             g_free (current_value);
334             current_status = WAIT_GROUP;
335             break;
336         default:
337             break;
338         }
339     }
340     g_free (current_group);
341     g_free (current_param);
342 
343     return ret_data;
344 }
345 
346 #undef FUNC_NAME
347 
348 /* --------------------------------------------------------------------------------------------- */
349