1 /*
2  * e-mail-config-service-backend.c
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
11  * for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program; if not, see <http://www.gnu.org/licenses/>.
15  *
16  */
17 
18 #include "e-mail-config-service-backend.h"
19 
20 #include <mail/e-mail-config-receiving-page.h>
21 #include <mail/e-mail-config-sending-page.h>
22 
23 #define E_MAIL_CONFIG_SERVICE_BACKEND_GET_PRIVATE(obj) \
24 	(G_TYPE_INSTANCE_GET_PRIVATE \
25 	((obj), E_TYPE_MAIL_CONFIG_SERVICE_BACKEND, EMailConfigServiceBackendPrivate))
26 
27 struct _EMailConfigServiceBackendPrivate {
28 	ESource *source;
29 	ESource *collection;
30 };
31 
32 enum {
33 	PROP_0,
34 	PROP_COLLECTION,
35 	PROP_SELECTABLE,
36 	PROP_SOURCE
37 };
38 
G_DEFINE_ABSTRACT_TYPE(EMailConfigServiceBackend,e_mail_config_service_backend,E_TYPE_EXTENSION)39 G_DEFINE_ABSTRACT_TYPE (
40 	EMailConfigServiceBackend,
41 	e_mail_config_service_backend,
42 	E_TYPE_EXTENSION)
43 
44 static void
45 mail_config_service_backend_init_collection (EMailConfigServiceBackend *backend)
46 {
47 	EMailConfigServiceBackendClass *class;
48 
49 	/* Use the new_collection() method to initialize the "collection"
50 	 * property.  This assumes we're editing a new account.  If we're
51 	 * editing an existing account, the initial "collection" property
52 	 * value should be overridden with the existing collection source. */
53 
54 	g_return_if_fail (backend->priv->collection == NULL);
55 
56 	class = E_MAIL_CONFIG_SERVICE_BACKEND_GET_CLASS (backend);
57 	g_return_if_fail (class != NULL);
58 	g_return_if_fail (class->new_collection != NULL);
59 
60 	backend->priv->collection = class->new_collection (backend);
61 }
62 
63 static void
mail_config_service_backend_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)64 mail_config_service_backend_set_property (GObject *object,
65                                           guint property_id,
66                                           const GValue *value,
67                                           GParamSpec *pspec)
68 {
69 	switch (property_id) {
70 		case PROP_COLLECTION:
71 			e_mail_config_service_backend_set_collection (
72 				E_MAIL_CONFIG_SERVICE_BACKEND (object),
73 				g_value_get_object (value));
74 			return;
75 
76 		case PROP_SOURCE:
77 			e_mail_config_service_backend_set_source (
78 				E_MAIL_CONFIG_SERVICE_BACKEND (object),
79 				g_value_get_object (value));
80 			return;
81 	}
82 
83 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
84 }
85 
86 static void
mail_config_service_backend_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)87 mail_config_service_backend_get_property (GObject *object,
88                                           guint property_id,
89                                           GValue *value,
90                                           GParamSpec *pspec)
91 {
92 	switch (property_id) {
93 		case PROP_COLLECTION:
94 			g_value_set_object (
95 				value,
96 				e_mail_config_service_backend_get_collection (
97 				E_MAIL_CONFIG_SERVICE_BACKEND (object)));
98 			return;
99 
100 		case PROP_SELECTABLE:
101 			g_value_set_boolean (
102 				value,
103 				e_mail_config_service_backend_get_selectable (
104 				E_MAIL_CONFIG_SERVICE_BACKEND (object)));
105 			return;
106 
107 		case PROP_SOURCE:
108 			g_value_set_object (
109 				value,
110 				e_mail_config_service_backend_get_source (
111 				E_MAIL_CONFIG_SERVICE_BACKEND (object)));
112 			return;
113 	}
114 
115 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
116 }
117 
118 static void
mail_config_service_backend_dispose(GObject * object)119 mail_config_service_backend_dispose (GObject *object)
120 {
121 	EMailConfigServiceBackendPrivate *priv;
122 
123 	priv = E_MAIL_CONFIG_SERVICE_BACKEND_GET_PRIVATE (object);
124 	g_clear_object (&priv->source);
125 	g_clear_object (&priv->collection);
126 
127 	/* Chain up to parent's dispose() method. */
128 	G_OBJECT_CLASS (e_mail_config_service_backend_parent_class)->
129 		dispose (object);
130 }
131 
132 static void
mail_config_service_backend_constructed(GObject * object)133 mail_config_service_backend_constructed (GObject *object)
134 {
135 	EMailConfigServiceBackend *backend;
136 
137 	backend = E_MAIL_CONFIG_SERVICE_BACKEND (object);
138 	mail_config_service_backend_init_collection (backend);
139 
140 	/* Chain up to parent's constructed() method. */
141 	G_OBJECT_CLASS (e_mail_config_service_backend_parent_class)->constructed (object);
142 }
143 
144 static gboolean
mail_config_service_backend_get_selectable(EMailConfigServiceBackend * backend)145 mail_config_service_backend_get_selectable (EMailConfigServiceBackend *backend)
146 {
147 	EMailConfigServicePage *page;
148 	CamelProvider *provider;
149 	gboolean selectable = TRUE;
150 
151 	page = e_mail_config_service_backend_get_page (backend);
152 	provider = e_mail_config_service_backend_get_provider (backend);
153 
154 	if (CAMEL_PROVIDER_IS_STORE_AND_TRANSPORT (provider))
155 		selectable = E_IS_MAIL_CONFIG_RECEIVING_PAGE (page);
156 
157 	return selectable;
158 }
159 
160 static ESource *
mail_config_service_backend_new_collection(EMailConfigServiceBackend * backend)161 mail_config_service_backend_new_collection (EMailConfigServiceBackend *backend)
162 {
163 	/* This is typically only used for groupware backends. */
164 	return NULL;
165 }
166 
167 static void
mail_config_service_backend_insert_widgets(EMailConfigServiceBackend * backend,GtkBox * parent)168 mail_config_service_backend_insert_widgets (EMailConfigServiceBackend *backend,
169                                             GtkBox *parent)
170 {
171 	/* does nothing */
172 }
173 
174 static void
mail_config_service_backend_setup_defaults(EMailConfigServiceBackend * backend)175 mail_config_service_backend_setup_defaults (EMailConfigServiceBackend *backend)
176 {
177 	/* does nothing */
178 }
179 
180 static gboolean
mail_config_service_backend_auto_configure(EMailConfigServiceBackend * backend,EConfigLookup * config_lookup,gint * out_priority,gboolean * out_is_complete)181 mail_config_service_backend_auto_configure (EMailConfigServiceBackend *backend,
182 					    EConfigLookup *config_lookup,
183 					    gint *out_priority,
184 					    gboolean *out_is_complete)
185 {
186 	return FALSE;
187 }
188 
189 static gboolean
mail_config_service_backend_check_complete(EMailConfigServiceBackend * backend)190 mail_config_service_backend_check_complete (EMailConfigServiceBackend *backend)
191 {
192 	return TRUE;
193 }
194 
195 static void
mail_config_service_backend_commit_changes(EMailConfigServiceBackend * backend)196 mail_config_service_backend_commit_changes (EMailConfigServiceBackend *backend)
197 {
198 	/* does nothing */
199 }
200 
201 static void
e_mail_config_service_backend_class_init(EMailConfigServiceBackendClass * class)202 e_mail_config_service_backend_class_init (EMailConfigServiceBackendClass *class)
203 {
204 	GObjectClass *object_class;
205 	EExtensionClass *extension_class;
206 
207 	g_type_class_add_private (
208 		class, sizeof (EMailConfigServiceBackendPrivate));
209 
210 	object_class = G_OBJECT_CLASS (class);
211 	object_class->set_property = mail_config_service_backend_set_property;
212 	object_class->get_property = mail_config_service_backend_get_property;
213 	object_class->dispose = mail_config_service_backend_dispose;
214 	object_class->constructed = mail_config_service_backend_constructed;
215 
216 	extension_class = E_EXTENSION_CLASS (class);
217 	extension_class->extensible_type = E_TYPE_MAIL_CONFIG_SERVICE_PAGE;
218 
219 	class->get_selectable = mail_config_service_backend_get_selectable;
220 	class->new_collection = mail_config_service_backend_new_collection;
221 	class->insert_widgets = mail_config_service_backend_insert_widgets;
222 	class->setup_defaults = mail_config_service_backend_setup_defaults;
223 	class->auto_configure = mail_config_service_backend_auto_configure;
224 	class->check_complete = mail_config_service_backend_check_complete;
225 	class->commit_changes = mail_config_service_backend_commit_changes;
226 
227 	g_object_class_install_property (
228 		object_class,
229 		PROP_COLLECTION,
230 		g_param_spec_object (
231 			"collection",
232 			"Collection",
233 			"Optional collection ESource",
234 			E_TYPE_SOURCE,
235 			G_PARAM_READWRITE |
236 			G_PARAM_STATIC_STRINGS));
237 
238 	g_object_class_install_property (
239 		object_class,
240 		PROP_SELECTABLE,
241 		g_param_spec_boolean (
242 			"selectable",
243 			"Selectable",
244 			"Whether the backend is user selectable",
245 			TRUE,  /* not applied */
246 			G_PARAM_READABLE |
247 			G_PARAM_STATIC_STRINGS));
248 
249 	g_object_class_install_property (
250 		object_class,
251 		PROP_SOURCE,
252 		g_param_spec_object (
253 			"source",
254 			"Source",
255 			"The ESource being edited",
256 			E_TYPE_SOURCE,
257 			G_PARAM_READWRITE |
258 			G_PARAM_STATIC_STRINGS));
259 }
260 
261 static void
e_mail_config_service_backend_init(EMailConfigServiceBackend * backend)262 e_mail_config_service_backend_init (EMailConfigServiceBackend *backend)
263 {
264 	backend->priv = E_MAIL_CONFIG_SERVICE_BACKEND_GET_PRIVATE (backend);
265 }
266 
267 EMailConfigServicePage *
e_mail_config_service_backend_get_page(EMailConfigServiceBackend * backend)268 e_mail_config_service_backend_get_page (EMailConfigServiceBackend *backend)
269 {
270 	EExtensible *extensible;
271 
272 	g_return_val_if_fail (E_IS_MAIL_CONFIG_SERVICE_BACKEND (backend), NULL);
273 
274 	extensible = e_extension_get_extensible (E_EXTENSION (backend));
275 
276 	return E_MAIL_CONFIG_SERVICE_PAGE (extensible);
277 }
278 
279 ESource *
e_mail_config_service_backend_get_source(EMailConfigServiceBackend * backend)280 e_mail_config_service_backend_get_source (EMailConfigServiceBackend *backend)
281 {
282 	g_return_val_if_fail (E_IS_MAIL_CONFIG_SERVICE_BACKEND (backend), NULL);
283 
284 	return backend->priv->source;
285 }
286 
287 void
e_mail_config_service_backend_set_source(EMailConfigServiceBackend * backend,ESource * source)288 e_mail_config_service_backend_set_source (EMailConfigServiceBackend *backend,
289                                           ESource *source)
290 {
291 	g_return_if_fail (E_IS_MAIL_CONFIG_SERVICE_BACKEND (backend));
292 
293 	if (backend->priv->source == source)
294 		return;
295 
296 	if (source != NULL) {
297 		g_return_if_fail (E_IS_SOURCE (source));
298 		g_object_ref (source);
299 	}
300 
301 	if (backend->priv->source != NULL)
302 		g_object_unref (backend->priv->source);
303 
304 	backend->priv->source = source;
305 
306 	g_object_notify (G_OBJECT (backend), "source");
307 }
308 
309 ESource *
e_mail_config_service_backend_get_collection(EMailConfigServiceBackend * backend)310 e_mail_config_service_backend_get_collection (EMailConfigServiceBackend *backend)
311 {
312 	g_return_val_if_fail (E_IS_MAIL_CONFIG_SERVICE_BACKEND (backend), NULL);
313 
314 	return backend->priv->collection;
315 }
316 
317 void
e_mail_config_service_backend_set_collection(EMailConfigServiceBackend * backend,ESource * collection)318 e_mail_config_service_backend_set_collection (EMailConfigServiceBackend *backend,
319                                               ESource *collection)
320 {
321 	g_return_if_fail (E_IS_MAIL_CONFIG_SERVICE_BACKEND (backend));
322 
323 	if (backend->priv->collection == collection)
324 		return;
325 
326 	if (collection != NULL) {
327 		g_return_if_fail (E_IS_SOURCE (collection));
328 		g_object_ref (collection);
329 	}
330 
331 	if (backend->priv->collection != NULL)
332 		g_object_unref (backend->priv->collection);
333 
334 	backend->priv->collection = collection;
335 
336 	g_object_notify (G_OBJECT (backend), "collection");
337 }
338 
339 CamelProvider *
e_mail_config_service_backend_get_provider(EMailConfigServiceBackend * backend)340 e_mail_config_service_backend_get_provider (EMailConfigServiceBackend *backend)
341 {
342 	EMailConfigServiceBackendClass *class;
343 
344 	g_return_val_if_fail (E_IS_MAIL_CONFIG_SERVICE_BACKEND (backend), NULL);
345 
346 	class = E_MAIL_CONFIG_SERVICE_BACKEND_GET_CLASS (backend);
347 	g_return_val_if_fail (class != NULL, NULL);
348 	g_return_val_if_fail (class->backend_name != NULL, NULL);
349 
350 	return camel_provider_get (class->backend_name, NULL);
351 }
352 
353 CamelSettings *
e_mail_config_service_backend_get_settings(EMailConfigServiceBackend * backend)354 e_mail_config_service_backend_get_settings (EMailConfigServiceBackend *backend)
355 {
356 	ESource *source;
357 	ESourceCamel *camel_extension = NULL;
358 	EMailConfigServicePage *page;
359 	EMailConfigServicePageClass *page_class;
360 
361 	g_return_val_if_fail (E_IS_MAIL_CONFIG_SERVICE_BACKEND (backend), NULL);
362 
363 	page = e_mail_config_service_backend_get_page (backend);
364 	page_class = E_MAIL_CONFIG_SERVICE_PAGE_GET_CLASS (page);
365 
366 	/* Which ESource do we pull the CamelSettings from?  This is a
367 	 * little tricky because we have to handle the following cases:
368 	 *
369 	 *   1) A stand-alone mail account.
370 	 *
371 	 *   2) A collection with a specialized backend (e.g. ews).
372 	 *
373 	 *   3) A collection that uses standard backends (e.g. yahoo).
374 	 *
375 	 * So the semantics are as follows.  They work for now but may
376 	 * need further tweaking as we support more collection types.
377 	 *
378 	 *   1) If the service backend defines a collection source,
379 	 *      assume the CamelSettings will be pulled from there.
380 	 *
381 	 *   2) If we have a collection source, try extracting the
382 	 *      ESourceCamel extension for the collection source's
383 	 *      backend name.
384 	 *
385 	 *   3) If steps 1 or 2 fail, pull the CamelSettings from
386 	 *      the service backend's own scratch source.
387 	 */
388 
389 	source = e_mail_config_service_backend_get_collection (backend);
390 	if (source != NULL) {
391 		ESourceBackend *backend_extension;
392 		const gchar *backend_name;
393 		const gchar *extension_name;
394 
395 		extension_name = E_SOURCE_EXTENSION_COLLECTION;
396 		backend_extension =
397 			e_source_get_extension (source, extension_name);
398 		backend_name =
399 			e_source_backend_get_backend_name (backend_extension);
400 
401 		/* XXX ESourceCollection's default backend name is "none".
402 		 *     Unfortunately so is CamelNullStore's provider name.
403 		 *     Make sure these two misfits don't get paired up! */
404 		if (g_strcmp0 (backend_name, "none") != 0) {
405 			extension_name =
406 				e_source_camel_get_extension_name (backend_name);
407 			camel_extension =
408 				e_source_get_extension (source, extension_name);
409 		}
410 	}
411 
412 	if (camel_extension == NULL) {
413 		ESourceBackend *backend_extension;
414 		const gchar *backend_name;
415 		const gchar *extension_name;
416 
417 		source = e_mail_config_service_backend_get_source (backend);
418 
419 		extension_name = page_class->extension_name;
420 		backend_extension =
421 			e_source_get_extension (source, extension_name);
422 		backend_name =
423 			e_source_backend_get_backend_name (backend_extension);
424 
425 		extension_name =
426 			e_source_camel_get_extension_name (backend_name);
427 		camel_extension =
428 			e_source_get_extension (source, extension_name);
429 	}
430 
431 	return e_source_camel_get_settings (camel_extension);
432 }
433 
434 gboolean
e_mail_config_service_backend_get_selectable(EMailConfigServiceBackend * backend)435 e_mail_config_service_backend_get_selectable (EMailConfigServiceBackend *backend)
436 {
437 	EMailConfigServiceBackendClass *class;
438 
439 	g_return_val_if_fail (E_IS_MAIL_CONFIG_SERVICE_BACKEND (backend), FALSE);
440 
441 	class = E_MAIL_CONFIG_SERVICE_BACKEND_GET_CLASS (backend);
442 	g_return_val_if_fail (class != NULL, FALSE);
443 	g_return_val_if_fail (class->get_selectable != NULL, FALSE);
444 
445 	return class->get_selectable (backend);
446 }
447 
448 void
e_mail_config_service_backend_insert_widgets(EMailConfigServiceBackend * backend,GtkBox * parent)449 e_mail_config_service_backend_insert_widgets (EMailConfigServiceBackend *backend,
450                                               GtkBox *parent)
451 {
452 	EMailConfigServiceBackendClass *class;
453 
454 	g_return_if_fail (E_IS_MAIL_CONFIG_SERVICE_BACKEND (backend));
455 	g_return_if_fail (GTK_IS_BOX (parent));
456 
457 	class = E_MAIL_CONFIG_SERVICE_BACKEND_GET_CLASS (backend);
458 	g_return_if_fail (class != NULL);
459 	g_return_if_fail (class->insert_widgets != NULL);
460 
461 	class->insert_widgets (backend, parent);
462 }
463 
464 void
e_mail_config_service_backend_setup_defaults(EMailConfigServiceBackend * backend)465 e_mail_config_service_backend_setup_defaults (EMailConfigServiceBackend *backend)
466 {
467 	EMailConfigServiceBackendClass *class;
468 
469 	g_return_if_fail (E_IS_MAIL_CONFIG_SERVICE_BACKEND (backend));
470 
471 	class = E_MAIL_CONFIG_SERVICE_BACKEND_GET_CLASS (backend);
472 	g_return_if_fail (class != NULL);
473 	g_return_if_fail (class->setup_defaults != NULL);
474 
475 	return class->setup_defaults (backend);
476 }
477 
478 gboolean
e_mail_config_service_backend_auto_configure(EMailConfigServiceBackend * backend,EConfigLookup * config_lookup,gint * out_priority,gboolean * out_is_complete)479 e_mail_config_service_backend_auto_configure (EMailConfigServiceBackend *backend,
480 					      EConfigLookup *config_lookup,
481 					      gint *out_priority,
482 					      gboolean *out_is_complete)
483 {
484 	EMailConfigServiceBackendClass *class;
485 
486 	g_return_val_if_fail (E_IS_MAIL_CONFIG_SERVICE_BACKEND (backend), FALSE);
487 	g_return_val_if_fail (E_IS_CONFIG_LOOKUP (config_lookup), FALSE);
488 
489 	class = E_MAIL_CONFIG_SERVICE_BACKEND_GET_CLASS (backend);
490 	g_return_val_if_fail (class != NULL, FALSE);
491 	g_return_val_if_fail (class->auto_configure != NULL, FALSE);
492 
493 	return class->auto_configure (backend, config_lookup, out_priority, out_is_complete);
494 }
495 
496 gboolean
e_mail_config_service_backend_check_complete(EMailConfigServiceBackend * backend)497 e_mail_config_service_backend_check_complete (EMailConfigServiceBackend *backend)
498 {
499 	EMailConfigServiceBackendClass *class;
500 
501 	g_return_val_if_fail (E_IS_MAIL_CONFIG_SERVICE_BACKEND (backend), FALSE);
502 
503 	class = E_MAIL_CONFIG_SERVICE_BACKEND_GET_CLASS (backend);
504 	g_return_val_if_fail (class != NULL, FALSE);
505 	g_return_val_if_fail (class->check_complete != NULL, FALSE);
506 
507 	return class->check_complete (backend);
508 }
509 
510 void
e_mail_config_service_backend_commit_changes(EMailConfigServiceBackend * backend)511 e_mail_config_service_backend_commit_changes (EMailConfigServiceBackend *backend)
512 {
513 	EMailConfigServiceBackendClass *class;
514 
515 	g_return_if_fail (E_IS_MAIL_CONFIG_SERVICE_BACKEND (backend));
516 
517 	class = E_MAIL_CONFIG_SERVICE_BACKEND_GET_CLASS (backend);
518 	g_return_if_fail (class != NULL);
519 	g_return_if_fail (class->commit_changes != NULL);
520 
521 	class->commit_changes (backend);
522 }
523 
524 /*
525  * e_mail_config_service_backend_auto_configure_for_kind:
526  * @backend: an #EMailConfigServiceBackend
527  * @config_lookup: an #EConfigLookup
528  * @kind: an #EConfigLookupResultKind
529  * @protocol: (nullable): optional protocol name, or %NULL
530  * @source: (nullable): optional #ESource to configure, or %NULL
531  * @out_priority: (out) (nullable): priority of the chosen lookup result
532  * @out_is_complete: (out) (nullable): whether the config is complete
533  *
534  * Finds a config lookup result for the given @kind and @protocol and
535  * configures the @source with it. The @out_priority is set to the priority
536  * of that lookup result.
537  *
538  * If no @protocol is given, then the backend name of the @backend is used.
539  * If no @source is given, then gets it with e_mail_config_service_backend_get_source().
540  *
541  * Returns: whether applied any changes
542  *
543  * Since: 3.26
544  */
545 gboolean
e_mail_config_service_backend_auto_configure_for_kind(EMailConfigServiceBackend * backend,EConfigLookup * config_lookup,EConfigLookupResultKind kind,const gchar * protocol,ESource * source,gint * out_priority,gboolean * out_is_complete)546 e_mail_config_service_backend_auto_configure_for_kind (EMailConfigServiceBackend *backend,
547 						       EConfigLookup *config_lookup,
548 						       EConfigLookupResultKind kind,
549 						       const gchar *protocol,
550 						       ESource *source,
551 						       gint *out_priority,
552 						       gboolean *out_is_complete)
553 {
554 	EMailConfigServiceBackendClass *klass;
555 	GSList *results;
556 	gboolean changed = FALSE;
557 
558 	g_return_val_if_fail (E_IS_MAIL_CONFIG_SERVICE_BACKEND (backend), FALSE);
559 	g_return_val_if_fail (E_IS_CONFIG_LOOKUP (config_lookup), FALSE);
560 	g_return_val_if_fail (kind != E_CONFIG_LOOKUP_RESULT_UNKNOWN, FALSE);
561 
562 	klass = E_MAIL_CONFIG_SERVICE_BACKEND_GET_CLASS (backend);
563 	g_return_val_if_fail (klass != NULL, FALSE);
564 	g_return_val_if_fail (klass->backend_name != NULL, FALSE);
565 
566 	if (!source)
567 		source = e_mail_config_service_backend_get_source (backend);
568 	if (!protocol)
569 		protocol = klass->backend_name;
570 
571 	results = e_config_lookup_dup_results (config_lookup, kind, protocol);
572 	results = g_slist_sort (results, e_config_lookup_result_compare);
573 
574 	if (results && results->data) {
575 		EConfigLookupResult *lookup_result = results->data;
576 
577 		changed = e_config_lookup_result_configure_source (lookup_result, config_lookup, source);
578 
579 		if (changed) {
580 			if (out_priority)
581 				*out_priority = e_config_lookup_result_get_priority (lookup_result);
582 
583 			if (out_is_complete)
584 				*out_is_complete = e_config_lookup_result_get_is_complete (lookup_result);
585 		}
586 	}
587 
588 	g_slist_free_full (results, g_object_unref);
589 
590 	return changed;
591 }
592