1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* NetworkManager -- Network link manager
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
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
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Copyright 2004 - 2016 Red Hat, Inc.
19 * Copyright 2005 - 2008 Novell, Inc.
22 #include "nm-default.h"
24 #include "NetworkManagerUtils.h"
27 #include "nm-setting-connection.h"
28 #include "nm-setting-ip4-config.h"
29 #include "nm-setting-ip6-config.h"
30 #include "nm-core-internal.h"
32 #include "nm-platform.h"
33 #include "nm-exported-object.h"
34 #include "nm-auth-utils.h"
36 /******************************************************************************/
39 * nm_utils_get_shared_wifi_permission:
40 * @connection: the NMConnection to lookup the permission.
42 * Returns: a static string of the wifi-permission (if any) or %NULL.
45 nm_utils_get_shared_wifi_permission (NMConnection *connection)
47 NMSettingWireless *s_wifi;
48 NMSettingWirelessSecurity *s_wsec;
49 const char *method = NULL;
51 method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG);
52 if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED) != 0)
53 return NULL; /* Not shared */
55 s_wifi = nm_connection_get_setting_wireless (connection);
57 s_wsec = nm_connection_get_setting_wireless_security (connection);
59 return NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED;
61 return NM_AUTH_PERMISSION_WIFI_SHARE_OPEN;
67 /******************************************************************************/
70 get_new_connection_name (const GSList *existing,
71 const char *preferred,
72 const char *fallback_prefix)
78 gboolean preferred_found = FALSE;
80 g_assert (fallback_prefix);
82 for (iter = existing; iter; iter = g_slist_next (iter)) {
83 NMConnection *candidate = NM_CONNECTION (iter->data);
86 id = nm_connection_get_id (candidate);
88 names = g_slist_append (names, (gpointer) id);
90 if (preferred && !preferred_found && (strcmp (preferred, id) == 0))
91 preferred_found = TRUE;
94 /* Return the preferred name if it was unique */
95 if (preferred && !preferred_found) {
97 return g_strdup (preferred);
100 /* Otherwise find the next available unique connection name using the given
101 * connection name template.
103 while (!cname && (i++ < 10000)) {
105 gboolean found = FALSE;
107 /* Translators: the first %s is a prefix for the connection id, such
108 * as "Wired Connection" or "VPN Connection". The %d is a number
109 * that is combined with the first argument to create a unique
111 temp = g_strdup_printf (C_("connection id fallback", "%s %d"),
113 for (iter = names; iter; iter = g_slist_next (iter)) {
114 if (!strcmp (iter->data, temp)) {
125 g_slist_free (names);
130 get_new_connection_ifname (NMPlatform *platform,
131 const GSList *existing,
139 for (i = 0; i < 500; i++) {
140 name = g_strdup_printf ("%s%d", prefix, i);
142 if (nm_platform_link_get_by_ifname (platform, name))
145 for (iter = existing, found = FALSE; iter; iter = g_slist_next (iter)) {
146 NMConnection *candidate = iter->data;
148 if (g_strcmp0 (nm_connection_get_interface_name (candidate), name) == 0) {
165 nm_utils_get_ip_config_method (NMConnection *connection,
166 GType ip_setting_type)
168 NMSettingConnection *s_con;
169 NMSettingIPConfig *s_ip4, *s_ip6;
172 s_con = nm_connection_get_setting_connection (connection);
174 if (ip_setting_type == NM_TYPE_SETTING_IP4_CONFIG) {
175 g_return_val_if_fail (s_con != NULL, NM_SETTING_IP4_CONFIG_METHOD_AUTO);
177 if (nm_setting_connection_get_master (s_con))
178 return NM_SETTING_IP4_CONFIG_METHOD_DISABLED;
180 s_ip4 = nm_connection_get_setting_ip4_config (connection);
182 return NM_SETTING_IP4_CONFIG_METHOD_DISABLED;
183 method = nm_setting_ip_config_get_method (s_ip4);
184 g_return_val_if_fail (method != NULL, NM_SETTING_IP4_CONFIG_METHOD_AUTO);
189 } else if (ip_setting_type == NM_TYPE_SETTING_IP6_CONFIG) {
190 g_return_val_if_fail (s_con != NULL, NM_SETTING_IP6_CONFIG_METHOD_AUTO);
192 if (nm_setting_connection_get_master (s_con))
193 return NM_SETTING_IP6_CONFIG_METHOD_IGNORE;
195 s_ip6 = nm_connection_get_setting_ip6_config (connection);
197 return NM_SETTING_IP6_CONFIG_METHOD_IGNORE;
198 method = nm_setting_ip_config_get_method (s_ip6);
199 g_return_val_if_fail (method != NULL, NM_SETTING_IP6_CONFIG_METHOD_AUTO);
205 g_assert_not_reached ();
209 nm_utils_complete_generic (NMPlatform *platform,
210 NMConnection *connection,
212 const GSList *existing,
213 const char *preferred_id,
214 const char *fallback_id_prefix,
215 const char *ifname_prefix,
216 gboolean default_enable_ipv6)
218 NMSettingConnection *s_con;
219 char *id, *uuid, *ifname;
220 GHashTable *parameters;
222 g_assert (fallback_id_prefix);
224 s_con = nm_connection_get_setting_connection (connection);
226 s_con = (NMSettingConnection *) nm_setting_connection_new ();
227 nm_connection_add_setting (connection, NM_SETTING (s_con));
229 g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_TYPE, ctype, NULL);
231 if (!nm_setting_connection_get_uuid (s_con)) {
232 uuid = nm_utils_uuid_generate ();
233 g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_UUID, uuid, NULL);
237 /* Add a connection ID if absent */
238 if (!nm_setting_connection_get_id (s_con)) {
239 id = get_new_connection_name (existing, preferred_id, fallback_id_prefix);
240 g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_ID, id, NULL);
244 /* Add an interface name, if requested */
245 if (ifname_prefix && !nm_setting_connection_get_interface_name (s_con)) {
246 ifname = get_new_connection_ifname (platform, existing, ifname_prefix);
247 g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_INTERFACE_NAME, ifname, NULL);
252 parameters = g_hash_table_new (g_str_hash, g_str_equal);
253 g_hash_table_insert (parameters, NM_CONNECTION_NORMALIZE_PARAM_IP6_CONFIG_METHOD,
254 default_enable_ipv6 ? NM_SETTING_IP6_CONFIG_METHOD_AUTO : NM_SETTING_IP6_CONFIG_METHOD_IGNORE);
255 nm_connection_normalize (connection, parameters, NULL, NULL);
256 g_hash_table_destroy (parameters);
259 /*****************************************************************************/
262 check_property_in_hash (GHashTable *hash,
268 props = g_hash_table_lookup (hash, s_name);
270 || !g_hash_table_lookup (props, p_name)) {
277 remove_from_hash (GHashTable *s_hash,
285 g_hash_table_remove (p_hash, p_name);
286 if (g_hash_table_size (p_hash) == 0)
287 g_hash_table_remove (s_hash, s_name);
291 check_ip6_method (NMConnection *orig,
292 NMConnection *candidate,
293 GHashTable *settings)
296 const char *orig_ip6_method, *candidate_ip6_method;
297 NMSettingIPConfig *candidate_ip6;
298 gboolean allow = FALSE;
300 props = check_property_in_hash (settings,
301 NM_SETTING_IP6_CONFIG_SETTING_NAME,
302 NM_SETTING_IP_CONFIG_METHOD);
306 /* If the generated connection is 'link-local' and the candidate is both 'auto'
307 * and may-fail=TRUE, then the candidate is OK to use. may-fail is included
308 * in the decision because if the candidate is 'auto' but may-fail=FALSE, then
309 * the connection could not possibly have been previously activated on the
310 * device if the device has no non-link-local IPv6 address.
312 orig_ip6_method = nm_utils_get_ip_config_method (orig, NM_TYPE_SETTING_IP6_CONFIG);
313 candidate_ip6_method = nm_utils_get_ip_config_method (candidate, NM_TYPE_SETTING_IP6_CONFIG);
314 candidate_ip6 = nm_connection_get_setting_ip6_config (candidate);
316 if ( strcmp (orig_ip6_method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0
317 && strcmp (candidate_ip6_method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0
318 && (!candidate_ip6 || nm_setting_ip_config_get_may_fail (candidate_ip6))) {
322 /* If the generated connection method is 'link-local' or 'auto' and the candidate
323 * method is 'ignore' we can take the connection, because NM didn't simply take care
326 if ( ( strcmp (orig_ip6_method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0
327 || strcmp (orig_ip6_method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0)
328 && strcmp (candidate_ip6_method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0) {
333 remove_from_hash (settings, props,
334 NM_SETTING_IP6_CONFIG_SETTING_NAME,
335 NM_SETTING_IP_CONFIG_METHOD);
341 route_compare (NMIPRoute *route1, NMIPRoute *route2, gint64 default_metric)
343 gint64 r, metric1, metric2;
345 r = g_strcmp0 (nm_ip_route_get_dest (route1), nm_ip_route_get_dest (route2));
349 r = nm_ip_route_get_prefix (route1) - nm_ip_route_get_prefix (route2);
351 return r > 0 ? 1 : -1;
353 r = g_strcmp0 (nm_ip_route_get_next_hop (route1), nm_ip_route_get_next_hop (route2));
357 metric1 = nm_ip_route_get_metric (route1) == -1 ? default_metric : nm_ip_route_get_metric (route1);
358 metric2 = nm_ip_route_get_metric (route2) == -1 ? default_metric : nm_ip_route_get_metric (route2);
360 r = metric1 - metric2;
362 return r > 0 ? 1 : -1;
364 r = nm_ip_route_get_family (route1) - nm_ip_route_get_family (route2);
366 return r > 0 ? 1 : -1;
372 route_ptr_compare (const void *a, const void *b)
374 return route_compare (*(NMIPRoute **) a, *(NMIPRoute **) b, -1);
378 check_ip_routes (NMConnection *orig,
379 NMConnection *candidate,
380 GHashTable *settings,
381 gint64 default_metric,
384 gs_free NMIPRoute **routes1 = NULL, **routes2 = NULL;
385 NMSettingIPConfig *s_ip1, *s_ip2;
390 s_name = v4 ? NM_SETTING_IP4_CONFIG_SETTING_NAME :
391 NM_SETTING_IP6_CONFIG_SETTING_NAME;
393 props = check_property_in_hash (settings,
395 NM_SETTING_IP_CONFIG_ROUTES);
399 s_ip1 = (NMSettingIPConfig *) nm_connection_get_setting_by_name (orig, s_name);
400 s_ip2 = (NMSettingIPConfig *) nm_connection_get_setting_by_name (candidate, s_name);
402 if (!s_ip1 || !s_ip2)
405 num = nm_setting_ip_config_get_num_routes (s_ip1);
406 if (num != nm_setting_ip_config_get_num_routes (s_ip2))
409 routes1 = g_new (NMIPRoute *, num);
410 routes2 = g_new (NMIPRoute *, num);
412 for (i = 0; i < num; i++) {
413 routes1[i] = nm_setting_ip_config_get_route (s_ip1, i);
414 routes2[i] = nm_setting_ip_config_get_route (s_ip2, i);
417 qsort (routes1, num, sizeof (NMIPRoute *), route_ptr_compare);
418 qsort (routes2, num, sizeof (NMIPRoute *), route_ptr_compare);
420 for (i = 0; i < num; i++) {
421 if (route_compare (routes1[i], routes2[i], default_metric))
425 remove_from_hash (settings, props, s_name, NM_SETTING_IP_CONFIG_ROUTES);
430 check_ip4_method (NMConnection *orig,
431 NMConnection *candidate,
432 GHashTable *settings,
433 gboolean device_has_carrier)
436 const char *orig_ip4_method, *candidate_ip4_method;
437 NMSettingIPConfig *candidate_ip4;
439 props = check_property_in_hash (settings,
440 NM_SETTING_IP4_CONFIG_SETTING_NAME,
441 NM_SETTING_IP_CONFIG_METHOD);
445 /* If the generated connection is 'disabled' (device had no IP addresses)
446 * but it has no carrier, that most likely means that IP addressing could
447 * not complete and thus no IP addresses were assigned. In that case, allow
448 * matching to the "auto" method.
450 orig_ip4_method = nm_utils_get_ip_config_method (orig, NM_TYPE_SETTING_IP4_CONFIG);
451 candidate_ip4_method = nm_utils_get_ip_config_method (candidate, NM_TYPE_SETTING_IP4_CONFIG);
452 candidate_ip4 = nm_connection_get_setting_ip4_config (candidate);
454 if ( strcmp (orig_ip4_method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0
455 && strcmp (candidate_ip4_method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0
456 && (!candidate_ip4 || nm_setting_ip_config_get_may_fail (candidate_ip4))
457 && (device_has_carrier == FALSE)) {
458 remove_from_hash (settings, props,
459 NM_SETTING_IP4_CONFIG_SETTING_NAME,
460 NM_SETTING_IP_CONFIG_METHOD);
467 check_connection_interface_name (NMConnection *orig,
468 NMConnection *candidate,
469 GHashTable *settings)
472 const char *orig_ifname, *cand_ifname;
473 NMSettingConnection *s_con_orig, *s_con_cand;
475 props = check_property_in_hash (settings,
476 NM_SETTING_CONNECTION_SETTING_NAME,
477 NM_SETTING_CONNECTION_INTERFACE_NAME);
481 /* If one of the interface names is NULL, we accept that connection */
482 s_con_orig = nm_connection_get_setting_connection (orig);
483 s_con_cand = nm_connection_get_setting_connection (candidate);
484 orig_ifname = nm_setting_connection_get_interface_name (s_con_orig);
485 cand_ifname = nm_setting_connection_get_interface_name (s_con_cand);
487 if (!orig_ifname || !cand_ifname) {
488 remove_from_hash (settings, props,
489 NM_SETTING_CONNECTION_SETTING_NAME,
490 NM_SETTING_CONNECTION_INTERFACE_NAME);
497 check_connection_mac_address (NMConnection *orig,
498 NMConnection *candidate,
499 GHashTable *settings)
502 const char *orig_mac = NULL, *cand_mac = NULL;
503 NMSettingWired *s_wired_orig, *s_wired_cand;
505 props = check_property_in_hash (settings,
506 NM_SETTING_WIRED_SETTING_NAME,
507 NM_SETTING_WIRED_MAC_ADDRESS);
511 /* If one of the MAC addresses is NULL, we accept that connection */
512 s_wired_orig = nm_connection_get_setting_wired (orig);
514 orig_mac = nm_setting_wired_get_mac_address (s_wired_orig);
516 s_wired_cand = nm_connection_get_setting_wired (candidate);
518 cand_mac = nm_setting_wired_get_mac_address (s_wired_cand);
520 if (!orig_mac || !cand_mac) {
521 remove_from_hash (settings, props,
522 NM_SETTING_WIRED_SETTING_NAME,
523 NM_SETTING_WIRED_MAC_ADDRESS);
530 check_connection_cloned_mac_address (NMConnection *orig,
531 NMConnection *candidate,
532 GHashTable *settings)
535 const char *orig_mac = NULL, *cand_mac = NULL;
536 NMSettingWired *s_wired_orig, *s_wired_cand;
538 props = check_property_in_hash (settings,
539 NM_SETTING_WIRED_SETTING_NAME,
540 NM_SETTING_WIRED_CLONED_MAC_ADDRESS);
544 /* If one of the MAC addresses is NULL, we accept that connection */
545 s_wired_orig = nm_connection_get_setting_wired (orig);
547 orig_mac = nm_setting_wired_get_cloned_mac_address (s_wired_orig);
549 s_wired_cand = nm_connection_get_setting_wired (candidate);
551 cand_mac = nm_setting_wired_get_cloned_mac_address (s_wired_cand);
553 if (!orig_mac || !cand_mac) {
554 remove_from_hash (settings, props,
555 NM_SETTING_WIRED_SETTING_NAME,
556 NM_SETTING_WIRED_CLONED_MAC_ADDRESS);
563 check_connection_s390_props (NMConnection *orig,
564 NMConnection *candidate,
565 GHashTable *settings)
567 GHashTable *props1, *props2, *props3;
568 NMSettingWired *s_wired_orig, *s_wired_cand;
570 props1 = check_property_in_hash (settings,
571 NM_SETTING_WIRED_SETTING_NAME,
572 NM_SETTING_WIRED_S390_SUBCHANNELS);
573 props2 = check_property_in_hash (settings,
574 NM_SETTING_WIRED_SETTING_NAME,
575 NM_SETTING_WIRED_S390_NETTYPE);
576 props3 = check_property_in_hash (settings,
577 NM_SETTING_WIRED_SETTING_NAME,
578 NM_SETTING_WIRED_S390_OPTIONS);
579 if (!props1 && !props2 && !props3)
582 /* If the generated connection did not contain wired setting,
583 * allow it to match to a connection with a wired setting,
584 * but default (empty) s390-* properties */
585 s_wired_orig = nm_connection_get_setting_wired (orig);
586 s_wired_cand = nm_connection_get_setting_wired (candidate);
587 if (!s_wired_orig && s_wired_cand) {
588 const char * const *subchans = nm_setting_wired_get_s390_subchannels (s_wired_cand);
589 const char *nettype = nm_setting_wired_get_s390_nettype (s_wired_cand);
590 guint32 num_options = nm_setting_wired_get_num_s390_options (s_wired_cand);
592 if ((!subchans || !*subchans) && !nettype && num_options == 0) {
593 remove_from_hash (settings, props1,
594 NM_SETTING_WIRED_SETTING_NAME,
595 NM_SETTING_WIRED_S390_SUBCHANNELS);
596 remove_from_hash (settings, props2,
597 NM_SETTING_WIRED_SETTING_NAME,
598 NM_SETTING_WIRED_S390_NETTYPE);
599 remove_from_hash (settings, props3,
600 NM_SETTING_WIRED_SETTING_NAME,
601 NM_SETTING_WIRED_S390_OPTIONS);
608 static NMConnection *
609 check_possible_match (NMConnection *orig,
610 NMConnection *candidate,
611 GHashTable *settings,
612 gboolean device_has_carrier,
613 gint64 default_v4_metric,
614 gint64 default_v6_metric)
616 g_return_val_if_fail (settings != NULL, NULL);
618 if (!check_ip6_method (orig, candidate, settings))
621 if (!check_ip4_method (orig, candidate, settings, device_has_carrier))
624 if (!check_ip_routes (orig, candidate, settings, default_v4_metric, TRUE))
627 if (!check_ip_routes (orig, candidate, settings, default_v6_metric, FALSE))
630 if (!check_connection_interface_name (orig, candidate, settings))
633 if (!check_connection_mac_address (orig, candidate, settings))
636 if (!check_connection_cloned_mac_address (orig, candidate, settings))
639 if (!check_connection_s390_props (orig, candidate, settings))
642 if (g_hash_table_size (settings) == 0)
649 * nm_utils_match_connection:
650 * @connections: a (optionally pre-sorted) list of connections from which to
651 * find a matching connection to @original based on "inferrable" properties
652 * @original: the #NMConnection to find a match for from @connections
653 * @device_has_carrier: pass %TRUE if the device that generated @original has
654 * a carrier, %FALSE if not
655 * @match_filter_func: a function to check whether each connection from @connections
656 * should be considered for matching. This function should return %TRUE if the
657 * connection should be considered, %FALSE if the connection should be ignored
658 * @match_compat_data: data pointer passed to @match_filter_func
660 * Checks each connection from @connections until a matching connection is found
661 * considering only setting properties marked with %NM_SETTING_PARAM_INFERRABLE
662 * and checking a few other characteristics like IPv6 method. If the caller
663 * desires some priority order of the connections, @connections should be
664 * sorted before calling this function.
666 * Returns: the best #NMConnection matching @original, or %NULL if no connection
667 * matches well enough.
670 nm_utils_match_connection (GSList *connections,
671 NMConnection *original,
672 gboolean device_has_carrier,
673 gint64 default_v4_metric,
674 gint64 default_v6_metric,
675 NMUtilsMatchFilterFunc match_filter_func,
676 gpointer match_filter_data)
678 NMConnection *best_match = NULL;
681 for (iter = connections; iter; iter = iter->next) {
682 NMConnection *candidate = NM_CONNECTION (iter->data);
683 GHashTable *diffs = NULL;
685 if (match_filter_func) {
686 if (!match_filter_func (candidate, match_filter_data))
690 if (!nm_connection_diff (original, candidate, NM_SETTING_COMPARE_FLAG_INFERRABLE, &diffs)) {
692 best_match = check_possible_match (original, candidate, diffs, device_has_carrier,
693 default_v4_metric, default_v6_metric);
696 if (!best_match && nm_logging_enabled (LOGL_DEBUG, LOGD_CORE)) {
697 GString *diff_string;
698 GHashTableIter s_iter, p_iter;
699 gpointer setting_name, setting;
700 gpointer property_name, value;
702 diff_string = g_string_new (NULL);
703 g_hash_table_iter_init (&s_iter, diffs);
704 while (g_hash_table_iter_next (&s_iter, &setting_name, &setting)) {
705 g_hash_table_iter_init (&p_iter, setting);
706 while (g_hash_table_iter_next (&p_iter, &property_name, &value)) {
707 if (diff_string->len)
708 g_string_append (diff_string, ", ");
709 g_string_append_printf (diff_string, "%s.%s",
710 (char *) setting_name,
711 (char *) property_name);
715 nm_log_dbg (LOGD_CORE, "Connection '%s' differs from candidate '%s' in %s",
716 nm_connection_get_id (original),
717 nm_connection_get_id (candidate),
719 g_string_free (diff_string, TRUE);
722 g_hash_table_unref (diffs);
730 /* Best match (if any) */
734 /******************************************************************************/
737 * nm_utils_g_value_set_object_path:
738 * @value: a #GValue, initialized to store an object path
739 * @object: (allow-none): an #NMExportedObject
741 * Sets @value to @object's object path. If @object is %NULL, or not
742 * exported, @value is set to "/".
745 nm_utils_g_value_set_object_path (GValue *value, gpointer object)
747 g_return_if_fail (!object || NM_IS_EXPORTED_OBJECT (object));
749 if (object && nm_exported_object_is_exported (object))
750 g_value_set_string (value, nm_exported_object_get_path (object));
752 g_value_set_string (value, "/");
756 * nm_utils_g_value_set_object_path_array:
757 * @value: a #GValue, initialized to store an object path
758 * @objects: a #GSList of #NMExportedObjects
759 * @filter_func: (allow-none): function to call on each object in @objects
760 * @user_data: data to pass to @filter_func
762 * Sets @value to an array of object paths of the objects in @objects.
765 nm_utils_g_value_set_object_path_array (GValue *value,
767 NMUtilsObjectFunc filter_func,
774 paths = g_new (char *, g_slist_length (objects) + 1);
775 for (i = 0, iter = objects; iter; iter = iter->next) {
776 NMExportedObject *object = iter->data;
779 path = nm_exported_object_get_path (object);
782 if (filter_func && !filter_func ((GObject *) object, user_data))
784 paths[i++] = g_strdup (path);
787 g_value_take_boxed (value, paths);
790 /******************************************************************************/