1 /*
2  * e-credentials.c
3  *
4  * Copyright (C) 2011 Red Hat, Inc. (www.redhat.com)
5  *
6  * This library is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation.
9  *
10  * This library is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
13  * for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with this library. If not, see <http://www.gnu.org/licenses/>.
17  *
18  */
19 
20 #include "evolution-data-server-config.h"
21 
22 #include <stdio.h>
23 #include <string.h>
24 
25 #include "e-data-server-util.h"
26 
27 #include "e-credentials.h"
28 
29 struct _ECredentialsPrivate
30 {
31 	GHashTable *keys;
32 	GHashTable *peek_keys;
33 };
34 
35 static gboolean
key_equal(gconstpointer str1,gconstpointer str2)36 key_equal (gconstpointer str1,
37            gconstpointer str2)
38 {
39 	g_return_val_if_fail (str1 != NULL, FALSE);
40 	g_return_val_if_fail (str2 != NULL, FALSE);
41 
42 	if (str1 == str2)
43 		return TRUE;
44 
45 	return g_ascii_strcasecmp (str1, str2) == 0;
46 }
47 
48 /**
49  * e_credentials_new:
50  *
51  * Returns: (transfer full): a new empty #ECredentials. Free with e_credentials_free() when done with it.
52  *
53  * Since: 3.2
54  **/
55 ECredentials *
e_credentials_new(void)56 e_credentials_new (void)
57 {
58 	ECredentials *credentials;
59 
60 	credentials = g_slice_new0 (ECredentials);
61 	credentials->priv = g_slice_new0 (ECredentialsPrivate);
62 	credentials->priv->keys = g_hash_table_new_full (g_str_hash, key_equal, g_free, (GDestroyNotify) e_credentials_util_safe_free_string);
63 	credentials->priv->peek_keys = g_hash_table_new_full (g_str_hash, key_equal, g_free, (GDestroyNotify) e_credentials_util_safe_free_string);
64 
65 	return credentials;
66 }
67 
68 /**
69  * e_credentials_new_strv:
70  * @strv: an array of key/value-s to prefill
71  *
72  * Creates a new #ECredentials with prefilled key/value-s from @strv. It expects
73  * the @strv as a NULL-terminated list of strings "key:encoded_value".
74  * The same can be returned from e_credentials_to_strv ().
75  *
76  * Returns: (transfer full): a new #ECredentials. Free with e_credentials_free() when done with it.
77  *
78  * Since: 3.2
79  **/
80 ECredentials *
e_credentials_new_strv(const gchar * const * strv)81 e_credentials_new_strv (const gchar * const *strv)
82 {
83 	ECredentials *credentials;
84 	gint ii;
85 
86 	g_return_val_if_fail (strv != NULL, NULL);
87 
88 	credentials = e_credentials_new ();
89 
90 	for (ii = 0; strv[ii]; ii++) {
91 		const gchar *key = strv[ii], *sep;
92 
93 		sep = strchr (key, ':');
94 
95 		/* skip empty and invalid values */
96 		if (sep)
97 			g_hash_table_insert (credentials->priv->keys, g_strndup (key, sep - key), g_strdup (sep + 1));
98 	}
99 
100 	return credentials;
101 }
102 
103 /**
104  * e_credentials_new_args:
105  * @key: the first key name
106  * @...: value, followed by key,value pairs, terminated with %NULL
107  *
108  * Creates a new #ECredentials with prefilled keys. The arguments is
109  * a NULL-terminated list of string pairs &lt;key, value&gt;; value is in a clear form.
110  *
111  * Returns: (transfer full): a new #ECredentials. Free with e_credentials_free() when done with it.
112  *
113  * Since: 3.2
114  **/
115 ECredentials *
e_credentials_new_args(const gchar * key,...)116 e_credentials_new_args (const gchar *key,
117                         ...)
118 {
119 	ECredentials *credentials;
120 	va_list va;
121 
122 	/* NULL-terminated list of string pairs <key, value>; value is
123 	 * in a clear form. */
124 
125 	g_return_val_if_fail (key != NULL, NULL);
126 
127 	credentials = e_credentials_new ();
128 
129 	va_start (va, key);
130 
131 	while (key) {
132 		const gchar *value = va_arg (va, const gchar *);
133 
134 		if (key && *key && value && *value)
135 			e_credentials_set (credentials, key, value);
136 
137 		key = va_arg (va, const gchar *);
138 	}
139 
140 	va_end (va);
141 
142 	return credentials;
143 }
144 
145 static void
copy_keys_cb(gpointer key,gpointer value,gpointer hash_table)146 copy_keys_cb (gpointer key,
147               gpointer value,
148               gpointer hash_table)
149 {
150 	g_hash_table_insert (hash_table, g_strdup (key), g_strdup (value));
151 }
152 
153 /**
154  * e_credentials_new_clone:
155  * @credentials: an #ECredentials
156  *
157  * Returns: (transfer full): Creates a clone (copy) of the given @credentials.
158  *   Free with e_credentials_free() when done with it.
159  *
160  * Since: 3.2
161  **/
162 ECredentials *
e_credentials_new_clone(const ECredentials * credentials)163 e_credentials_new_clone (const ECredentials *credentials)
164 {
165 	ECredentials *res;
166 
167 	g_return_val_if_fail (credentials != NULL, NULL);
168 	g_return_val_if_fail (credentials->priv != NULL, NULL);
169 	g_return_val_if_fail (credentials->priv->keys != NULL, NULL);
170 
171 	res = e_credentials_new ();
172 
173 	g_hash_table_foreach (credentials->priv->keys, copy_keys_cb, res->priv->keys);
174 
175 	return res;
176 }
177 
178 /**
179  * e_credentials_free:
180  * @credentials: an #ECredentials
181  *
182  * Frees the @credentials. Any peek-ed values are invalidated
183  * by this call.
184  *
185  * Since: 3.2
186  **/
187 void
e_credentials_free(ECredentials * credentials)188 e_credentials_free (ECredentials *credentials)
189 {
190 	if (!credentials)
191 		return;
192 
193 	g_return_if_fail (credentials->priv != NULL);
194 
195 	g_hash_table_destroy (credentials->priv->keys);
196 	g_hash_table_destroy (credentials->priv->peek_keys);
197 	g_slice_free (ECredentialsPrivate, credentials->priv);
198 	g_slice_free (ECredentials, credentials);
199 }
200 
201 static void
add_to_array_cb(gpointer key,gpointer value,gpointer ptr_array)202 add_to_array_cb (gpointer key,
203                  gpointer value,
204                  gpointer ptr_array)
205 {
206 	if (key && value && ptr_array) {
207 		gchar *str = g_strconcat (key, ":", value, NULL);
208 
209 		g_ptr_array_add (ptr_array, e_util_utf8_make_valid (str));
210 
211 		g_free (str);
212 	}
213 }
214 
215 /**
216  * e_credentials_to_strv:
217  * @credentials: an #ECredentials
218  *
219  * Returns NULL-terminated array of strings with keys and encoded values;
220  * To read them back pass this pointer to e_credentials_new(). As it returns
221  * newly allocated string then this should be freed with g_strfreev() when no
222  * longer needed.
223  *
224  * Returns: (transfer full): a NULL-terminated array of key/value strings
225  *
226  * Since: 3.2
227  **/
228 gchar **
e_credentials_to_strv(const ECredentials * credentials)229 e_credentials_to_strv (const ECredentials *credentials)
230 {
231 	GPtrArray *array;
232 
233 	g_return_val_if_fail (credentials != NULL, NULL);
234 	g_return_val_if_fail (credentials->priv != NULL, NULL);
235 	g_return_val_if_fail (credentials->priv->keys != NULL, NULL);
236 
237 	array = g_ptr_array_sized_new (g_hash_table_size (credentials->priv->keys) + 1);
238 
239 	g_hash_table_foreach (credentials->priv->keys, add_to_array_cb, array);
240 
241 	/* NULL-terminated */
242 	g_ptr_array_add (array, NULL);
243 
244 	return (gchar **) g_ptr_array_free (array, FALSE);
245 }
246 
247 static gchar *
encode_string(const gchar * decoded)248 encode_string (const gchar *decoded)
249 {
250 	gsize len, ii;
251 	guchar xval, *copy;
252 	gchar *res;
253 
254 	if (!decoded || !*decoded)
255 		return NULL;
256 
257 	copy = (guchar *) g_strdup (decoded);
258 	len = strlen ((const gchar *) copy);
259 
260 	xval = 17;
261 	for (ii = 0; ii < len; ii++) {
262 		copy[ii] = copy[ii] ^ xval;
263 		xval += 17;
264 	}
265 
266 	res = g_base64_encode (copy, len);
267 
268 	g_free (copy);
269 
270 	return res;
271 }
272 
273 static gchar *
decode_string(const gchar * encoded)274 decode_string (const gchar *encoded)
275 {
276 	guchar *data, xval;
277 	gsize len = 0, ii;
278 	gchar *res;
279 
280 	g_return_val_if_fail (encoded != NULL, NULL);
281 	g_return_val_if_fail (*encoded, NULL);
282 
283 	data = g_base64_decode (encoded, &len);
284 	g_return_val_if_fail (data != NULL, NULL);
285 	g_return_val_if_fail (len > 0, NULL);
286 
287 	xval = 17;
288 	for (ii = 0; ii < len; ii++) {
289 		data[ii] = data[ii] ^ xval;
290 		xval += 17;
291 	}
292 
293 	res = g_strndup ((const gchar *) data, len);
294 
295 	e_credentials_util_safe_free_string ((gchar *) data);
296 
297 	return res;
298 }
299 
300 /**
301  * e_credentials_set:
302  * @credentials: an #ECredentials
303  * @key: a key string
304  * @value: a value string
305  *
306  * Sets value for @key, if @value is %NULL or an empty string then @key is
307  * removed.  The value is supposed to be in a clear form (unencoded).
308  * @key cannot contain colon.
309  *
310  * Since: 3.2
311  **/
312 void
e_credentials_set(ECredentials * credentials,const gchar * key,const gchar * value)313 e_credentials_set (ECredentials *credentials,
314                    const gchar *key,
315                    const gchar *value)
316 {
317 	g_return_if_fail (credentials != NULL);
318 	g_return_if_fail (credentials->priv != NULL);
319 	g_return_if_fail (credentials->priv->keys != NULL);
320 	g_return_if_fail (credentials->priv->peek_keys != NULL);
321 	g_return_if_fail (key != NULL);
322 	g_return_if_fail (*key);
323 	g_return_if_fail (strchr (key, ':') == NULL);
324 
325 	g_hash_table_remove (credentials->priv->peek_keys, key);
326 
327 	if (!value) {
328 		g_hash_table_remove (credentials->priv->keys, key);
329 	} else {
330 		g_hash_table_insert (credentials->priv->keys, g_strdup (key), encode_string (value));
331 	}
332 }
333 
334 /**
335  * e_credentials_get:
336  * @credentials: an #ECredentials
337  * @key: a key name
338  *
339  * Returns: (transfer full) (nullable): A copy of the key's value, or %NULL,
340  *   if no such key is stored in the @credentuals. Free returned string with
341  *   e_credentials_util_safe_free_string(), when done with it.
342  *
343  * Since: 3.2
344  **/
345 gchar *
e_credentials_get(const ECredentials * credentials,const gchar * key)346 e_credentials_get (const ECredentials *credentials,
347                    const gchar *key)
348 {
349 	const gchar *stored;
350 
351 	/* Returned pointer should be freed with
352 	 * e_credentials_util_safe_free_string() when no longer needed. */
353 
354 	g_return_val_if_fail (credentials != NULL, NULL);
355 	g_return_val_if_fail (credentials->priv != NULL, NULL);
356 	g_return_val_if_fail (credentials->priv->keys != NULL, NULL);
357 	g_return_val_if_fail (key != NULL, NULL);
358 	g_return_val_if_fail (*key, NULL);
359 
360 	stored = g_hash_table_lookup (credentials->priv->keys, key);
361 	if (!stored)
362 		return NULL;
363 
364 	return decode_string (stored);
365 }
366 
367 /**
368  * e_credentials_peek:
369  * @credentials: an #ECredentials
370  * @key: a key string
371  *
372  * Peeks at the value for @key, in a clear form. The returned value is valid
373  * until free of the @credentials structure or until the key value is rewritten
374  * by e_credentials_set().
375  *
376  * Returns: the value for @key
377  *
378  * Since: 3.2
379  **/
380 const gchar *
e_credentials_peek(ECredentials * credentials,const gchar * key)381 e_credentials_peek (ECredentials *credentials,
382                     const gchar *key)
383 {
384 	gchar *value;
385 
386 	g_return_val_if_fail (credentials != NULL, NULL);
387 	g_return_val_if_fail (credentials->priv != NULL, NULL);
388 	g_return_val_if_fail (credentials->priv->peek_keys != NULL, NULL);
389 	g_return_val_if_fail (key != NULL, NULL);
390 	g_return_val_if_fail (*key, NULL);
391 
392 	value = g_hash_table_lookup (credentials->priv->peek_keys, key);
393 	if (value)
394 		return value;
395 
396 	value = e_credentials_get (credentials, key);
397 	if (value)
398 		g_hash_table_insert (credentials->priv->peek_keys, g_strdup (key), value);
399 
400 	return value;
401 }
402 
403 struct equal_data
404 {
405 	gboolean equal;
406 	GHashTable *keys;
407 };
408 
409 static void
check_equal_cb(gpointer key,gpointer value,gpointer user_data)410 check_equal_cb (gpointer key,
411                 gpointer value,
412                 gpointer user_data)
413 {
414 	struct equal_data *ed = user_data;
415 
416 	g_return_if_fail (ed != NULL);
417 	g_return_if_fail (ed->keys != NULL);
418 	g_return_if_fail (key != NULL);
419 	g_return_if_fail (value != NULL);
420 
421 	ed->equal = ed->equal && g_strcmp0 (value, g_hash_table_lookup (ed->keys, key)) == 0;
422 }
423 
424 /**
425  * e_credentials_equal:
426  * @credentials1: an #ECredentials
427  * @credentials2: another #ECredentials
428  *
429  * Returns whether two #ECredentials structures contain the same keys with
430  * same values.
431  *
432  * Returns: %TRUE if they are equal, %FALSE otherwise
433  *
434  * Since: 3.2
435  **/
436 gboolean
e_credentials_equal(const ECredentials * credentials1,const ECredentials * credentials2)437 e_credentials_equal (const ECredentials *credentials1,
438                      const ECredentials *credentials2)
439 {
440 	struct equal_data ed;
441 
442 	if (!credentials1 && !credentials2)
443 		return TRUE;
444 
445 	if (credentials1 == credentials2)
446 		return TRUE;
447 
448 	if (!credentials1 || !credentials2)
449 		return FALSE;
450 
451 	g_return_val_if_fail (credentials1->priv != NULL, FALSE);
452 	g_return_val_if_fail (credentials1->priv->keys != NULL, FALSE);
453 	g_return_val_if_fail (credentials2->priv != NULL, FALSE);
454 	g_return_val_if_fail (credentials2->priv->keys != NULL, FALSE);
455 
456 	if (g_hash_table_size (credentials1->priv->keys) != g_hash_table_size (credentials2->priv->keys))
457 		return FALSE;
458 
459 	ed.equal = TRUE;
460 	ed.keys = credentials2->priv->keys;
461 
462 	g_hash_table_foreach (credentials1->priv->keys, check_equal_cb, &ed);
463 
464 	return ed.equal;
465 }
466 
467 /**
468  * e_credentials_equal_keys:
469  * @credentials1: an #ECredentials
470  * @credentials2: another #ECredentials
471  * @key1: the first key name
472  * @...: other key names, terminated with %NULL
473  *
474  * Returns whether two #ECredentials structures have the same keys. Key names
475  * are NULL-terminated.
476  *
477  * Returns: %TRUE if the key sets match, %FALSE otherwise
478  *
479  * Since: 3.2
480  **/
481 gboolean
e_credentials_equal_keys(const ECredentials * credentials1,const ECredentials * credentials2,const gchar * key1,...)482 e_credentials_equal_keys (const ECredentials *credentials1,
483                           const ECredentials *credentials2,
484                           const gchar *key1,
485                           ...)
486 {
487 	va_list va;
488 	gboolean equal = TRUE;
489 
490 	g_return_val_if_fail (credentials1 != NULL, FALSE);
491 	g_return_val_if_fail (credentials1->priv != NULL, FALSE);
492 	g_return_val_if_fail (credentials1->priv->keys != NULL, FALSE);
493 	g_return_val_if_fail (credentials2 != NULL, FALSE);
494 	g_return_val_if_fail (credentials2->priv != NULL, FALSE);
495 	g_return_val_if_fail (credentials2->priv->keys != NULL, FALSE);
496 	g_return_val_if_fail (key1 != NULL, FALSE);
497 
498 	va_start (va, key1);
499 
500 	while (key1 && equal) {
501 		equal = g_strcmp0 (g_hash_table_lookup (credentials1->priv->keys, key1), g_hash_table_lookup (credentials2->priv->keys, key1)) == 0;
502 
503 		key1 = va_arg (va, const gchar *);
504 	}
505 
506 	va_end (va);
507 
508 	return equal;
509 }
510 
511 /**
512  * e_credentials_has_key:
513  * @credentials: an #ECredentials
514  * @key: a key string
515  *
516  * Returns whether @credentials contains @key.
517  *
518  * Returns: %TRUE if @credentials contains @key, %FALSE otherwise
519  *
520  * Since: 3.2
521  **/
522 gboolean
e_credentials_has_key(const ECredentials * credentials,const gchar * key)523 e_credentials_has_key (const ECredentials *credentials,
524                        const gchar *key)
525 {
526 	g_return_val_if_fail (credentials != NULL, FALSE);
527 	g_return_val_if_fail (credentials->priv != NULL, FALSE);
528 	g_return_val_if_fail (credentials->priv->keys != NULL, FALSE);
529 	g_return_val_if_fail (key != NULL, FALSE);
530 	g_return_val_if_fail (*key, FALSE);
531 
532 	return g_hash_table_lookup (credentials->priv->keys, key) != NULL;
533 }
534 
535 /**
536  * e_credentials_keys_size:
537  * @credentials: an #ECredentials
538  *
539  * Returns the number of keys in @credentials.
540  *
541  * Returns: the number of keys in @credentials
542  *
543  * Since: 3.2
544  **/
545 guint
e_credentials_keys_size(const ECredentials * credentials)546 e_credentials_keys_size (const ECredentials *credentials)
547 {
548 	g_return_val_if_fail (credentials != NULL, 0);
549 	g_return_val_if_fail (credentials->priv != NULL, 0);
550 	g_return_val_if_fail (credentials->priv->keys != NULL, 0);
551 
552 	return g_hash_table_size (credentials->priv->keys);
553 }
554 
555 static void
gather_key_names(gpointer key,gpointer value,gpointer pslist)556 gather_key_names (gpointer key,
557                   gpointer value,
558                   gpointer pslist)
559 {
560 	GSList **slist = pslist;
561 
562 	g_return_if_fail (pslist != NULL);
563 	g_return_if_fail (key != NULL);
564 
565 	*slist = g_slist_prepend (*slist, key);
566 }
567 
568 /**
569  * e_credentials_list_keys:
570  * @credentials: an #ECredentials
571  *
572  * Returns a newly-allocated #GSList of key names stored in @credentials.
573  * The key names are internal credentials values and should not be modified
574  * or freed.  Free the list with g_slist_free() when no longer needed.
575  *
576  * Returns: (transfer container) (element-type utf8): a newly-allocated #GSList
577  * of key names
578  *
579  * Since: 3.2
580  **/
581 GSList *
e_credentials_list_keys(const ECredentials * credentials)582 e_credentials_list_keys (const ECredentials *credentials)
583 {
584 	GSList *keys = NULL;
585 
586 	g_return_val_if_fail (credentials != NULL, NULL);
587 	g_return_val_if_fail (credentials->priv != NULL, NULL);
588 	g_return_val_if_fail (credentials->priv->keys != NULL, NULL);
589 
590 	/* XXX g_hash_table_get_keys() would have been
591 	 *     easier had we used #GList instead. */
592 	g_hash_table_foreach (credentials->priv->keys, gather_key_names, &keys);
593 
594 	return g_slist_reverse (keys);
595 }
596 
597 /**
598  * e_credentials_clear:
599  * @credentials: an #ECredentials
600  *
601  * Clears all the stored keys (and their peek-ed values).
602  *
603  * Since: 3.2
604  **/
605 void
e_credentials_clear(ECredentials * credentials)606 e_credentials_clear (ECredentials *credentials)
607 {
608 	g_return_if_fail (credentials != NULL);
609 	g_return_if_fail (credentials->priv != NULL);
610 	g_return_if_fail (credentials->priv->keys != NULL);
611 	g_return_if_fail (credentials->priv->peek_keys != NULL);
612 
613 	g_hash_table_remove_all (credentials->priv->peek_keys);
614 	g_hash_table_remove_all (credentials->priv->keys);
615 }
616 
617 /**
618  * e_credentials_clear_peek:
619  * @credentials: an #ECredentials
620  *
621  * Clears cache of peek-ed values.
622  *
623  * Since: 3.2
624  **/
625 void
e_credentials_clear_peek(ECredentials * credentials)626 e_credentials_clear_peek (ECredentials *credentials)
627 {
628 	g_return_if_fail (credentials != NULL);
629 	g_return_if_fail (credentials->priv != NULL);
630 	g_return_if_fail (credentials->priv->peek_keys != NULL);
631 
632 	g_hash_table_remove_all (credentials->priv->peek_keys);
633 }
634 
635 /**
636  * e_credentials_util_safe_free_string:
637  * @str: (nullable): a string to safely free
638  *
639  * Frees a nul-terminated string safely, which means it
640  * overwrites the content first and only then calls g_free()
641  * on it. If @str is %NULL, then does nothing.
642  *
643  * Since: 3.2
644  **/
645 void
e_credentials_util_safe_free_string(gchar * str)646 e_credentials_util_safe_free_string (gchar *str)
647 {
648 	if (!str)
649 		return;
650 
651 	if (*str)
652 		memset (str, 0, sizeof (gchar) * strlen (str));
653 
654 	g_free (str);
655 }
656 
657 static struct _PromptFlags {
658 	ECredentialsPromptFlags flag_uint;
659 	const gchar *flag_string;
660 	gboolean is_bit_flag; /* if false, then checked against E_CREDENTIALS_PROMPT_FLAG_REMEMBER_MASK */
661 } PromptFlags[] = {
662 	{ E_CREDENTIALS_PROMPT_FLAG_REMEMBER_NEVER,	"remember-never",	FALSE },
663 	{ E_CREDENTIALS_PROMPT_FLAG_REMEMBER_SESSION,	"remember-session",	FALSE },
664 	{ E_CREDENTIALS_PROMPT_FLAG_REMEMBER_FOREVER,	"remember-forever",	FALSE },
665 
666 	{ E_CREDENTIALS_PROMPT_FLAG_SECRET,		"secret",		TRUE },
667 	{ E_CREDENTIALS_PROMPT_FLAG_REPROMPT,		"reprompt",		TRUE },
668 	{ E_CREDENTIALS_PROMPT_FLAG_ONLINE,		"online",		TRUE },
669 	{ E_CREDENTIALS_PROMPT_FLAG_DISABLE_REMEMBER,	"disable-remember",	TRUE },
670 	{ E_CREDENTIALS_PROMPT_FLAG_PASSPHRASE,		"passphrase",		TRUE }
671 };
672 
673 /**
674  * e_credentials_util_prompt_flags_to_string:
675  * @prompt_flags: bit-or of #ECredentialsPromptFlags
676  *
677  * Converts bit-or of # to a string representation. Use
678  * e_credentials_util_string_to_prompt_flags() to convert
679  * the string back to the numeric representation.
680  *
681  * Returns: (transfer full): text representation of the @prompt_flags
682  *
683  * Since: 3.2
684  **/
685 gchar *
e_credentials_util_prompt_flags_to_string(guint prompt_flags)686 e_credentials_util_prompt_flags_to_string (guint prompt_flags)
687 {
688 	gint ii;
689 	guint masked = prompt_flags & E_CREDENTIALS_PROMPT_FLAG_REMEMBER_MASK;
690 	GString *str = g_string_new ("");
691 
692 	/* Returned pointer can be passed to
693 	 * e_credentials_util_string_to prompt_flags() to decode
694 	 * it back to flags. Free returned pointer with g_free(). */
695 
696 	for (ii = 0; ii < G_N_ELEMENTS (PromptFlags); ii++) {
697 		const gchar *add = NULL;
698 
699 		if (PromptFlags[ii].is_bit_flag) {
700 			if ((prompt_flags & PromptFlags[ii].flag_uint) != 0)
701 				add = PromptFlags[ii].flag_string;
702 		} else if (masked == PromptFlags[ii].flag_uint) {
703 			add = PromptFlags[ii].flag_string;
704 		}
705 
706 		if (!add)
707 			continue;
708 
709 		if (str->len)
710 			g_string_append_c (str, ',');
711 
712 		g_string_append (str, add);
713 	}
714 
715 	return g_string_free (str, FALSE);
716 }
717 
718 /**
719  * e_credentials_util_string_to_prompt_flags:
720  * @prompt_flags_string: flags encoded as string
721  *
722  * Converts #ECredentialsPromptFlags represented as string back to
723  * the numeric representation. This is a reverse function of
724  * e_credentials_util_prompt_flags_to_string().
725  *
726  * Returns: decoded bit-or of #ECredentialsPromptFlags
727  *
728  * Since: 3.2
729  **/
730 guint
e_credentials_util_string_to_prompt_flags(const gchar * prompt_flags_string)731 e_credentials_util_string_to_prompt_flags (const gchar *prompt_flags_string)
732 {
733 	gchar **strv;
734 	gint ii, jj;
735 	guint flags = 0;
736 
737 	if (!prompt_flags_string || !*prompt_flags_string)
738 		return flags;
739 
740 	strv = g_strsplit (prompt_flags_string, ",", -1);
741 	if (!strv)
742 		return flags;
743 
744 	for (jj = 0; strv[jj]; jj++) {
745 		const gchar *str = strv[jj];
746 
747 		for (ii = 0; ii < G_N_ELEMENTS (PromptFlags); ii++) {
748 			if (g_str_equal (PromptFlags[ii].flag_string, str)) {
749 				if (PromptFlags[ii].is_bit_flag)
750 					flags |= PromptFlags[ii].flag_uint;
751 				else
752 					flags = (flags & (~E_CREDENTIALS_PROMPT_FLAG_REMEMBER_MASK)) | PromptFlags[ii].flag_uint;
753 			}
754 		}
755 	}
756 
757 	g_strfreev (strv);
758 
759 	return flags;
760 }
761