1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com)
4 *
5 * This library is free software: you can redistribute it and/or modify it
6 * under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation.
8 *
9 * This library is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 /**
19 * SECTION: e-config-lookup
20 * @include: e-util/e-util.h
21 * @short_description: Configuration lookup
22 *
23 * #EConfigLookup is used to search for configuration of an account,
24 * which is identified by an e-mail address, server address or such.
25 * It is an #EExtensible object, where the extensions connect to
26 * the #EConfigLookup::run signal to run the configuration lookup.
27 **/
28
29 #include "evolution-config.h"
30
31 #include <glib/gi18n-lib.h>
32 #include <camel/camel.h>
33 #include <libedataserver/libedataserver.h>
34
35 #include "e-config-lookup-result.h"
36 #include "e-config-lookup-worker.h"
37 #include "e-simple-async-result.h"
38 #include "e-util-enumtypes.h"
39
40 #include "e-config-lookup.h"
41
42 struct _EConfigLookupPrivate {
43 ESourceRegistry *registry;
44
45 GMutex property_lock;
46 GSList *workers; /* EConfigLookupWorker * */
47 GSList *results; /* EConfigLookupResult * */
48
49 ESimpleAsyncResult *run_result;
50 GCancellable *run_cancellable;
51 GSList *worker_cancellables; /* CamelOperation * */
52
53 GThreadPool *pool;
54 };
55
56 enum {
57 PROP_0,
58 PROP_REGISTRY,
59 PROP_BUSY
60 };
61
62 enum {
63 GET_SOURCE,
64 WORKER_STARTED,
65 WORKER_FINISHED,
66 RESULT_ADDED,
67 LAST_SIGNAL
68 };
69
70 static guint signals[LAST_SIGNAL];
71
72 G_DEFINE_TYPE_WITH_CODE (EConfigLookup, e_config_lookup, G_TYPE_OBJECT,
73 G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL))
74
75 enum {
76 EMIT_BUSY = 1 << 0,
77 EMIT_WORKER_STARTED = 1 << 1,
78 EMIT_WORKER_FINISHED = 1 << 2
79 };
80
81 typedef struct _EmitData {
82 EConfigLookup *config_lookup;
83 EConfigLookupWorker *worker;
84 guint32 flags;
85 GCancellable *cancellable;
86 ENamedParameters *params;
87 GError *error;
88 } EmitData;
89
90 static void
emit_data_free(gpointer ptr)91 emit_data_free (gpointer ptr)
92 {
93 EmitData *ed = ptr;
94
95 if (ed) {
96 e_named_parameters_free (ed->params);
97 g_clear_object (&ed->config_lookup);
98 g_clear_object (&ed->worker);
99 g_clear_object (&ed->cancellable);
100 g_clear_error (&ed->error);
101 g_slice_free (EmitData, ed);
102 }
103 }
104
105 static gboolean
config_lookup_emit_idle_cb(gpointer user_data)106 config_lookup_emit_idle_cb (gpointer user_data)
107 {
108 EmitData *ed = user_data;
109
110 g_return_val_if_fail (ed != NULL, FALSE);
111 g_return_val_if_fail (E_IS_CONFIG_LOOKUP (ed->config_lookup), FALSE);
112
113 if ((ed->flags & EMIT_WORKER_STARTED) != 0)
114 g_signal_emit (ed->config_lookup, signals[WORKER_STARTED], 0, ed->worker, ed->cancellable);
115
116 if ((ed->flags & EMIT_WORKER_FINISHED) != 0)
117 g_signal_emit (ed->config_lookup, signals[WORKER_FINISHED], 0, ed->worker, ed->params, ed->error);
118
119 if ((ed->flags & EMIT_BUSY) != 0)
120 g_object_notify (G_OBJECT (ed->config_lookup), "busy");
121
122 return FALSE;
123 }
124
125 static void
config_lookup_schedule_emit_idle(EConfigLookup * config_lookup,guint32 emit_flags,EConfigLookupWorker * worker,GCancellable * cancellable,const ENamedParameters * params,const GError * error)126 config_lookup_schedule_emit_idle (EConfigLookup *config_lookup,
127 guint32 emit_flags,
128 EConfigLookupWorker *worker,
129 GCancellable *cancellable,
130 const ENamedParameters *params,
131 const GError *error)
132 {
133 EmitData *ed;
134
135 g_return_if_fail (E_IS_CONFIG_LOOKUP (config_lookup));
136 if (worker)
137 g_return_if_fail (E_IS_CONFIG_LOOKUP_WORKER (worker));
138
139 ed = g_slice_new0 (EmitData);
140 ed->config_lookup = g_object_ref (config_lookup);
141 ed->flags = emit_flags;
142 ed->worker = worker ? g_object_ref (worker) : NULL;
143 ed->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
144 ed->params = params ? e_named_parameters_new_clone (params) : NULL;
145 ed->error = error ? g_error_copy (error) : NULL;
146
147 g_idle_add_full (G_PRIORITY_HIGH_IDLE, config_lookup_emit_idle_cb, ed, emit_data_free);
148 }
149
150 typedef struct _ThreadData {
151 ENamedParameters *params;
152 EConfigLookupWorker *worker;
153 GCancellable *cancellable;
154 } ThreadData;
155
156 static void
config_lookup_thread(gpointer data,gpointer user_data)157 config_lookup_thread (gpointer data,
158 gpointer user_data)
159 {
160 ThreadData *td = data;
161 EConfigLookup *config_lookup = user_data;
162 ESimpleAsyncResult *run_result = NULL;
163 guint32 emit_flags;
164 ENamedParameters *restart_params = NULL;
165 GError *error = NULL;
166
167 g_return_if_fail (td != NULL);
168 g_return_if_fail (td->params != NULL);
169 g_return_if_fail (E_IS_CONFIG_LOOKUP_WORKER (td->worker));
170 g_return_if_fail (G_IS_CANCELLABLE (td->cancellable));
171 g_return_if_fail (E_IS_CONFIG_LOOKUP (config_lookup));
172
173 e_config_lookup_worker_run (td->worker, config_lookup, td->params, &restart_params, td->cancellable, &error);
174
175 g_mutex_lock (&config_lookup->priv->property_lock);
176
177 emit_flags = EMIT_WORKER_FINISHED;
178
179 if (g_slist_find (config_lookup->priv->worker_cancellables, td->cancellable)) {
180 config_lookup->priv->worker_cancellables = g_slist_remove (config_lookup->priv->worker_cancellables, td->cancellable);
181 g_object_unref (td->cancellable);
182
183 if (!config_lookup->priv->worker_cancellables)
184 emit_flags |= EMIT_BUSY;
185 }
186
187 config_lookup_schedule_emit_idle (config_lookup, emit_flags, td->worker, NULL, restart_params, error);
188
189 if ((emit_flags & EMIT_BUSY) != 0) {
190 run_result = config_lookup->priv->run_result;
191 config_lookup->priv->run_result = NULL;
192
193 g_clear_object (&config_lookup->priv->run_cancellable);
194 }
195
196 g_mutex_unlock (&config_lookup->priv->property_lock);
197
198 if (run_result) {
199 e_simple_async_result_complete_idle_take (run_result);
200 }
201
202 e_named_parameters_free (restart_params);
203 e_named_parameters_free (td->params);
204 g_clear_object (&td->worker);
205 g_clear_object (&td->cancellable);
206 g_clear_error (&error);
207 g_slice_free (ThreadData, td);
208 }
209
210 static void
config_lookup_set_registry(EConfigLookup * config_lookup,ESourceRegistry * registry)211 config_lookup_set_registry (EConfigLookup *config_lookup,
212 ESourceRegistry *registry)
213 {
214 g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
215 g_return_if_fail (config_lookup->priv->registry == NULL);
216
217 config_lookup->priv->registry = g_object_ref (registry);
218 }
219
220 static void
config_lookup_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)221 config_lookup_set_property (GObject *object,
222 guint property_id,
223 const GValue *value,
224 GParamSpec *pspec)
225 {
226 switch (property_id) {
227 case PROP_REGISTRY:
228 config_lookup_set_registry (
229 E_CONFIG_LOOKUP (object),
230 g_value_get_object (value));
231 return;
232 }
233
234 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
235 }
236
237 static void
config_lookup_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)238 config_lookup_get_property (GObject *object,
239 guint property_id,
240 GValue *value,
241 GParamSpec *pspec)
242 {
243 switch (property_id) {
244 case PROP_BUSY:
245 g_value_set_boolean (
246 value,
247 e_config_lookup_get_busy (
248 E_CONFIG_LOOKUP (object)));
249 return;
250
251 case PROP_REGISTRY:
252 g_value_set_object (
253 value,
254 e_config_lookup_get_registry (
255 E_CONFIG_LOOKUP (object)));
256 return;
257 }
258
259 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
260 }
261
262 static void
config_lookup_constructed(GObject * object)263 config_lookup_constructed (GObject *object)
264 {
265 /* Chain up to parent's method. */
266 G_OBJECT_CLASS (e_config_lookup_parent_class)->constructed (object);
267
268 e_extensible_load_extensions (E_EXTENSIBLE (object));
269 }
270
271 static void
config_lookup_dispose(GObject * object)272 config_lookup_dispose (GObject *object)
273 {
274 EConfigLookup *config_lookup = E_CONFIG_LOOKUP (object);
275 gboolean had_running_workers;
276
277 e_config_lookup_cancel_all (config_lookup);
278
279 if (config_lookup->priv->pool) {
280 g_thread_pool_free (config_lookup->priv->pool, TRUE, TRUE);
281 config_lookup->priv->pool = NULL;
282 }
283
284 g_mutex_lock (&config_lookup->priv->property_lock);
285
286 g_clear_object (&config_lookup->priv->run_cancellable);
287
288 g_slist_free_full (config_lookup->priv->workers, g_object_unref);
289 config_lookup->priv->workers = NULL;
290
291 had_running_workers = config_lookup->priv->worker_cancellables != NULL;
292 g_slist_free_full (config_lookup->priv->worker_cancellables, g_object_unref);
293 config_lookup->priv->worker_cancellables = NULL;
294
295 g_mutex_unlock (&config_lookup->priv->property_lock);
296
297 if (had_running_workers)
298 g_object_notify (object, "busy");
299
300 g_clear_object (&config_lookup->priv->registry);
301
302 /* Chain up to parent's method. */
303 G_OBJECT_CLASS (e_config_lookup_parent_class)->dispose (object);
304 }
305
306 static void
config_lookup_finalize(GObject * object)307 config_lookup_finalize (GObject *object)
308 {
309 EConfigLookup *config_lookup = E_CONFIG_LOOKUP (object);
310
311 g_slist_free_full (config_lookup->priv->results, g_object_unref);
312 g_mutex_clear (&config_lookup->priv->property_lock);
313
314 /* Chain up to parent's method. */
315 G_OBJECT_CLASS (e_config_lookup_parent_class)->finalize (object);
316 }
317
318 static void
e_config_lookup_class_init(EConfigLookupClass * klass)319 e_config_lookup_class_init (EConfigLookupClass *klass)
320 {
321 GObjectClass *object_class;
322
323 g_type_class_add_private (klass, sizeof (EConfigLookupPrivate));
324
325 object_class = G_OBJECT_CLASS (klass);
326 object_class->set_property = config_lookup_set_property;
327 object_class->get_property = config_lookup_get_property;
328 object_class->constructed = config_lookup_constructed;
329 object_class->dispose = config_lookup_dispose;
330 object_class->finalize = config_lookup_finalize;
331
332 /**
333 * EConfigLookup:registry:
334 *
335 * The #ESourceRegistry manages #ESource instances.
336 *
337 * Since: 3.26
338 **/
339 g_object_class_install_property (
340 object_class,
341 PROP_REGISTRY,
342 g_param_spec_object (
343 "registry",
344 "Registry",
345 "Data source registry",
346 E_TYPE_SOURCE_REGISTRY,
347 G_PARAM_READWRITE |
348 G_PARAM_CONSTRUCT_ONLY |
349 G_PARAM_STATIC_STRINGS));
350
351 /**
352 * EConfigLookup:busy:
353 *
354 * Whether the EConfigLookup has any running workers.
355 *
356 * Since: 3.28
357 **/
358 g_object_class_install_property (
359 object_class,
360 PROP_BUSY,
361 g_param_spec_boolean (
362 "busy",
363 "Busy",
364 NULL,
365 FALSE,
366 G_PARAM_READABLE |
367 G_PARAM_STATIC_STRINGS));
368
369 /**
370 * EConfigLookup::get-source:
371 * @kind: an #EConfigLookupSourceKind
372 *
373 * Emitted to get an #ESource of the given @kind. Return %NULL, when not available.
374 *
375 * Since: 3.26
376 **/
377 signals[GET_SOURCE] = g_signal_new (
378 "get-source",
379 G_TYPE_FROM_CLASS (klass),
380 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
381 G_STRUCT_OFFSET (EConfigLookupClass, get_source),
382 NULL, NULL,
383 NULL,
384 G_TYPE_POINTER, 1,
385 E_TYPE_CONFIG_LOOKUP_SOURCE_KIND);
386
387 /**
388 * EConfigLookup::worker-started:
389 * @worker: an #EConfigLookupWorker
390 * @cancellable: associated #GCancellable for this worker run
391 *
392 * Emitted when the @worker is about to start running.
393 * Corresponding @EConfigLookup::worker-finished is emitted when
394 * the run is finished.
395 *
396 * Note that this signal is always emitted in the main thread.
397 *
398 * Since: 3.28
399 **/
400 signals[WORKER_STARTED] = g_signal_new (
401 "worker-started",
402 G_TYPE_FROM_CLASS (klass),
403 G_SIGNAL_RUN_LAST,
404 G_STRUCT_OFFSET (EConfigLookupClass, worker_started),
405 NULL, NULL,
406 NULL,
407 G_TYPE_NONE, 2,
408 E_TYPE_CONFIG_LOOKUP_WORKER,
409 G_TYPE_CANCELLABLE);
410
411 /**
412 * EConfigLookup::worker-finished:
413 * @worker: an #EConfigLookupWorker
414 * @restart_params: an optional #ENamedParameters to use when the @worker might be restarted
415 * @error: an optional #GError with an overall result of the run
416 *
417 * Emitted when the @worker finished its running.
418 *
419 * Note that this signal is always emitted in the main thread.
420 *
421 * Since: 3.28
422 **/
423 signals[WORKER_FINISHED] = g_signal_new (
424 "worker-finished",
425 G_TYPE_FROM_CLASS (klass),
426 G_SIGNAL_RUN_LAST,
427 G_STRUCT_OFFSET (EConfigLookupClass, worker_finished),
428 NULL, NULL,
429 NULL,
430 G_TYPE_NONE, 3,
431 E_TYPE_CONFIG_LOOKUP_WORKER,
432 E_TYPE_NAMED_PARAMETERS,
433 G_TYPE_ERROR);
434
435 /**
436 * EConfigLookup::result-added:
437 * @result: an #EConfigLookupResult
438 *
439 * Emitted when a new @result is added to the config lookup.
440 *
441 * Note that this signal can be emitted in a worker's dedicated thread.
442 *
443 * Since: 3.28
444 **/
445 signals[RESULT_ADDED] = g_signal_new (
446 "result-added",
447 G_TYPE_FROM_CLASS (klass),
448 G_SIGNAL_RUN_LAST,
449 G_STRUCT_OFFSET (EConfigLookupClass, result_added),
450 NULL, NULL,
451 NULL,
452 G_TYPE_NONE, 1,
453 E_TYPE_CONFIG_LOOKUP_RESULT);
454 }
455
456 static void
e_config_lookup_init(EConfigLookup * config_lookup)457 e_config_lookup_init (EConfigLookup *config_lookup)
458 {
459 config_lookup->priv = G_TYPE_INSTANCE_GET_PRIVATE (config_lookup, E_TYPE_CONFIG_LOOKUP, EConfigLookupPrivate);
460
461 g_mutex_init (&config_lookup->priv->property_lock);
462 config_lookup->priv->pool = g_thread_pool_new (config_lookup_thread, config_lookup, 10, FALSE, NULL);
463 }
464
465 /**
466 * e_config_lookup_new:
467 * @registry: an #ESourceRegistry
468 *
469 * Creates a new #EConfigLookup instance.
470 *
471 * Returns: (transfer full): a new #EConfigLookup
472 *
473 * Since: 3.26
474 **/
475 EConfigLookup *
e_config_lookup_new(ESourceRegistry * registry)476 e_config_lookup_new (ESourceRegistry *registry)
477 {
478 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
479
480 return g_object_new (E_TYPE_CONFIG_LOOKUP,
481 "registry", registry,
482 NULL);
483 }
484
485 /**
486 * e_config_lookup_get_registry:
487 * @config_lookup: an #EConfigLookup
488 *
489 * Returns the #ESourceRegistry passed to e_config_lookup_new().
490 *
491 * Returns: (transfer none): an #ESourceRegistry
492 *
493 * Since: 3.26
494 **/
495 ESourceRegistry *
e_config_lookup_get_registry(EConfigLookup * config_lookup)496 e_config_lookup_get_registry (EConfigLookup *config_lookup)
497 {
498 g_return_val_if_fail (E_IS_CONFIG_LOOKUP (config_lookup), NULL);
499
500 return config_lookup->priv->registry;
501 }
502
503 /**
504 * e_config_lookup_get_source:
505 * @config_lookup: an #EConfigLookup
506 * @kind: one of #EConfigLookupSourceKind, except of the %E_CONFIG_LOOKUP_SOURCE_UNKNOWN
507 *
508 * Emits the #EConfigLookup::get-source signal and any listener can provide
509 * the source. The function can return %NULL, when there are no listeners
510 * or when such source is not available.
511 *
512 * Returns: (transfer none) (nullable): an #ESource of the given @kind, or %NULL, if not found
513 *
514 * Since: 3.26
515 **/
516 ESource *
e_config_lookup_get_source(EConfigLookup * config_lookup,EConfigLookupSourceKind kind)517 e_config_lookup_get_source (EConfigLookup *config_lookup,
518 EConfigLookupSourceKind kind)
519 {
520 ESource *source = NULL;
521
522 g_return_val_if_fail (E_IS_CONFIG_LOOKUP (config_lookup), NULL);
523
524 g_signal_emit (config_lookup, signals[GET_SOURCE], 0, kind, &source);
525
526 return source;
527 }
528
529 /**
530 * e_config_lookup_get_busy:
531 * @config_lookup: an #EConfigLookup
532 *
533 * Returns whether there's any running worker. They can be cancelled
534 * with e_config_lookup_cancel_all().
535 *
536 * Returns: whether there's any running worker
537 *
538 * Since: 3.28
539 **/
540 gboolean
e_config_lookup_get_busy(EConfigLookup * config_lookup)541 e_config_lookup_get_busy (EConfigLookup *config_lookup)
542 {
543 gboolean busy;
544
545 g_return_val_if_fail (E_IS_CONFIG_LOOKUP (config_lookup), FALSE);
546
547 g_mutex_lock (&config_lookup->priv->property_lock);
548 busy = config_lookup->priv->worker_cancellables != NULL;
549 g_mutex_unlock (&config_lookup->priv->property_lock);
550
551 return busy;
552 }
553
554 /**
555 * e_config_lookup_cancel_all:
556 * @config_lookup: an #EConfigLookup
557 *
558 * Cancels all pending workers.
559 *
560 * Since: 3.28
561 **/
562 void
e_config_lookup_cancel_all(EConfigLookup * config_lookup)563 e_config_lookup_cancel_all (EConfigLookup *config_lookup)
564 {
565 GSList *cancellables;
566 GCancellable *run_cancellable;
567
568 g_return_if_fail (E_IS_CONFIG_LOOKUP (config_lookup));
569
570 g_mutex_lock (&config_lookup->priv->property_lock);
571 cancellables = g_slist_copy_deep (config_lookup->priv->worker_cancellables, (GCopyFunc) g_object_ref, NULL);
572 run_cancellable = config_lookup->priv->run_cancellable ? g_object_ref (config_lookup->priv->run_cancellable) : NULL;
573 g_mutex_unlock (&config_lookup->priv->property_lock);
574
575 g_slist_foreach (cancellables, (GFunc) g_cancellable_cancel, NULL);
576 g_slist_free_full (cancellables, g_object_unref);
577
578 if (run_cancellable) {
579 g_cancellable_cancel (run_cancellable);
580 g_object_unref (run_cancellable);
581 }
582 }
583
584 /**
585 * e_config_lookup_register_worker:
586 * @config_lookup: an #EConfigLookup
587 * @worker: an #EConfigLookupWorker
588 *
589 * Registers a @worker as a worker, which can be run as part of e_config_lookup_run().
590 * The function adds its own reference to @worker.
591 *
592 * Since: 3.28
593 **/
594 void
e_config_lookup_register_worker(EConfigLookup * config_lookup,EConfigLookupWorker * worker)595 e_config_lookup_register_worker (EConfigLookup *config_lookup,
596 EConfigLookupWorker *worker)
597 {
598 GSList *existing_worker;
599
600 g_return_if_fail (E_IS_CONFIG_LOOKUP (config_lookup));
601 g_return_if_fail (E_IS_CONFIG_LOOKUP_WORKER (worker));
602
603 g_mutex_lock (&config_lookup->priv->property_lock);
604
605 existing_worker = g_slist_find (config_lookup->priv->workers, worker);
606
607 g_warn_if_fail (existing_worker == NULL);
608
609 if (!existing_worker)
610 config_lookup->priv->workers = g_slist_prepend (config_lookup->priv->workers, g_object_ref (worker));
611
612 g_mutex_unlock (&config_lookup->priv->property_lock);
613 }
614
615 /**
616 * e_config_lookup_unregister_worker:
617 * @config_lookup: an #EConfigLookup
618 * @worker: an #EConfigLookupWorker
619 *
620 * Removes a @worker previously registered with e_config_lookup_register_worker().
621 *
622 * Since: 3.28
623 **/
624 void
e_config_lookup_unregister_worker(EConfigLookup * config_lookup,EConfigLookupWorker * worker)625 e_config_lookup_unregister_worker (EConfigLookup *config_lookup,
626 EConfigLookupWorker *worker)
627 {
628 GSList *existing_worker;
629
630 g_return_if_fail (E_IS_CONFIG_LOOKUP (config_lookup));
631 g_return_if_fail (E_IS_CONFIG_LOOKUP_WORKER (worker));
632
633 g_mutex_lock (&config_lookup->priv->property_lock);
634
635 existing_worker = g_slist_find (config_lookup->priv->workers, worker);
636
637 g_warn_if_fail (existing_worker != NULL);
638
639 if (existing_worker) {
640 config_lookup->priv->workers = g_slist_remove (config_lookup->priv->workers, worker);
641 g_object_unref (worker);
642 }
643
644 g_mutex_unlock (&config_lookup->priv->property_lock);
645 }
646
647 /**
648 * e_config_lookup_dup_registered_workers:
649 * @config_lookup: an #EConfigLookup
650 *
651 * Returns a list of all registered #EConfigLookupWorker objects.
652 *
653 * The returned #GSList should be freed with
654 * g_slist_free_full (workers, g_object_unref);
655 * when no longer needed.
656 *
657 * Returns: (transfer full) (element-type EConfigLookupWorker): a #GSList with all
658 * workers registered with e_config_lookup_register_worker().
659 *
660 * Since: 3.28
661 **/
662 GSList *
e_config_lookup_dup_registered_workers(EConfigLookup * config_lookup)663 e_config_lookup_dup_registered_workers (EConfigLookup *config_lookup)
664 {
665 GSList *workers;
666
667 g_return_val_if_fail (E_IS_CONFIG_LOOKUP (config_lookup), NULL);
668
669 g_mutex_lock (&config_lookup->priv->property_lock);
670 workers = g_slist_copy_deep (config_lookup->priv->workers, (GCopyFunc) g_object_ref, NULL);
671 g_mutex_unlock (&config_lookup->priv->property_lock);
672
673 return workers;
674 }
675
676 /**
677 * e_config_lookup_run:
678 * @config_lookup: an #EConfigLookup
679 * @params: an #ENamedParameters with lookup parameters
680 * @cancellable: an optional #GCancellable, or %NULL
681 * @callback: a callback to call, when the run is finished
682 * @user_data: user data for the @callback
683 *
684 * Runs configuration lookup asynchronously. Once the run is done, the @callback is called,
685 * and the call can be finished with e_config_lookup_run_finish(). The @callback is always
686 * called from the main thread.
687 *
688 * Workers can be run individually using e_config_lookup_run_worker().
689 *
690 * Note that there cannot be run two lookups at the same time, thus if it
691 * happens, then the @callback is called immediately with a %NULL result.
692 *
693 * Since: 3.26
694 **/
695 void
e_config_lookup_run(EConfigLookup * config_lookup,const ENamedParameters * params,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)696 e_config_lookup_run (EConfigLookup *config_lookup,
697 const ENamedParameters *params,
698 GCancellable *cancellable,
699 GAsyncReadyCallback callback,
700 gpointer user_data)
701 {
702 GSList *workers, *link;
703
704 g_return_if_fail (E_IS_CONFIG_LOOKUP (config_lookup));
705 g_return_if_fail (params != NULL);
706
707 g_mutex_lock (&config_lookup->priv->property_lock);
708
709 if (config_lookup->priv->run_result) {
710 g_mutex_unlock (&config_lookup->priv->property_lock);
711
712 if (callback)
713 callback (G_OBJECT (config_lookup), NULL, user_data);
714 return;
715 }
716
717 g_slist_free_full (config_lookup->priv->results, g_object_unref);
718 config_lookup->priv->results = NULL;
719
720 if (cancellable)
721 g_object_ref (cancellable);
722 else
723 cancellable = g_cancellable_new ();
724
725 config_lookup->priv->run_result = e_simple_async_result_new (G_OBJECT (config_lookup), callback, user_data, e_config_lookup_run);
726 config_lookup->priv->run_cancellable = cancellable;
727
728 workers = g_slist_copy_deep (config_lookup->priv->workers, (GCopyFunc) g_object_ref, NULL);
729
730 g_mutex_unlock (&config_lookup->priv->property_lock);
731
732 if (workers) {
733 for (link = workers; link; link = g_slist_next (link)) {
734 EConfigLookupWorker *worker = link->data;
735
736 e_config_lookup_run_worker (config_lookup, worker, params, cancellable);
737 }
738
739 g_slist_free_full (workers, g_object_unref);
740 } else {
741 ESimpleAsyncResult *run_result;
742
743 g_mutex_lock (&config_lookup->priv->property_lock);
744
745 run_result = config_lookup->priv->run_result;
746 config_lookup->priv->run_result = NULL;
747
748 g_clear_object (&config_lookup->priv->run_cancellable);
749
750 g_mutex_unlock (&config_lookup->priv->property_lock);
751
752 if (run_result) {
753 e_simple_async_result_complete_idle_take (run_result);
754 }
755 }
756 }
757
758 /**
759 * e_config_lookup_run_finish:
760 * @config_lookup: an #EConfigLookup
761 * @result: result of the operation
762 *
763 * Finishes the configuration lookup previously run by e_config_lookup_run().
764 * It's expected that the extensions may fail, thus it doesn't return
765 * anything and is provided mainly for consistency with asynchronous API.
766 *
767 * Since: 3.26
768 **/
769 void
e_config_lookup_run_finish(EConfigLookup * config_lookup,GAsyncResult * result)770 e_config_lookup_run_finish (EConfigLookup *config_lookup,
771 GAsyncResult *result)
772 {
773 g_return_if_fail (E_IS_CONFIG_LOOKUP (config_lookup));
774 g_return_if_fail (G_IS_ASYNC_RESULT (result));
775 g_return_if_fail (g_async_result_is_tagged (result, e_config_lookup_run));
776 }
777
778 /**
779 * e_config_lookup_run_worker:
780 * @config_lookup: an #EConfigLookup
781 * @worker: an #EConfigLookupWorker to run in a dedicated thread
782 * @params: an #ENamedParameters with lookup parameters
783 * @cancellable: an optional #GCancellable, or %NULL
784 *
785 * Creates a new thread and runs @worker in it. When the @cancellable is %NULL,
786 * then there's creates a new #CamelOperation, which either proxies currently
787 * running lookup or the newly created cancellable is completely independent.
788 *
789 * This function can be called while there's an ongoing configuration lookup, but
790 * also when the @worker is restarted.
791 *
792 * Since: 3.28
793 **/
794 void
e_config_lookup_run_worker(EConfigLookup * config_lookup,EConfigLookupWorker * worker,const ENamedParameters * params,GCancellable * cancellable)795 e_config_lookup_run_worker (EConfigLookup *config_lookup,
796 EConfigLookupWorker *worker,
797 const ENamedParameters *params,
798 GCancellable *cancellable)
799 {
800 ThreadData *td;
801
802 g_return_if_fail (E_IS_CONFIG_LOOKUP (config_lookup));
803 g_return_if_fail (E_IS_CONFIG_LOOKUP_WORKER (worker));
804 g_return_if_fail (params != NULL);
805
806 td = g_slice_new0 (ThreadData);
807 td->params = e_named_parameters_new_clone (params);
808 td->worker = g_object_ref (worker);
809
810 g_mutex_lock (&config_lookup->priv->property_lock);
811
812 if (cancellable)
813 td->cancellable = camel_operation_new_proxy (cancellable);
814 else if (config_lookup->priv->run_cancellable)
815 td->cancellable = camel_operation_new_proxy (config_lookup->priv->run_cancellable);
816 else
817 td->cancellable = camel_operation_new ();
818
819 camel_operation_push_message (td->cancellable, "%s", _("Running…"));
820 config_lookup->priv->worker_cancellables = g_slist_prepend (config_lookup->priv->worker_cancellables, g_object_ref (td->cancellable));
821
822 config_lookup_schedule_emit_idle (config_lookup, EMIT_WORKER_STARTED |
823 (!config_lookup->priv->worker_cancellables->next ? EMIT_BUSY : 0),
824 worker, td->cancellable, NULL, NULL);
825
826 g_thread_pool_push (config_lookup->priv->pool, td, NULL);
827
828 g_mutex_unlock (&config_lookup->priv->property_lock);
829 }
830
831 /**
832 * e_config_lookup_add_result:
833 * @config_lookup: an #EConfigLookup
834 * @result: (transfer full): an #EConfigLookupResult
835 *
836 * Adds a new @result in a list of known configuration lookup results.
837 * The @config_lookup assumes ownership of the @result and frees it
838 * when no longer needed.
839 *
840 * The list of results can be obtained with e_config_lookup_dup_results().
841 *
842 * Since: 3.26
843 **/
844 void
e_config_lookup_add_result(EConfigLookup * config_lookup,EConfigLookupResult * result)845 e_config_lookup_add_result (EConfigLookup *config_lookup,
846 EConfigLookupResult *result)
847 {
848 g_return_if_fail (E_IS_CONFIG_LOOKUP (config_lookup));
849 g_return_if_fail (E_IS_CONFIG_LOOKUP_RESULT (result));
850
851 g_mutex_lock (&config_lookup->priv->property_lock);
852
853 config_lookup->priv->results = g_slist_prepend (config_lookup->priv->results, result);
854
855 g_mutex_unlock (&config_lookup->priv->property_lock);
856
857 g_signal_emit (config_lookup, signals[RESULT_ADDED], 0, result);
858 }
859
860 /**
861 * e_config_lookup_count_results:
862 * @config_lookup: an #EConfigLookup
863 *
864 * Returns: how many results had been added already.
865 *
866 * Since: 3.28
867 **/
868 gint
e_config_lookup_count_results(EConfigLookup * config_lookup)869 e_config_lookup_count_results (EConfigLookup *config_lookup)
870 {
871 gint n_results;
872
873 g_return_val_if_fail (E_IS_CONFIG_LOOKUP (config_lookup), -1);
874
875 g_mutex_lock (&config_lookup->priv->property_lock);
876
877 n_results = g_slist_length (config_lookup->priv->results);
878
879 g_mutex_unlock (&config_lookup->priv->property_lock);
880
881 return n_results;
882 }
883
884 /**
885 * e_config_lookup_dup_results:
886 * @config_lookup: an #EConfigLookup
887 * @kind: an #EConfigLookupResultKind to filter the results with
888 * @protocol: (nullable): optional protocol to filter the results with, or %NULL
889 *
890 * Returns a #GSList with #EConfigLookupResult objects satisfying
891 * the @kind and @protocol filtering conditions. To receive all
892 * gathered results use %E_CONFIG_LOOKUP_RESULT_UNKNOWN for @kind
893 * and %NULL for the @protocol.
894 *
895 * Free the returned #GSList with
896 * g_slist_free_full (results, g_object_unref);
897 * when no longer needed.
898 *
899 * Returns: (transfer full) (element-type EConfigLookupResult): a #GSList
900 * with results satisfying the @kind and @protocol filtering conditions.
901 *
902 * Since: 3.28
903 **/
904 GSList *
e_config_lookup_dup_results(EConfigLookup * config_lookup,EConfigLookupResultKind kind,const gchar * protocol)905 e_config_lookup_dup_results (EConfigLookup *config_lookup,
906 EConfigLookupResultKind kind,
907 const gchar *protocol)
908 {
909 GSList *results = NULL, *link;
910
911 g_return_val_if_fail (E_IS_CONFIG_LOOKUP (config_lookup), NULL);
912
913 g_mutex_lock (&config_lookup->priv->property_lock);
914
915 for (link = config_lookup->priv->results; link; link = g_slist_next (link)) {
916 EConfigLookupResult *result = link->data;
917
918 if (!E_IS_CONFIG_LOOKUP_RESULT (result))
919 continue;
920
921 if (kind != E_CONFIG_LOOKUP_RESULT_UNKNOWN &&
922 kind != e_config_lookup_result_get_kind (result))
923 continue;
924
925 if (protocol &&
926 g_strcmp0 (protocol, e_config_lookup_result_get_protocol (result)) != 0)
927 continue;
928
929 results = g_slist_prepend (results, g_object_ref (result));
930 }
931
932 g_mutex_unlock (&config_lookup->priv->property_lock);
933
934 return results;
935 }
936
937 /**
938 * e_config_lookup_clear_results:
939 * @config_lookup: an #EConfigLookup
940 *
941 * Frees all gathered results. This might be usually called before
942 * starting new custom lookup. The e_config_lookup_run() frees
943 * all results automatically.
944 *
945 * Since: 3.28
946 **/
947 void
e_config_lookup_clear_results(EConfigLookup * config_lookup)948 e_config_lookup_clear_results (EConfigLookup *config_lookup)
949 {
950 g_return_if_fail (E_IS_CONFIG_LOOKUP (config_lookup));
951
952 g_mutex_lock (&config_lookup->priv->property_lock);
953
954 g_slist_free_full (config_lookup->priv->results, g_object_unref);
955 config_lookup->priv->results = NULL;
956
957 g_mutex_unlock (&config_lookup->priv->property_lock);
958 }
959
960 /**
961 * e_config_lookup_encode_certificate_trust:
962 * @response: an #ETrustPromptResponse to encode
963 *
964 * Encodes @response to a string. This can be decoded back to enum
965 * with e_config_lookup_decode_certificate_trust().
966 *
967 * Returns: string representation of @response.
968 *
969 * Since: 3.28
970 **/
971 const gchar *
e_config_lookup_encode_certificate_trust(ETrustPromptResponse response)972 e_config_lookup_encode_certificate_trust (ETrustPromptResponse response)
973 {
974 return e_enum_to_string (E_TYPE_TRUST_PROMPT_RESPONSE, response);
975 }
976
977 /**
978 * e_config_lookup_decode_certificate_trust:
979 * @value: a text value to decode
980 *
981 * Decodes text @value to #ETrustPromptResponse, previously encoded
982 * with e_config_lookup_encode_certificate_trust().
983 *
984 * Returns: an #ETrustPromptResponse corresponding to @value.
985 *
986 * Since: 3.28
987 **/
988 ETrustPromptResponse
e_config_lookup_decode_certificate_trust(const gchar * value)989 e_config_lookup_decode_certificate_trust (const gchar *value)
990 {
991 gint decoded;
992
993 if (!value ||
994 !e_enum_from_string (E_TYPE_TRUST_PROMPT_RESPONSE, value, &decoded))
995 decoded = E_TRUST_PROMPT_RESPONSE_UNKNOWN;
996
997 return decoded;
998 }
999