goa: Add missing linker flag (for real).
[platform/upstream/evolution-data-server.git] / libedataserver / e-credentials.c
1 /*
2  * e-credentials.c
3  *
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.
8  *
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.
13  *
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/>
16  *
17  *
18  * Copyright (C) 2011 Red Hat, Inc. (www.redhat.com)
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include <string.h>
28
29 #include "e-data-server-util.h"
30
31 #include "e-credentials.h"
32
33 struct _ECredentialsPrivate
34 {
35         GHashTable *keys;
36         GHashTable *peek_keys;
37 };
38
39 static gboolean
40 key_equal (gconstpointer str1,
41            gconstpointer str2)
42 {
43         g_return_val_if_fail (str1 != NULL, FALSE);
44         g_return_val_if_fail (str2 != NULL, FALSE);
45
46         if (str1 == str2)
47                 return TRUE;
48
49         return g_ascii_strcasecmp (str1, str2) == 0;
50 }
51
52 /**
53  * e_credentials_new:
54  *
55  * FIXME: Document me.
56  *
57  * Since: 3.2
58  **/
59 ECredentials *
60 e_credentials_new (void)
61 {
62         ECredentials *credentials;
63
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);
68
69         return credentials;
70 }
71
72 /**
73  * e_credentials_new_strv:
74  *
75  * FIXME: Document me.
76  *
77  * Since: 3.2
78  **/
79 ECredentials *
80 e_credentials_new_strv (const gchar * const *keys)
81 {
82         ECredentials *credentials;
83         gint ii;
84
85         /* Expects @keys as NULL terminated list of strings
86          * "key:encoded_value".  The same can be returned from
87          * e_credentials_to_strv (). */
88
89         g_return_val_if_fail (keys != NULL, NULL);
90
91         credentials = e_credentials_new ();
92
93         for (ii = 0; keys[ii]; ii++) {
94                 const gchar *key = keys[ii], *sep;
95
96                 sep = strchr (key, ':');
97
98                 /* skip empty and invalid values */
99                 if (sep)
100                         g_hash_table_insert (credentials->priv->keys, g_strndup (key, sep - key), g_strdup (sep + 1));
101         }
102
103         return credentials;
104 }
105
106 /**
107  * e_credentials_new_args:
108  *
109  * FIXME: Document me.
110  *
111  * Since: 3.2
112  **/
113 ECredentials *
114 e_credentials_new_args (const gchar *key,
115                         ...)
116 {
117         ECredentials *credentials;
118         va_list va;
119
120         /* NULL-terminated list of string pairs <key, value>; value is
121          * in a clear form. */
122
123         g_return_val_if_fail (key != NULL, NULL);
124
125         credentials = e_credentials_new ();
126
127         va_start (va, key);
128
129         while (key) {
130                 const gchar *value = va_arg (va, const gchar *);
131
132                 if (key && *key && value && *value)
133                         e_credentials_set (credentials, key, value);
134
135                 key = va_arg (va, const gchar *);
136         }
137
138         va_end (va);
139
140         return credentials;
141 }
142
143 static void
144 copy_keys_cb (gpointer key,
145               gpointer value,
146               gpointer hash_table)
147 {
148         g_hash_table_insert (hash_table, g_strdup (key), g_strdup (value));
149 }
150
151 /**
152  * e_credentials_new_clone:
153  *
154  * FIXME: Document me.
155  *
156  * Since: 3.2
157  **/
158 ECredentials *
159 e_credentials_new_clone (const ECredentials *credentials)
160 {
161         ECredentials *res;
162
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);
166
167         res = e_credentials_new ();
168
169         g_hash_table_foreach (credentials->priv->keys, copy_keys_cb, res->priv->keys);
170
171         return res;
172 }
173
174 /**
175  * e_credentials_free:
176  *
177  * FIXME: Document me.
178  *
179  * Since: 3.2
180  **/
181 void
182 e_credentials_free (ECredentials *credentials)
183 {
184         if (!credentials)
185                 return;
186
187         g_return_if_fail (credentials->priv != NULL);
188
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);
193 }
194
195 static void
196 add_to_array_cb (gpointer key,
197                  gpointer value,
198                  gpointer ptr_array)
199 {
200         if (key && value && ptr_array) {
201                 gchar *str = g_strconcat (key, ":", value, NULL);
202
203                 g_ptr_array_add (ptr_array, e_util_utf8_make_valid (str));
204
205                 g_free (str);
206         }
207 }
208
209 /**
210  * e_credentials_to_strv:
211  * @credentials: an #ECredentials
212  *
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
216  * longer needed.
217  *
218  * Returns: (transfer full): a %NULL-terminated array of key/value strings
219  *
220  * Since: 3.2
221  **/
222 gchar **
223 e_credentials_to_strv (const ECredentials *credentials)
224 {
225         GPtrArray *array;
226
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);
230
231         array = g_ptr_array_sized_new (g_hash_table_size (credentials->priv->keys) + 1);
232
233         g_hash_table_foreach (credentials->priv->keys, add_to_array_cb, array);
234
235         /* NULL-terminated */
236         g_ptr_array_add (array, NULL);
237
238         return (gchar **) g_ptr_array_free (array, FALSE);
239 }
240
241 static gchar *
242 encode_string (const gchar *decoded)
243 {
244         gsize len, ii;
245         guchar xval, *copy;
246         gchar *res;
247
248         if (!decoded || !*decoded)
249                 return NULL;
250
251         copy = (guchar *) g_strdup (decoded);
252         len = strlen ((const gchar *) copy);
253
254         xval = 17;
255         for (ii = 0; ii < len; ii++) {
256                 copy[ii] = copy[ii] ^ xval;
257                 xval += 17;
258         }
259
260         res = g_base64_encode (copy, len);
261
262         g_free (copy);
263
264         return res;
265 }
266
267 static gchar *
268 decode_string (const gchar *encoded)
269 {
270         guchar *data, xval;
271         gsize len = 0, ii;
272         gchar *res;
273
274         g_return_val_if_fail (encoded != NULL, NULL);
275         g_return_val_if_fail (*encoded, NULL);
276
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);
280
281         xval = 17;
282         for (ii = 0; ii < len; ii++) {
283                 data[ii] = data[ii] ^ xval;
284                 xval += 17;
285         }
286
287         res = g_strndup ((const gchar *) data, len);
288
289         e_credentials_util_safe_free_string ((gchar *) data);
290
291         return res;
292 }
293
294 /**
295  * e_credentials_set:
296  * @credentials: an #ECredentials
297  * @key: a key string
298  * @value: a value string
299  *
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.
303  *
304  * Since: 3.2
305  **/
306 void
307 e_credentials_set (ECredentials *credentials,
308                    const gchar *key,
309                    const gchar *value)
310 {
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);
318
319         g_hash_table_remove (credentials->priv->peek_keys, key);
320
321         if (!value) {
322                 g_hash_table_remove (credentials->priv->keys, key);
323         } else {
324                 g_hash_table_insert (credentials->priv->keys, g_strdup (key), encode_string (value));
325         }
326 }
327
328 /**
329  * e_credentials_get:
330  *
331  * FIXME: Document me.
332  *
333  * Since: 3.2
334  **/
335 gchar *
336 e_credentials_get (const ECredentials *credentials,
337                    const gchar *key)
338 {
339         const gchar *stored;
340
341         /* Returned pointer should be freed with
342          * e_credentials_util_safe_free_string() when no longer needed. */
343
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);
349
350         stored = g_hash_table_lookup (credentials->priv->keys, key);
351         if (!stored)
352                 return NULL;
353
354         return decode_string (stored);
355 }
356
357 /**
358  * e_credentials_peek:
359  * @credentials: an #ECredentials
360  * @key: a key string
361  *
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().
365  *
366  * Returns: the value for @key
367  *
368  * Since: 3.2
369  **/
370 const gchar *
371 e_credentials_peek (ECredentials *credentials,
372                     const gchar *key)
373 {
374         gchar *value;
375
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);
381
382         value = g_hash_table_lookup (credentials->priv->peek_keys, key);
383         if (value)
384                 return value;
385
386         value = e_credentials_get (credentials, key);
387         if (value)
388                 g_hash_table_insert (credentials->priv->peek_keys, g_strdup (key), value);
389
390         return value;
391 }
392
393 struct equal_data
394 {
395         gboolean equal;
396         GHashTable *keys;
397 };
398
399 static void
400 check_equal_cb (gpointer key,
401                 gpointer value,
402                 gpointer user_data)
403 {
404         struct equal_data *ed = user_data;
405
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);
410
411         ed->equal = ed->equal && g_strcmp0 (value, g_hash_table_lookup (ed->keys, key)) == 0;
412 }
413
414 /**
415  * e_credentials_equal:
416  * @credentials1: an #ECredentials
417  * @credentials2: another #ECredentials
418  *
419  * Returns whether two #ECredential structures contain the same keys with
420  * same values.
421  *
422  * Returns: %TRUE if they are equal, %FALSE otherwise
423  *
424  * Since: 3.2
425  **/
426 gboolean
427 e_credentials_equal (const ECredentials *credentials1,
428                      const ECredentials *credentials2)
429 {
430         struct equal_data ed;
431
432         if (!credentials1 && !credentials2)
433                 return TRUE;
434
435         if (credentials1 == credentials2)
436                 return TRUE;
437
438         if (!credentials1 || !credentials2)
439                 return FALSE;
440
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);
445
446         if (g_hash_table_size (credentials1->priv->keys) != g_hash_table_size (credentials2->priv->keys))
447                 return FALSE;
448
449         ed.equal = TRUE;
450         ed.keys = credentials2->priv->keys;
451
452         g_hash_table_foreach (credentials1->priv->keys, check_equal_cb, &ed);
453
454         return ed.equal;
455 }
456
457 /**
458  * e_credentials_equal_keys:
459  * @credentials1: an #ECredentials
460  * @credentials2: another #ECredentials
461  *
462  * Returns whether two #ECredentials structures have the same keys. Key names
463  * are NULL-terminated.
464  *
465  * Returns: %TRUE if the key sets match, %FALSE otherwise
466  *
467  * Since: 3.2
468  **/
469 gboolean
470 e_credentials_equal_keys (const ECredentials *credentials1,
471                           const ECredentials *credentials2,
472                           const gchar *key1,
473                           ...)
474 {
475         va_list va;
476         gboolean equal = TRUE;
477
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);
485
486         va_start (va, key1);
487
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;
490
491                 key1 = va_arg (va, const gchar *);
492         }
493
494         va_end (va);
495
496         return equal;
497 }
498
499 /**
500  * e_credentials_has_key:
501  * @credentials: an #ECredentials
502  * @key: a key string
503  *
504  * Returns whether @credentials contains @key.
505  *
506  * Returns: %TRUE if @credentials contains @key, %FALSE otherwise
507  *
508  * Since: 3.2
509  **/
510 gboolean
511 e_credentials_has_key (const ECredentials *credentials,
512                        const gchar *key)
513 {
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);
519
520         return g_hash_table_lookup (credentials->priv->keys, key) != NULL;
521 }
522
523 /**
524  * e_credentials_keys_size:
525  * @credentials: an #ECredentials
526  *
527  * Returns the number of keys in @credentials.
528  *
529  * Returns: the number of keys in @credentials
530  *
531  * Since: 3.2
532  **/
533 guint
534 e_credentials_keys_size (const ECredentials *credentials)
535 {
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);
539
540         return g_hash_table_size (credentials->priv->keys);
541 }
542
543 static void
544 gather_key_names (gpointer key,
545                   gpointer value,
546                   gpointer pslist)
547 {
548         GSList **slist = pslist;
549
550         g_return_if_fail (pslist != NULL);
551         g_return_if_fail (key != NULL);
552
553         *slist = g_slist_prepend (*slist, key);
554 }
555
556 /**
557  * e_credentials_list_keys:
558  * @credentials: an #ECredentials
559  *
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.
563  *
564  * Returns: (transfer container) (element-type utf8): a newly-allocated #GSList
565  * of key names
566  *
567  * Since: 3.2
568  **/
569 GSList *
570 e_credentials_list_keys (const ECredentials *credentials)
571 {
572         GSList *keys = NULL;
573
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);
577
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);
581
582         return g_slist_reverse (keys);
583 }
584
585 /**
586  * e_credentials_clear:
587  *
588  * FIXME: Document me.
589  *
590  * Since: 3.2
591  **/
592 void
593 e_credentials_clear (ECredentials *credentials)
594 {
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);
599
600         g_hash_table_remove_all (credentials->priv->peek_keys);
601         g_hash_table_remove_all (credentials->priv->keys);
602 }
603
604 /**
605  * e_credentials_clear_peek:
606  *
607  * FIXME: Document me.
608  *
609  * Since: 3.2
610  **/
611 void
612 e_credentials_clear_peek (ECredentials *credentials)
613 {
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);
617
618         g_hash_table_remove_all (credentials->priv->peek_keys);
619 }
620
621 /**
622  * e_credentials_util_safe_free_string:
623  *
624  * FIXME Document me.
625  *
626  * Since: 3.2
627  **/
628 void
629 e_credentials_util_safe_free_string (gchar *str)
630 {
631         if (!str)
632                 return;
633
634         if (*str)
635                 memset (str, 0, sizeof (gchar) * strlen (str));
636
637         g_free (str);
638 }
639
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 */
644 } PromptFlags[] = {
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 },
648
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 }
654 };
655
656 /**
657  * e_credentials_util_prompt_flags_to_string:
658  *
659  * FIXME: Document me.
660  *
661  * Since: 3.2
662  **/
663 gchar *
664 e_credentials_util_prompt_flags_to_string (guint prompt_flags)
665 {
666         gint ii;
667         guint masked = prompt_flags & E_CREDENTIALS_PROMPT_FLAG_REMEMBER_MASK;
668         GString *str = g_string_new ("");
669
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(). */
673
674         for (ii = 0; ii < G_N_ELEMENTS (PromptFlags); ii++) {
675                 const gchar *add = NULL;
676
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;
682                 }
683
684                 if (!add)
685                         continue;
686
687                 if (str->len)
688                         g_string_append (str, ",");
689
690                 g_string_append (str, add);
691         }
692
693         return g_string_free (str, FALSE);
694 }
695
696 /**
697  * e_credentials_util_string_to_prompt_flags:
698  *
699  * FIXME: Document me.
700  *
701  * Since: 3.2
702  **/
703 guint
704 e_credentials_util_string_to_prompt_flags (const gchar *prompt_flags_string)
705 {
706         gchar **strv;
707         gint ii, jj;
708         guint flags = 0;
709
710         if (!prompt_flags_string || !*prompt_flags_string)
711                 return flags;
712
713         strv = g_strsplit (prompt_flags_string, ",", -1);
714         if (!strv)
715                 return flags;
716
717         for (jj = 0; strv[jj]; jj++) {
718                 const gchar *str = strv[jj];
719
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;
724                                 else
725                                         flags = (flags & (~E_CREDENTIALS_PROMPT_FLAG_REMEMBER_MASK)) | PromptFlags[ii].flag_uint;
726                         }
727                 }
728         }
729
730         g_strfreev (strv);
731
732         return flags;
733 }