Fix leaks around categories editing
[platform/upstream/evolution-data-server.git] / libedataserverui / e-client-utils.c
1 /*
2  * e-client-utils.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 <glib/gi18n-lib.h>
27 #include <gtk/gtk.h>
28 #include <libsoup/soup.h>
29
30 #include <libedataserver/e-client.h>
31 #include "libedataserver/e-client-private.h"
32 #include <libebook/e-book-client.h>
33 #include <libecal/e-cal-client.h>
34
35 #include "e-passwords.h"
36 #include "e-client-utils.h"
37
38 /**
39  * e_client_utils_new:
40  *
41  * Proxy function for e_book_client_utils_new() and e_cal_client_utils_new().
42  *
43  * Since: 3.2
44  **/
45 EClient *
46 e_client_utils_new (ESource *source,
47                     EClientSourceType source_type,
48                     GError **error)
49 {
50         EClient *res = NULL;
51
52         g_return_val_if_fail (source != NULL, NULL);
53         g_return_val_if_fail (E_IS_SOURCE (source), NULL);
54
55         switch (source_type) {
56         case E_CLIENT_SOURCE_TYPE_CONTACTS:
57                 res = E_CLIENT (e_book_client_new (source, error));
58                 break;
59         case E_CLIENT_SOURCE_TYPE_EVENTS:
60                 res = E_CLIENT (e_cal_client_new (source, E_CAL_CLIENT_SOURCE_TYPE_EVENTS, error));
61                 break;
62         case E_CLIENT_SOURCE_TYPE_MEMOS:
63                 res = E_CLIENT (e_cal_client_new (source, E_CAL_CLIENT_SOURCE_TYPE_MEMOS, error));
64                 break;
65         case E_CLIENT_SOURCE_TYPE_TASKS:
66                 res = E_CLIENT (e_cal_client_new (source, E_CAL_CLIENT_SOURCE_TYPE_TASKS, error));
67                 break;
68         default:
69                 g_return_val_if_reached (NULL);
70                 break;
71         }
72
73         return res;
74 }
75
76 /**
77  * e_client_utils_new_from_uri:
78  *
79  * Proxy function for e_book_client_utils_new_from_uri() and e_cal_client_utils_new_from_uri().
80  *
81  * Since: 3.2
82  **/
83 EClient *
84 e_client_utils_new_from_uri (const gchar *uri,
85                              EClientSourceType source_type,
86                              GError **error)
87 {
88         EClient *res = NULL;
89
90         g_return_val_if_fail (uri != NULL, NULL);
91
92         switch (source_type) {
93         case E_CLIENT_SOURCE_TYPE_CONTACTS:
94                 res = E_CLIENT (e_book_client_new_from_uri (uri, error));
95                 break;
96         case E_CLIENT_SOURCE_TYPE_EVENTS:
97                 res = E_CLIENT (e_cal_client_new_from_uri (uri, E_CAL_CLIENT_SOURCE_TYPE_EVENTS, error));
98                 break;
99         case E_CLIENT_SOURCE_TYPE_MEMOS:
100                 res = E_CLIENT (e_cal_client_new_from_uri (uri, E_CAL_CLIENT_SOURCE_TYPE_MEMOS, error));
101                 break;
102         case E_CLIENT_SOURCE_TYPE_TASKS:
103                 res = E_CLIENT (e_cal_client_new_from_uri (uri, E_CAL_CLIENT_SOURCE_TYPE_TASKS, error));
104                 break;
105         default:
106                 g_return_val_if_reached (NULL);
107                 break;
108         }
109
110         return res;
111 }
112
113 /**
114  * e_client_utils_new_system:
115  *
116  * Proxy function for e_book_client_utils_new_system() and e_cal_client_utils_new_system().
117  *
118  * Since: 3.2
119  **/
120 EClient *
121 e_client_utils_new_system (EClientSourceType source_type,
122                            GError **error)
123 {
124         EClient *res = NULL;
125
126         switch (source_type) {
127         case E_CLIENT_SOURCE_TYPE_CONTACTS:
128                 res = E_CLIENT (e_book_client_new_system (error));
129                 break;
130         case E_CLIENT_SOURCE_TYPE_EVENTS:
131                 res = E_CLIENT (e_cal_client_new_system (E_CAL_CLIENT_SOURCE_TYPE_EVENTS, error));
132                 break;
133         case E_CLIENT_SOURCE_TYPE_MEMOS:
134                 res = E_CLIENT (e_cal_client_new_system (E_CAL_CLIENT_SOURCE_TYPE_MEMOS, error));
135                 break;
136         case E_CLIENT_SOURCE_TYPE_TASKS:
137                 res = E_CLIENT (e_cal_client_new_system (E_CAL_CLIENT_SOURCE_TYPE_TASKS, error));
138                 break;
139         default:
140                 g_return_val_if_reached (NULL);
141                 break;
142         }
143
144         return res;
145 }
146
147 /**
148  * e_client_utils_new_default:
149  *
150  * Proxy function for e_book_client_utils_new_default() and e_cal_client_utils_new_default().
151  *
152  * Since: 3.2
153  **/
154 EClient *
155 e_client_utils_new_default (EClientSourceType source_type,
156                             GError **error)
157 {
158         EClient *res = NULL;
159
160         switch (source_type) {
161         case E_CLIENT_SOURCE_TYPE_CONTACTS:
162                 res = E_CLIENT (e_book_client_new_default (error));
163                 break;
164         case E_CLIENT_SOURCE_TYPE_EVENTS:
165                 res = E_CLIENT (e_cal_client_new_default (E_CAL_CLIENT_SOURCE_TYPE_EVENTS, error));
166                 break;
167         case E_CLIENT_SOURCE_TYPE_MEMOS:
168                 res = E_CLIENT (e_cal_client_new_default (E_CAL_CLIENT_SOURCE_TYPE_MEMOS, error));
169                 break;
170         case E_CLIENT_SOURCE_TYPE_TASKS:
171                 res = E_CLIENT (e_cal_client_new_default (E_CAL_CLIENT_SOURCE_TYPE_TASKS, error));
172                 break;
173         default:
174                 g_return_val_if_reached (NULL);
175                 break;
176         }
177
178         return res;
179 }
180
181 /**
182  * e_client_utils_set_default:
183  *
184  * Proxy function for e_book_client_utils_set_default() and e_book_client_utils_set_default().
185  *
186  * Since: 3.2
187  **/
188 gboolean
189 e_client_utils_set_default (EClient *client,
190                             EClientSourceType source_type,
191                             GError **error)
192 {
193         gboolean res = FALSE;
194
195         g_return_val_if_fail (client != NULL, FALSE);
196         g_return_val_if_fail (E_IS_CLIENT (client), FALSE);
197
198         switch (source_type) {
199         case E_CLIENT_SOURCE_TYPE_CONTACTS:
200                 g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
201                 res = e_book_client_set_default (E_BOOK_CLIENT (client), error);
202                 break;
203         case E_CLIENT_SOURCE_TYPE_EVENTS:
204         case E_CLIENT_SOURCE_TYPE_MEMOS:
205         case E_CLIENT_SOURCE_TYPE_TASKS:
206                 g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
207                 res = e_cal_client_set_default (E_CAL_CLIENT (client), error);
208                 break;
209         default:
210                 g_return_val_if_reached (FALSE);
211                 break;
212         }
213
214         return res;
215 }
216
217 /**
218  * e_client_utils_set_default_source:
219  *
220  * Proxy function for e_book_client_utils_set_default_source() and e_cal_client_utils_set_default_source().
221  *
222  * Since: 3.2
223  **/
224 gboolean
225 e_client_utils_set_default_source (ESource *source,
226                                    EClientSourceType source_type,
227                                    GError **error)
228 {
229         gboolean res = FALSE;
230
231         g_return_val_if_fail (source != NULL, FALSE);
232         g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
233
234         switch (source_type) {
235         case E_CLIENT_SOURCE_TYPE_CONTACTS:
236                 res = e_book_client_set_default_source (source, error);
237                 break;
238         case E_CLIENT_SOURCE_TYPE_EVENTS:
239                 res = e_cal_client_set_default_source (source, E_CAL_CLIENT_SOURCE_TYPE_EVENTS, error);
240                 break;
241         case E_CLIENT_SOURCE_TYPE_MEMOS:
242                 res = e_cal_client_set_default_source (source, E_CAL_CLIENT_SOURCE_TYPE_MEMOS, error);
243                 break;
244         case E_CLIENT_SOURCE_TYPE_TASKS:
245                 res = e_cal_client_set_default_source (source, E_CAL_CLIENT_SOURCE_TYPE_TASKS, error);
246                 break;
247         default:
248                 g_return_val_if_reached (FALSE);
249                 break;
250         }
251
252         return res;
253 }
254
255 /**
256  * e_client_utils_get_sources:
257  *
258  * Proxy function for e_book_client_utils_get_sources() and e_cal_client_utils_get_sources().
259  *
260  * Since: 3.2
261  **/
262 gboolean
263 e_client_utils_get_sources (ESourceList **sources,
264                             EClientSourceType source_type,
265                             GError **error)
266 {
267         gboolean res = FALSE;
268
269         g_return_val_if_fail (sources != NULL, FALSE);
270
271         switch (source_type) {
272         case E_CLIENT_SOURCE_TYPE_CONTACTS:
273                 res = e_book_client_get_sources (sources, error);
274                 break;
275         case E_CLIENT_SOURCE_TYPE_EVENTS:
276                 res = e_cal_client_get_sources (sources, E_CAL_CLIENT_SOURCE_TYPE_EVENTS, error);
277                 break;
278         case E_CLIENT_SOURCE_TYPE_MEMOS:
279                 res = e_cal_client_get_sources (sources, E_CAL_CLIENT_SOURCE_TYPE_MEMOS, error);
280                 break;
281         case E_CLIENT_SOURCE_TYPE_TASKS:
282                 res = e_cal_client_get_sources (sources, E_CAL_CLIENT_SOURCE_TYPE_TASKS, error);
283                 break;
284         default:
285                 g_return_val_if_reached (FALSE);
286                 break;
287         }
288
289         return res;
290 }
291
292 typedef struct _EClientUtilsAsyncOpData
293 {
294         EClientUtilsAuthenticateHandler auth_handler;
295         gpointer auth_handler_user_data;
296         GAsyncReadyCallback async_cb;
297         gpointer async_cb_user_data;
298         GCancellable *cancellable;
299         ESource *source;
300         EClient *client;
301         ECredentials *used_credentials;
302         gboolean open_finished;
303         GError *opened_cb_error;
304         guint retry_open_id;
305         gboolean only_if_exists;
306         guint pending_properties_count;
307 } EClientUtilsAsyncOpData;
308
309 static void
310 free_client_utils_async_op_data (EClientUtilsAsyncOpData *async_data)
311 {
312         g_return_if_fail (async_data != NULL);
313         g_return_if_fail (async_data->cancellable != NULL);
314         g_return_if_fail (async_data->client != NULL);
315
316         if (async_data->retry_open_id)
317                 g_source_remove (async_data->retry_open_id);
318
319         g_signal_handlers_disconnect_matched (async_data->cancellable, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, async_data);
320         g_signal_handlers_disconnect_matched (async_data->client, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, async_data);
321
322         if (async_data->used_credentials)
323                 e_credentials_free (async_data->used_credentials);
324         if (async_data->opened_cb_error)
325                 g_error_free (async_data->opened_cb_error);
326         g_object_unref (async_data->cancellable);
327         g_object_unref (async_data->client);
328         g_object_unref (async_data->source);
329         g_free (async_data);
330 }
331
332 static gboolean
333 complete_async_op_in_idle_cb (gpointer user_data)
334 {
335         GSimpleAsyncResult *simple = user_data;
336         gint run_main_depth;
337
338         g_return_val_if_fail (simple != NULL, FALSE);
339
340         run_main_depth = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (simple), "run-main-depth"));
341         if (run_main_depth < 1)
342                 run_main_depth = 1;
343
344         /* do not receive in higher level than was initially run */
345         if (g_main_depth () > run_main_depth) {
346                 return TRUE;
347         }
348
349         g_simple_async_result_complete (simple);
350         g_object_unref (simple);
351
352         return FALSE;
353 }
354
355 #define return_async_error_if_fail(expr, async_cb, async_cb_user_data, src, source_tag) G_STMT_START {  \
356         if (G_LIKELY ((expr))) { } else {                                                               \
357                 GError *error;                                                                          \
358                                                                                                         \
359                 error = g_error_new (E_CLIENT_ERROR, E_CLIENT_ERROR_INVALID_ARG,                        \
360                                 "%s: assertion '%s' failed", G_STRFUNC, #expr);                         \
361                                                                                                         \
362                 return_async_error (error, async_cb, async_cb_user_data, src, source_tag);              \
363                 g_error_free (error);                                                                   \
364                 return;                                                                                 \
365         }                                                                                               \
366         } G_STMT_END
367
368 static void
369 return_async_error (const GError *error,
370                     GAsyncReadyCallback async_cb,
371                     gpointer async_cb_user_data,
372                     ESource *source,
373                     gpointer source_tag)
374 {
375         GSimpleAsyncResult *simple;
376
377         g_return_if_fail (error != NULL);
378         g_return_if_fail (source_tag != NULL);
379
380         simple = g_simple_async_result_new (G_OBJECT (source), async_cb, async_cb_user_data, source_tag);
381         g_simple_async_result_set_from_error (simple, error);
382
383         g_object_set_data (G_OBJECT (simple), "run-main-depth", GINT_TO_POINTER (g_main_depth ()));
384         g_idle_add (complete_async_op_in_idle_cb, simple);
385 }
386
387 static void
388 client_utils_get_backend_property_cb (GObject *source_object,
389                                       GAsyncResult *result,
390                                       gpointer user_data)
391 {
392         EClient *client = E_CLIENT (source_object);
393         EClientUtilsAsyncOpData *async_data = user_data;
394         GSimpleAsyncResult *simple;
395
396         g_return_if_fail (async_data != NULL);
397         g_return_if_fail (async_data->client != NULL);
398         g_return_if_fail (async_data->client == client);
399
400         if (result) {
401                 gchar *prop_value = NULL;
402
403                 if (e_client_get_backend_property_finish (client, result, &prop_value, NULL))
404                         g_free (prop_value);
405
406                 async_data->pending_properties_count--;
407                 if (async_data->pending_properties_count)
408                         return;
409         }
410
411         /* keep the initial auth_handler connected directly, thus it will be able
412          * to answer any later authentication requests, for reconnection, for example
413         */
414         if (async_data->auth_handler)
415                 g_signal_connect (async_data->client, "authenticate", G_CALLBACK (async_data->auth_handler), async_data->auth_handler_user_data);
416
417         simple = g_simple_async_result_new (G_OBJECT (async_data->source), async_data->async_cb, async_data->async_cb_user_data, e_client_utils_open_new);
418         g_simple_async_result_set_op_res_gpointer (simple, g_object_ref (async_data->client), g_object_unref);
419
420         g_object_set_data (G_OBJECT (simple), "run-main-depth", GINT_TO_POINTER (g_main_depth ()));
421         g_idle_add (complete_async_op_in_idle_cb, simple);
422
423         free_client_utils_async_op_data (async_data);
424 }
425
426 static void
427 client_utils_capabilities_retrieved_cb (GObject *source_object,
428                                         GAsyncResult *result,
429                                         gpointer user_data)
430 {
431         EClient *client = E_CLIENT (source_object);
432         EClientUtilsAsyncOpData *async_data = user_data;
433         gchar *capabilities = NULL;
434         gboolean caps_res;
435
436         g_return_if_fail (async_data != NULL);
437         g_return_if_fail (async_data->client != NULL);
438         g_return_if_fail (async_data->client == client);
439
440         caps_res = e_client_retrieve_capabilities_finish (client, result, &capabilities, NULL);
441         g_free (capabilities);
442
443         if (caps_res) {
444                 async_data->pending_properties_count = 1;
445
446                 /* precache backend properties */
447                 if (E_IS_CAL_CLIENT (client)) {
448                         async_data->pending_properties_count += 3;
449
450                         e_client_get_backend_property (async_data->client, CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS, async_data->cancellable, client_utils_get_backend_property_cb, async_data);
451                         e_client_get_backend_property (async_data->client, CAL_BACKEND_PROPERTY_ALARM_EMAIL_ADDRESS, async_data->cancellable, client_utils_get_backend_property_cb, async_data);
452                         e_client_get_backend_property (async_data->client, CAL_BACKEND_PROPERTY_DEFAULT_OBJECT, async_data->cancellable, client_utils_get_backend_property_cb, async_data);
453                 } else if (E_IS_BOOK_CLIENT (client)) {
454                         async_data->pending_properties_count += 3;
455
456                         e_client_get_backend_property (async_data->client, BOOK_BACKEND_PROPERTY_REQUIRED_FIELDS, async_data->cancellable, client_utils_get_backend_property_cb, async_data);
457                         e_client_get_backend_property (async_data->client, BOOK_BACKEND_PROPERTY_SUPPORTED_FIELDS, async_data->cancellable, client_utils_get_backend_property_cb, async_data);
458                         e_client_get_backend_property (async_data->client, BOOK_BACKEND_PROPERTY_SUPPORTED_AUTH_METHODS, async_data->cancellable, client_utils_get_backend_property_cb, async_data);
459                 } else {
460                         g_warn_if_reached ();
461                         client_utils_get_backend_property_cb (source_object, NULL, async_data);
462                         return;
463                 }
464
465                 e_client_get_backend_property (async_data->client, CLIENT_BACKEND_PROPERTY_CACHE_DIR, async_data->cancellable, client_utils_get_backend_property_cb, async_data);
466         } else {
467                 client_utils_get_backend_property_cb (source_object, NULL, async_data);
468         }
469 }
470
471 static void
472 client_utils_open_new_done (EClientUtilsAsyncOpData *async_data)
473 {
474         g_return_if_fail (async_data != NULL);
475         g_return_if_fail (async_data->client != NULL);
476
477         /* retrieve capabilities just to have them cached on #EClient for later use */
478         e_client_retrieve_capabilities (async_data->client, async_data->cancellable, client_utils_capabilities_retrieved_cb, async_data);
479 }
480
481 static gboolean client_utils_retry_open_timeout_cb (gpointer user_data);
482
483 static void
484 finish_or_retry_open (EClientUtilsAsyncOpData *async_data,
485                       const GError *error)
486 {
487         g_return_if_fail (async_data != NULL);
488
489         if (async_data->auth_handler && error && g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_AUTHENTICATION_FAILED)) {
490                 if (async_data->used_credentials) {
491                         const gchar *prompt_key;
492
493                         prompt_key = e_credentials_peek (async_data->used_credentials, E_CREDENTIALS_KEY_PROMPT_KEY);
494
495                         /* make sure the old password is forgotten when authentication failed */
496                         if (prompt_key)
497                                 e_passwords_forget_password (NULL, prompt_key);
498
499                         e_credentials_set (async_data->used_credentials, E_CREDENTIALS_KEY_PROMPT_REASON, error->message);
500                 }
501
502                 e_client_process_authentication (async_data->client, async_data->used_credentials);
503         } else if (error && g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_BUSY)) {
504                 /* postpone for 1/2 of a second, backend is busy now */
505                 async_data->open_finished = FALSE;
506                 async_data->retry_open_id = g_timeout_add (500, client_utils_retry_open_timeout_cb, async_data);
507         } else if (error) {
508                 return_async_error (error, async_data->async_cb, async_data->async_cb_user_data, async_data->source, e_client_utils_open_new);
509                 free_client_utils_async_op_data (async_data);
510         } else {
511                 client_utils_open_new_done (async_data);
512         }
513 }
514
515 static void
516 client_utils_opened_cb (EClient *client,
517                         const GError *error,
518                         EClientUtilsAsyncOpData *async_data)
519 {
520         g_return_if_fail (client != NULL);
521         g_return_if_fail (async_data != NULL);
522         g_return_if_fail (client == async_data->client);
523
524         g_signal_handlers_disconnect_by_func (client, G_CALLBACK (client_utils_opened_cb), async_data);
525
526         if (!async_data->open_finished) {
527                 /* there can happen that the "opened" signal is received
528                  * before the e_client_open () is finished, thus keep detailed
529                  * error for later use, if any */
530                 if (error)
531                         async_data->opened_cb_error = g_error_copy (error);
532         } else {
533                 finish_or_retry_open (async_data, error);
534         }
535 }
536
537 static void
538 client_utils_open_new_async_cb (GObject *source_object,
539                                 GAsyncResult *result,
540                                 gpointer user_data)
541 {
542         EClientUtilsAsyncOpData *async_data = user_data;
543         GError *error = NULL;
544
545         g_return_if_fail (source_object != NULL);
546         g_return_if_fail (result != NULL);
547         g_return_if_fail (async_data != NULL);
548         g_return_if_fail (async_data->async_cb != NULL);
549         g_return_if_fail (async_data->client == E_CLIENT (source_object));
550
551         async_data->open_finished = TRUE;
552
553         if (!e_client_open_finish (E_CLIENT (source_object), result, &error)
554             || g_cancellable_set_error_if_cancelled (async_data->cancellable, &error)) {
555                 finish_or_retry_open (async_data, error);
556                 g_error_free (error);
557                 return;
558         }
559
560         if (async_data->opened_cb_error) {
561                 finish_or_retry_open (async_data, async_data->opened_cb_error);
562                 return;
563         }
564
565         if (e_client_is_opened (async_data->client)) {
566                 client_utils_open_new_done (async_data);
567                 return;
568         }
569
570         /* wait for 'opened' signal, which is received in client_utils_opened_cb */
571 }
572
573 static gboolean
574 client_utils_retry_open_timeout_cb (gpointer user_data)
575 {
576         EClientUtilsAsyncOpData *async_data = user_data;
577
578         g_return_val_if_fail (async_data != NULL, FALSE);
579
580         g_signal_handlers_disconnect_matched (async_data->cancellable, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, async_data);
581
582         e_client_open (async_data->client, async_data->only_if_exists, async_data->cancellable, client_utils_open_new_async_cb, async_data);
583
584         async_data->retry_open_id = 0;
585
586         return FALSE;
587 }
588
589 static gboolean
590 client_utils_open_new_auth_cb (EClient *client,
591                                ECredentials *credentials,
592                                gpointer user_data)
593 {
594         EClientUtilsAsyncOpData *async_data = user_data;
595         gboolean handled;
596
597         g_return_val_if_fail (client != NULL, FALSE);
598         g_return_val_if_fail (async_data != NULL, FALSE);
599         g_return_val_if_fail (async_data->auth_handler != NULL, FALSE);
600
601         if (async_data->used_credentials) {
602                 const gchar *reason = e_credentials_peek (async_data->used_credentials, E_CREDENTIALS_KEY_PROMPT_REASON);
603
604                 if (reason) {
605                         e_credentials_set (credentials, E_CREDENTIALS_KEY_PROMPT_TEXT, NULL);
606                         e_credentials_set (credentials, E_CREDENTIALS_KEY_PROMPT_REASON, reason);
607                 }
608         }
609
610         handled = async_data->auth_handler (client, credentials, async_data->auth_handler_user_data);
611
612         if (handled && credentials) {
613                 if (async_data->used_credentials) {
614                         gchar *prompt_flags_str;
615                         guint prompt_flags = 0;
616
617                         e_credentials_free (async_data->used_credentials);
618
619                         prompt_flags_str = e_credentials_get (credentials, E_CREDENTIALS_KEY_PROMPT_FLAGS);
620                         if (prompt_flags_str) {
621                                 prompt_flags = e_credentials_util_string_to_prompt_flags (prompt_flags_str);
622                                 g_free (prompt_flags_str);
623                         } else {
624                                 prompt_flags = E_CREDENTIALS_PROMPT_FLAG_REMEMBER_FOREVER
625                                              | E_CREDENTIALS_PROMPT_FLAG_SECRET
626                                              | E_CREDENTIALS_PROMPT_FLAG_ONLINE;
627                         }
628
629                         prompt_flags |= E_CREDENTIALS_PROMPT_FLAG_REPROMPT;
630
631                         prompt_flags_str = e_credentials_util_prompt_flags_to_string (prompt_flags);
632                         e_credentials_set (credentials, E_CREDENTIALS_KEY_PROMPT_FLAGS, prompt_flags_str);
633                         g_free (prompt_flags_str);
634                 }
635
636                 async_data->used_credentials = e_credentials_new_clone (credentials);
637         }
638
639         return handled;
640 }
641
642 /**
643  * e_client_utils_open_new:
644  * @source: an #ESource to be opened
645  * @source_type: an #EClientSourceType of the @source
646  * @only_if_exists: if %TRUE, fail if this client doesn't already exist, otherwise create it first
647  * @cancellable: a #GCancellable; can be %NULL
648  * @auth_handler: authentication handler, to be used; the e_client_utils_authenticate_handler() is usually sufficient
649  * @auth_handler_user_data: user data for @auth_handler function
650  * @async_cb: callback to call when a result is ready
651  * @async_cb_user_data: user data for the @async_cb
652  *
653  * Begins asynchronous opening of a new #EClient corresponding
654  * to the @source of type @source_type. The resulting #EClient
655  * is fully opened and authenticated client, ready to be used.
656  * The opened client has also fetched capabilities.
657  * This call is finished by e_client_utils_open_new_finish()
658  * from the @async_cb.
659  *
660  * Note: the @auth_handler, and its @auth_handler_user_data,
661  * should be valid through whole live of returned #EClient.
662  *
663  * Since: 3.2
664  **/
665 void
666 e_client_utils_open_new (ESource *source,
667                          EClientSourceType source_type,
668                          gboolean only_if_exists,
669                          GCancellable *cancellable,
670                          EClientUtilsAuthenticateHandler auth_handler,
671                          gpointer auth_handler_user_data,
672                          GAsyncReadyCallback async_cb,
673                          gpointer async_cb_user_data)
674 {
675         EClient *client;
676         GError *error = NULL;
677         EClientUtilsAsyncOpData *async_data;
678
679         g_return_if_fail (async_cb != NULL);
680         return_async_error_if_fail (source != NULL, async_cb, async_cb_user_data, source, e_client_utils_open_new);
681         return_async_error_if_fail (E_IS_SOURCE (source), async_cb, async_cb_user_data, source, e_client_utils_open_new);
682
683         client = e_client_utils_new (source, source_type, &error);
684         if (!client) {
685                 return_async_error (error, async_cb, async_cb_user_data, source, e_client_utils_open_new);
686                 g_error_free (error);
687                 return;
688         }
689
690         async_data = g_new0 (EClientUtilsAsyncOpData, 1);
691         async_data->auth_handler = auth_handler;
692         async_data->auth_handler_user_data = auth_handler_user_data;
693         async_data->async_cb = async_cb;
694         async_data->async_cb_user_data = async_cb_user_data;
695         async_data->source = g_object_ref (source);
696         async_data->client = client;
697         async_data->open_finished = FALSE;
698         async_data->only_if_exists = only_if_exists;
699         async_data->retry_open_id = 0;
700
701         if (cancellable)
702                 async_data->cancellable = g_object_ref (cancellable);
703         else
704                 async_data->cancellable = g_cancellable_new ();
705
706         if (auth_handler)
707                 g_signal_connect (client, "authenticate", G_CALLBACK (client_utils_open_new_auth_cb), async_data);
708
709         /* wait till backend notifies about its opened state */
710         g_signal_connect (client, "opened", G_CALLBACK (client_utils_opened_cb), async_data);
711
712         e_client_open (async_data->client, async_data->only_if_exists, async_data->cancellable, client_utils_open_new_async_cb, async_data);
713 }
714
715 /**
716  * e_client_utils_open_new_finish:
717  * @source: an #ESource on which the e_client_utils_open_new() was invoked
718  * @result: a #GAsyncResult
719  * @client: (out): Return value for an #EClient
720  * @error: (out): a #GError to set an error, if any
721  *
722  * Finishes previous call of e_client_utils_open_new() and
723  * sets @client to a fully opened and authenticated #EClient.
724  * This @client, if not NULL, should be freed with g_object_unref().
725  *
726  * Returns: %TRUE if successful, %FALSE otherwise.
727  *
728  * Since: 3.2
729  **/
730 gboolean
731 e_client_utils_open_new_finish (ESource *source,
732                                 GAsyncResult *result,
733                                 EClient **client,
734                                 GError **error)
735 {
736         GSimpleAsyncResult *simple;
737
738         g_return_val_if_fail (source != NULL, FALSE);
739         g_return_val_if_fail (result != NULL, FALSE);
740         g_return_val_if_fail (client != NULL, FALSE);
741         g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (source), e_client_utils_open_new), FALSE);
742
743         *client = NULL;
744         simple = G_SIMPLE_ASYNC_RESULT (result);
745
746         if (g_simple_async_result_propagate_error (simple, error))
747                 return FALSE;
748
749         *client = g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
750
751         return *client != NULL;
752 }
753
754 /* free returned pointer with g_free() */
755 static gchar *
756 get_prompt_key (EClient *client,
757                 const gchar *user_name)
758 {
759         SoupURI *suri;
760         gchar *uri_str;
761
762         g_return_val_if_fail (client != NULL, NULL);
763
764         suri = soup_uri_new (e_client_get_uri (client));
765         g_return_val_if_fail (suri != NULL, NULL);
766
767         soup_uri_set_user (suri, user_name);
768         soup_uri_set_password (suri, NULL);
769         soup_uri_set_fragment (suri, NULL);
770
771         uri_str = soup_uri_to_string (suri, FALSE);
772         soup_uri_free (suri);
773
774         return uri_str;
775 }
776
777 /**
778  * e_client_utils_authenticate_handler:
779  *
780  * This function is suitable as a handler for EClient::authenticate signal.
781  * It takes care of all the password prompt and such and returns TRUE if
782  * credentials (password) were provided. Thus just connect it to that signal
783  * and it'll take care of everything else.
784  *
785  * gtk_window_parent is user_data passed into the callback. It can be a pointer
786  * to GtkWindow, used as a parent for a pasword prompt dialog.
787  *
788  * Since: 3.2
789  **/
790 gboolean
791 e_client_utils_authenticate_handler (EClient *client,
792                                      ECredentials *credentials,
793                                      gpointer gtk_window_parent)
794 {
795         ESource *source;
796         gboolean is_book, is_cal, res, remember_password = FALSE;
797         const gchar *prop;
798
799         g_return_val_if_fail (client != NULL, FALSE);
800         g_return_val_if_fail (credentials != NULL, FALSE);
801
802         is_book = E_IS_BOOK_CLIENT (client);
803         is_cal = !is_book && E_IS_CAL_CLIENT (client);
804         g_return_val_if_fail (is_book || is_cal, FALSE);
805
806         source = e_client_get_source (client);
807         g_return_val_if_fail (source != NULL, FALSE);
808
809         if (!e_credentials_has_key (credentials, E_CREDENTIALS_KEY_USERNAME)) {
810                 const gchar *username;
811
812                 username = e_source_get_property (source, "username");
813                 if (!username) {
814                         const gchar *auth;
815
816                         auth = e_source_get_property (source, "auth");
817                         if (g_strcmp0 (auth, "ldap/simple-binddn") == 0)
818                                 username = e_source_get_property (source, "binddn");
819                         else
820                                 username = e_source_get_property (source, "email_addr");
821
822                         if (!username)
823                                 username = "";
824                 }
825
826                 e_credentials_set (credentials, E_CREDENTIALS_KEY_USERNAME, username);
827
828                 /* no username set on the source - deny authentication request until
829                  * username will be also enterable with e-passwords */
830                 if (!e_credentials_has_key (credentials, E_CREDENTIALS_KEY_USERNAME))
831                         return FALSE;
832         }
833
834         if (!e_credentials_has_key (credentials, E_CREDENTIALS_KEY_PROMPT_KEY)) {
835                 gchar *prompt_key = get_prompt_key (client, e_credentials_peek (credentials, E_CREDENTIALS_KEY_USERNAME));
836
837                 e_credentials_set (credentials, E_CREDENTIALS_KEY_PROMPT_KEY, prompt_key);
838
839                 g_free (prompt_key);
840         }
841
842         if (!e_credentials_has_key (credentials, E_CREDENTIALS_KEY_PROMPT_TEXT)) {
843                 gchar *prompt, *reason;
844                 gchar *username_markup, *source_name_markup;
845
846                 reason = e_credentials_get (credentials, E_CREDENTIALS_KEY_PROMPT_REASON);
847                 username_markup = g_markup_printf_escaped ("<b>%s</b>", e_credentials_peek (credentials, E_CREDENTIALS_KEY_USERNAME));
848                 source_name_markup = g_markup_printf_escaped ("<b>%s</b>", e_source_peek_name (source));
849
850                 if (is_cal) {
851                         switch (e_cal_client_get_source_type (E_CAL_CLIENT (client))) {
852                         default:
853                                 g_warn_if_reached ();
854                         case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
855                                 if (reason && *reason)
856                                         prompt = g_strdup_printf (_("Enter password for calendar %s (user %s)\nReason: %s"), source_name_markup, username_markup, reason);
857                                 else
858                                         prompt = g_strdup_printf (_("Enter password for calendar %s (user %s)"), source_name_markup, username_markup);
859                                 break;
860                         case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
861                                 if (reason && *reason)
862                                         prompt = g_strdup_printf (_("Enter password for task list %s (user %s)\nReason: %s"), source_name_markup, username_markup, reason);
863                                 else
864                                         prompt = g_strdup_printf (_("Enter password for task list %s (user %s)"), source_name_markup, username_markup);
865                                 break;
866                         case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
867                                 if (reason && *reason)
868                                         prompt = g_strdup_printf (_("Enter password for memo list %s (user %s)\nReason: %s"), source_name_markup, username_markup, reason);
869                                 else
870                                         prompt = g_strdup_printf (_("Enter password for memo list %s (user %s)"), source_name_markup, username_markup);
871                                 break;
872                         }
873                 } else {
874                         if (reason && *reason)
875                                 prompt = g_strdup_printf (_("Enter password for address book %s (user %s)\nReason: %s"), source_name_markup, username_markup, reason);
876                         else
877                                 prompt = g_strdup_printf (_("Enter password for address book %s (user %s)"), source_name_markup, username_markup);
878
879                         if (!is_book)
880                                 g_warn_if_reached ();
881                 }
882
883                 e_credentials_set (credentials, E_CREDENTIALS_KEY_PROMPT_TEXT, prompt);
884
885                 g_free (username_markup);
886                 g_free (source_name_markup);
887                 g_free (reason);
888                 g_free (prompt);
889         }
890
891         prop = e_source_get_property (source, "remember_password");
892         remember_password = !prop || g_strcmp0 (prop, "true") == 0;
893
894         res = e_credentials_authenticate_helper (credentials, gtk_window_parent, &remember_password);
895
896         if (res)
897                 e_source_set_property (source, "remember_password", remember_password ? "true" : "false");
898
899         e_credentials_clear_peek (credentials);
900
901         return res;
902 }
903
904 /**
905  * e_client_utils_forget_password:
906  * @client: An #EClient
907  *
908  * Forgets stored password for the given @client.
909  *
910  * Since: 3.2
911  **/
912 void
913 e_client_utils_forget_password (EClient *client)
914 {
915         gchar *prompt_key;
916         ESource *source;
917
918         g_return_if_fail (client != NULL);
919         g_return_if_fail (E_IS_CLIENT (client));
920
921         source = e_client_get_source (client);
922         g_return_if_fail (source != NULL);
923
924         prompt_key = get_prompt_key (client, e_source_get_property (source, "username"));
925
926         e_passwords_forget_password (NULL, prompt_key);
927
928         g_free (prompt_key);
929 }
930
931 /** 
932  * e_credentials_authenticate_helper:
933  *
934  * Asks for a password based on the provided credentials information.
935  * Credentials should have set following keys:
936  *    E_CREDENTIALS_KEY_USERNAME
937  *    E_CREDENTIALS_KEY_PROMPT_KEY
938  *    E_CREDENTIALS_KEY_PROMPT_TEXT
939  * all other keys are optional. If also E_CREDENTIALS_KEY_PASSWORD key is provided,
940  * then it implies a reprompt.
941  *
942  * When this returns TRUE, then the structure contains E_CREDENTIALS_KEY_PASSWORD set
943  * as entered by a user.
944  *
945  * Since: 3.2
946  **/
947 gboolean
948 e_credentials_authenticate_helper (ECredentials *credentials,
949                                    GtkWindow *parent,
950                                    gboolean *remember_password)
951 {
952         gboolean res, fake_remember_password = FALSE;
953         guint prompt_flags;
954         gchar *password = NULL;
955         const gchar *title, *prompt_key;
956
957         g_return_val_if_fail (credentials != NULL, FALSE);
958         g_return_val_if_fail (e_credentials_has_key (credentials, E_CREDENTIALS_KEY_USERNAME), FALSE);
959         g_return_val_if_fail (e_credentials_has_key (credentials, E_CREDENTIALS_KEY_PROMPT_KEY), FALSE);
960         g_return_val_if_fail (e_credentials_has_key (credentials, E_CREDENTIALS_KEY_PROMPT_TEXT), FALSE);
961
962         if (e_credentials_has_key (credentials, E_CREDENTIALS_KEY_PROMPT_FLAGS)) {
963                 prompt_flags = e_credentials_util_string_to_prompt_flags (e_credentials_peek (credentials, E_CREDENTIALS_KEY_PROMPT_FLAGS));
964         } else {
965                 prompt_flags = E_CREDENTIALS_PROMPT_FLAG_REMEMBER_FOREVER
966                              | E_CREDENTIALS_PROMPT_FLAG_SECRET
967                              | E_CREDENTIALS_PROMPT_FLAG_ONLINE;
968         }
969
970         if (!remember_password) {
971                 prompt_flags |= E_CREDENTIALS_PROMPT_FLAG_DISABLE_REMEMBER;
972                 remember_password = &fake_remember_password;
973         }
974
975         if (e_credentials_has_key (credentials, E_CREDENTIALS_KEY_PASSWORD))
976                 prompt_flags |= E_CREDENTIALS_PROMPT_FLAG_REPROMPT;
977
978         if (e_credentials_has_key (credentials, E_CREDENTIALS_KEY_PROMPT_TITLE))
979                 title = e_credentials_peek (credentials, E_CREDENTIALS_KEY_PROMPT_TITLE);
980         else if (prompt_flags & E_CREDENTIALS_PROMPT_FLAG_PASSPHRASE)
981                 title = _("Enter Passphrase");
982         else
983                 title = _("Enter Password");
984
985         prompt_key = e_credentials_peek (credentials, E_CREDENTIALS_KEY_PROMPT_KEY);
986
987         if (!(prompt_flags & E_CREDENTIALS_PROMPT_FLAG_REPROMPT))
988                 password = e_passwords_get_password (NULL, prompt_key);
989
990         if (!password)
991                 password = e_passwords_ask_password (title, NULL, prompt_key,
992                                 e_credentials_peek (credentials, E_CREDENTIALS_KEY_PROMPT_TEXT),
993                                 prompt_flags, remember_password, parent);
994
995         res = password != NULL;
996
997         if (res)
998                 e_credentials_set (credentials, E_CREDENTIALS_KEY_PASSWORD, password);
999
1000         e_credentials_util_safe_free_string (password);
1001         e_credentials_clear_peek (credentials);
1002
1003         return res;
1004 }
1005
1006 /**
1007  * e_credentials_forget_password:
1008  * @credentials: an #ECredentials
1009  *
1010  * Forgets stored password for given @credentials, which should contain
1011  * E_CREDENTIALS_KEY_PROMPT_KEY.
1012  *
1013  * Since: 3.2
1014  **/
1015 void
1016 e_credentials_forget_password (const ECredentials *credentials)
1017 {
1018         gchar *prompt_key;
1019
1020         g_return_if_fail (credentials != NULL);
1021         g_return_if_fail (e_credentials_has_key (credentials, E_CREDENTIALS_KEY_PROMPT_KEY));
1022
1023         prompt_key = e_credentials_get (credentials, E_CREDENTIALS_KEY_PROMPT_KEY);
1024
1025         e_passwords_forget_password (NULL, prompt_key);
1026
1027         g_free (prompt_key);
1028 }