4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) version 3.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with the program; if not, see <http://www.gnu.org/licenses/>
18 * Copyright (C) 2011 Red Hat, Inc. (www.redhat.com)
29 #include "e-data-server-util.h"
31 #include "e-credentials.h"
33 struct _ECredentialsPrivate
36 GHashTable *peek_keys;
40 key_equal (gconstpointer str1,
43 g_return_val_if_fail (str1 != NULL, FALSE);
44 g_return_val_if_fail (str2 != NULL, FALSE);
49 return g_ascii_strcasecmp (str1, str2) == 0;
60 e_credentials_new (void)
62 ECredentials *credentials;
64 credentials = g_new0 (ECredentials, 1);
65 credentials->priv = g_new0 (ECredentialsPrivate, 1);
66 credentials->priv->keys = g_hash_table_new_full (g_str_hash, key_equal, g_free, (GDestroyNotify) e_credentials_util_safe_free_string);
67 credentials->priv->peek_keys = g_hash_table_new_full (g_str_hash, key_equal, g_free, (GDestroyNotify) e_credentials_util_safe_free_string);
73 * e_credentials_new_strv:
80 e_credentials_new_strv (const gchar * const *keys)
82 ECredentials *credentials;
85 /* Expects @keys as NULL terminated list of strings
86 * "key:encoded_value". The same can be returned from
87 * e_credentials_to_strv (). */
89 g_return_val_if_fail (keys != NULL, NULL);
91 credentials = e_credentials_new ();
93 for (ii = 0; keys[ii]; ii++) {
94 const gchar *key = keys[ii], *sep;
96 sep = strchr (key, ':');
98 /* skip empty and invalid values */
100 g_hash_table_insert (credentials->priv->keys, g_strndup (key, sep - key), g_strdup (sep + 1));
107 * e_credentials_new_args:
109 * FIXME: Document me.
114 e_credentials_new_args (const gchar *key,
117 ECredentials *credentials;
120 /* NULL-terminated list of string pairs <key, value>; value is
121 * in a clear form. */
123 g_return_val_if_fail (key != NULL, NULL);
125 credentials = e_credentials_new ();
130 const gchar *value = va_arg (va, const gchar *);
132 if (key && *key && value && *value)
133 e_credentials_set (credentials, key, value);
135 key = va_arg (va, const gchar *);
144 copy_keys_cb (gpointer key,
148 g_hash_table_insert (hash_table, g_strdup (key), g_strdup (value));
152 * e_credentials_new_clone:
154 * FIXME: Document me.
159 e_credentials_new_clone (const ECredentials *credentials)
163 g_return_val_if_fail (credentials != NULL, NULL);
164 g_return_val_if_fail (credentials->priv != NULL, NULL);
165 g_return_val_if_fail (credentials->priv->keys != NULL, NULL);
167 res = e_credentials_new ();
169 g_hash_table_foreach (credentials->priv->keys, copy_keys_cb, res->priv->keys);
175 * e_credentials_free:
177 * FIXME: Document me.
182 e_credentials_free (ECredentials *credentials)
187 g_return_if_fail (credentials->priv != NULL);
189 g_hash_table_destroy (credentials->priv->keys);
190 g_hash_table_destroy (credentials->priv->peek_keys);
191 g_free (credentials->priv);
192 g_free (credentials);
196 add_to_array_cb (gpointer key,
200 if (key && value && ptr_array) {
201 gchar *str = g_strconcat (key, ":", value, NULL);
203 g_ptr_array_add (ptr_array, e_util_utf8_make_valid (str));
210 * e_credentials_to_strv:
211 * @credentials: an #ECredentials
213 * Returns %NULL-terminated array of strings with keys and encoded values;
214 * To read them back pass this pointer to e_credentials_new(). As it returns
215 * newly allocated string then this should be freed with g_strfreev() when no
218 * Returns: (transfer full): a %NULL-terminated array of key/value strings
223 e_credentials_to_strv (const ECredentials *credentials)
227 g_return_val_if_fail (credentials != NULL, NULL);
228 g_return_val_if_fail (credentials->priv != NULL, NULL);
229 g_return_val_if_fail (credentials->priv->keys != NULL, NULL);
231 array = g_ptr_array_sized_new (g_hash_table_size (credentials->priv->keys) + 1);
233 g_hash_table_foreach (credentials->priv->keys, add_to_array_cb, array);
235 /* NULL-terminated */
236 g_ptr_array_add (array, NULL);
238 return (gchar **) g_ptr_array_free (array, FALSE);
242 encode_string (const gchar *decoded)
248 if (!decoded || !*decoded)
251 copy = (guchar *) g_strdup (decoded);
252 len = strlen ((const gchar *) copy);
255 for (ii = 0; ii < len; ii++) {
256 copy[ii] = copy[ii] ^ xval;
260 res = g_base64_encode (copy, len);
268 decode_string (const gchar *encoded)
274 g_return_val_if_fail (encoded != NULL, NULL);
275 g_return_val_if_fail (*encoded, NULL);
277 data = g_base64_decode (encoded, &len);
278 g_return_val_if_fail (data != NULL, NULL);
279 g_return_val_if_fail (len > 0, NULL);
282 for (ii = 0; ii < len; ii++) {
283 data[ii] = data[ii] ^ xval;
287 res = g_strndup ((const gchar *) data, len);
289 e_credentials_util_safe_free_string ((gchar *) data);
296 * @credentials: an #ECredentials
298 * @value: a value string
300 * Sets value for @key, if @value is %NULL or an empty string then @key is
301 * removed. The value is supposed to be in a clear form (unencoded).
302 * @key cannot contain colon.
307 e_credentials_set (ECredentials *credentials,
311 g_return_if_fail (credentials != NULL);
312 g_return_if_fail (credentials->priv != NULL);
313 g_return_if_fail (credentials->priv->keys != NULL);
314 g_return_if_fail (credentials->priv->peek_keys != NULL);
315 g_return_if_fail (key != NULL);
316 g_return_if_fail (*key);
317 g_return_if_fail (strchr (key, ':') == NULL);
319 g_hash_table_remove (credentials->priv->peek_keys, key);
322 g_hash_table_remove (credentials->priv->keys, key);
324 g_hash_table_insert (credentials->priv->keys, g_strdup (key), encode_string (value));
331 * FIXME: Document me.
336 e_credentials_get (const ECredentials *credentials,
341 /* Returned pointer should be freed with
342 * e_credentials_util_safe_free_string() when no longer needed. */
344 g_return_val_if_fail (credentials != NULL, NULL);
345 g_return_val_if_fail (credentials->priv != NULL, NULL);
346 g_return_val_if_fail (credentials->priv->keys != NULL, NULL);
347 g_return_val_if_fail (key != NULL, NULL);
348 g_return_val_if_fail (*key, NULL);
350 stored = g_hash_table_lookup (credentials->priv->keys, key);
354 return decode_string (stored);
358 * e_credentials_peek:
359 * @credentials: an #ECredentials
362 * Peeks at the value for @key, in a clear form. The returned value is valid
363 * until free of the @credentials structure or until the key value is rewritten
364 * by e_credentials_set().
366 * Returns: the value for @key
371 e_credentials_peek (ECredentials *credentials,
376 g_return_val_if_fail (credentials != NULL, NULL);
377 g_return_val_if_fail (credentials->priv != NULL, NULL);
378 g_return_val_if_fail (credentials->priv->peek_keys != NULL, NULL);
379 g_return_val_if_fail (key != NULL, NULL);
380 g_return_val_if_fail (*key, NULL);
382 value = g_hash_table_lookup (credentials->priv->peek_keys, key);
386 value = e_credentials_get (credentials, key);
388 g_hash_table_insert (credentials->priv->peek_keys, g_strdup (key), value);
400 check_equal_cb (gpointer key,
404 struct equal_data *ed = user_data;
406 g_return_if_fail (ed != NULL);
407 g_return_if_fail (ed->keys != NULL);
408 g_return_if_fail (key != NULL);
409 g_return_if_fail (value != NULL);
411 ed->equal = ed->equal && g_strcmp0 (value, g_hash_table_lookup (ed->keys, key)) == 0;
415 * e_credentials_equal:
416 * @credentials1: an #ECredentials
417 * @credentials2: another #ECredentials
419 * Returns whether two #ECredential structures contain the same keys with
422 * Returns: %TRUE if they are equal, %FALSE otherwise
427 e_credentials_equal (const ECredentials *credentials1,
428 const ECredentials *credentials2)
430 struct equal_data ed;
432 if (!credentials1 && !credentials2)
435 if (credentials1 == credentials2)
438 if (!credentials1 || !credentials2)
441 g_return_val_if_fail (credentials1->priv != NULL, FALSE);
442 g_return_val_if_fail (credentials1->priv->keys != NULL, FALSE);
443 g_return_val_if_fail (credentials2->priv != NULL, FALSE);
444 g_return_val_if_fail (credentials2->priv->keys != NULL, FALSE);
446 if (g_hash_table_size (credentials1->priv->keys) != g_hash_table_size (credentials2->priv->keys))
450 ed.keys = credentials2->priv->keys;
452 g_hash_table_foreach (credentials1->priv->keys, check_equal_cb, &ed);
458 * e_credentials_equal_keys:
459 * @credentials1: an #ECredentials
460 * @credentials2: another #ECredentials
462 * Returns whether two #ECredentials structures have the same keys. Key names
463 * are NULL-terminated.
465 * Returns: %TRUE if the key sets match, %FALSE otherwise
470 e_credentials_equal_keys (const ECredentials *credentials1,
471 const ECredentials *credentials2,
476 gboolean equal = TRUE;
478 g_return_val_if_fail (credentials1 != NULL, FALSE);
479 g_return_val_if_fail (credentials1->priv != NULL, FALSE);
480 g_return_val_if_fail (credentials1->priv->keys != NULL, FALSE);
481 g_return_val_if_fail (credentials2 != NULL, FALSE);
482 g_return_val_if_fail (credentials2->priv != NULL, FALSE);
483 g_return_val_if_fail (credentials2->priv->keys != NULL, FALSE);
484 g_return_val_if_fail (key1 != NULL, FALSE);
488 while (key1 && equal) {
489 equal = g_strcmp0 (g_hash_table_lookup (credentials1->priv->keys, key1), g_hash_table_lookup (credentials2->priv->keys, key1)) == 0;
491 key1 = va_arg (va, const gchar *);
500 * e_credentials_has_key:
501 * @credentials: an #ECredentials
504 * Returns whether @credentials contains @key.
506 * Returns: %TRUE if @credentials contains @key, %FALSE otherwise
511 e_credentials_has_key (const ECredentials *credentials,
514 g_return_val_if_fail (credentials != NULL, FALSE);
515 g_return_val_if_fail (credentials->priv != NULL, FALSE);
516 g_return_val_if_fail (credentials->priv->keys != NULL, FALSE);
517 g_return_val_if_fail (key != NULL, FALSE);
518 g_return_val_if_fail (*key, FALSE);
520 return g_hash_table_lookup (credentials->priv->keys, key) != NULL;
524 * e_credentials_keys_size:
525 * @credentials: an #ECredentials
527 * Returns the number of keys in @credentials.
529 * Returns: the number of keys in @credentials
534 e_credentials_keys_size (const ECredentials *credentials)
536 g_return_val_if_fail (credentials != NULL, 0);
537 g_return_val_if_fail (credentials->priv != NULL, 0);
538 g_return_val_if_fail (credentials->priv->keys != NULL, 0);
540 return g_hash_table_size (credentials->priv->keys);
544 gather_key_names (gpointer key,
548 GSList **slist = pslist;
550 g_return_if_fail (pslist != NULL);
551 g_return_if_fail (key != NULL);
553 *slist = g_slist_prepend (*slist, key);
557 * e_credentials_list_keys:
558 * @credentials: an #ECredentials
560 * Returns a newly-allocated #GSList of key names stored in @credentials.
561 * The key names are internal credentials values and should not be modified
562 * or freed. Free the list with g_slist_free() when no longer needed.
564 * Returns: (transfer container) (element-type utf8): a newly-allocated #GSList
570 e_credentials_list_keys (const ECredentials *credentials)
574 g_return_val_if_fail (credentials != NULL, NULL);
575 g_return_val_if_fail (credentials->priv != NULL, NULL);
576 g_return_val_if_fail (credentials->priv->keys != NULL, NULL);
578 /* XXX g_hash_table_get_keys() would have been
579 * easier had we used #GList instead. */
580 g_hash_table_foreach (credentials->priv->keys, gather_key_names, &keys);
582 return g_slist_reverse (keys);
586 * e_credentials_clear:
588 * FIXME: Document me.
593 e_credentials_clear (ECredentials *credentials)
595 g_return_if_fail (credentials != NULL);
596 g_return_if_fail (credentials->priv != NULL);
597 g_return_if_fail (credentials->priv->keys != NULL);
598 g_return_if_fail (credentials->priv->peek_keys != NULL);
600 g_hash_table_remove_all (credentials->priv->peek_keys);
601 g_hash_table_remove_all (credentials->priv->keys);
605 * e_credentials_clear_peek:
607 * FIXME: Document me.
612 e_credentials_clear_peek (ECredentials *credentials)
614 g_return_if_fail (credentials != NULL);
615 g_return_if_fail (credentials->priv != NULL);
616 g_return_if_fail (credentials->priv->peek_keys != NULL);
618 g_hash_table_remove_all (credentials->priv->peek_keys);
622 * e_credentials_util_safe_free_string:
629 e_credentials_util_safe_free_string (gchar *str)
635 memset (str, 0, sizeof (gchar) * strlen (str));
640 static struct _PromptFlags {
641 ECredentialsPromptFlags flag_uint;
642 const gchar *flag_string;
643 gboolean is_bit_flag; /* if false, then checked against E_CREDENTIALS_PROMPT_FLAG_REMEMBER_MASK */
645 { E_CREDENTIALS_PROMPT_FLAG_REMEMBER_NEVER, "remember-never", FALSE },
646 { E_CREDENTIALS_PROMPT_FLAG_REMEMBER_SESSION, "remember-session", FALSE },
647 { E_CREDENTIALS_PROMPT_FLAG_REMEMBER_FOREVER, "remember-forever", FALSE },
649 { E_CREDENTIALS_PROMPT_FLAG_SECRET, "secret", TRUE },
650 { E_CREDENTIALS_PROMPT_FLAG_REPROMPT, "reprompt", TRUE },
651 { E_CREDENTIALS_PROMPT_FLAG_ONLINE, "online", TRUE },
652 { E_CREDENTIALS_PROMPT_FLAG_DISABLE_REMEMBER, "disable-remember", TRUE },
653 { E_CREDENTIALS_PROMPT_FLAG_PASSPHRASE, "passphrase", TRUE }
657 * e_credentials_util_prompt_flags_to_string:
659 * FIXME: Document me.
664 e_credentials_util_prompt_flags_to_string (guint prompt_flags)
667 guint masked = prompt_flags & E_CREDENTIALS_PROMPT_FLAG_REMEMBER_MASK;
668 GString *str = g_string_new ("");
670 /* Returned pointer can be passed to
671 * e_credentials_util_string_to prompt_flags() to decode
672 * it back to flags. Free returned pointer with g_free(). */
674 for (ii = 0; ii < G_N_ELEMENTS (PromptFlags); ii++) {
675 const gchar *add = NULL;
677 if (PromptFlags[ii].is_bit_flag) {
678 if ((prompt_flags & PromptFlags[ii].flag_uint) != 0)
679 add = PromptFlags[ii].flag_string;
680 } else if (masked == PromptFlags[ii].flag_uint) {
681 add = PromptFlags[ii].flag_string;
688 g_string_append (str, ",");
690 g_string_append (str, add);
693 return g_string_free (str, FALSE);
697 * e_credentials_util_string_to_prompt_flags:
699 * FIXME: Document me.
704 e_credentials_util_string_to_prompt_flags (const gchar *prompt_flags_string)
710 if (!prompt_flags_string || !*prompt_flags_string)
713 strv = g_strsplit (prompt_flags_string, ",", -1);
717 for (jj = 0; strv[jj]; jj++) {
718 const gchar *str = strv[jj];
720 for (ii = 0; ii < G_N_ELEMENTS (PromptFlags); ii++) {
721 if (g_str_equal (PromptFlags[ii].flag_string, str)) {
722 if (PromptFlags[ii].is_bit_flag)
723 flags |= PromptFlags[ii].flag_uint;
725 flags = (flags & (~E_CREDENTIALS_PROMPT_FLAG_REMEMBER_MASK)) | PromptFlags[ii].flag_uint;