2 * Copyright (C) 2006, 2007, 2008 OpenedHand Ltd.
4 * Author: Jorn Baayen <jorn@openedhand.com>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
23 * SECTION:gssdp-resource-browser
24 * @short_description: Class handling resource discovery.
26 * #GSSDPResourceBrowser handles resource discovery. After creating a browser
27 * and activating it, the ::resource-available and ::resource-unavailable
28 * signals will be emitted whenever the availability of a resource matching the
29 * specified discovery target changes. A discovery request is sent out
30 * automatically when activating the browser.
34 #include <libsoup/soup.h>
39 #include "gssdp-resource-browser.h"
40 #include "gssdp-client-private.h"
41 #include "gssdp-protocol.h"
42 #include "gssdp-marshal.h"
44 #define RESCAN_TIMEOUT 5 /* 5 seconds */
45 #define MAX_DISCOVERY_MESSAGES 3
46 #define DISCOVERY_FREQUENCY 500 /* 500 ms */
48 G_DEFINE_TYPE (GSSDPResourceBrowser,
49 gssdp_resource_browser,
52 struct _GSSDPResourceBrowserPrivate {
62 gulong message_received_id;
64 GHashTable *resources;
70 GSource *refresh_cache_src;
71 GHashTable *fresh_resources;
88 static guint signals[LAST_SIGNAL];
91 GSSDPResourceBrowser *resource_browser;
96 /* Function prototypes */
98 gssdp_resource_browser_set_client (GSSDPResourceBrowser *resource_browser,
101 message_received_cb (GSSDPClient *client,
104 _GSSDPMessageType type,
105 SoupMessageHeaders *headers,
108 resource_free (gpointer data);
110 clear_cache (GSSDPResourceBrowser *resource_browser);
112 send_discovery_request (GSSDPResourceBrowser *resource_browser);
114 discovery_timeout (gpointer data);
116 start_discovery (GSSDPResourceBrowser *resource_browser);
118 stop_discovery (GSSDPResourceBrowser *resource_browser);
120 refresh_cache (gpointer data);
123 gssdp_resource_browser_init (GSSDPResourceBrowser *resource_browser)
125 resource_browser->priv = G_TYPE_INSTANCE_GET_PRIVATE
127 GSSDP_TYPE_RESOURCE_BROWSER,
128 GSSDPResourceBrowserPrivate);
130 resource_browser->priv->mx = SSDP_DEFAULT_MX;
132 resource_browser->priv->resources =
133 g_hash_table_new_full (g_str_hash,
140 gssdp_resource_browser_get_property (GObject *object,
145 GSSDPResourceBrowser *resource_browser;
147 resource_browser = GSSDP_RESOURCE_BROWSER (object);
149 switch (property_id) {
153 gssdp_resource_browser_get_client (resource_browser));
158 gssdp_resource_browser_get_target (resource_browser));
163 gssdp_resource_browser_get_mx (resource_browser));
168 gssdp_resource_browser_get_active (resource_browser));
171 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
177 gssdp_resource_browser_set_property (GObject *object,
182 GSSDPResourceBrowser *resource_browser;
184 resource_browser = GSSDP_RESOURCE_BROWSER (object);
186 switch (property_id) {
188 gssdp_resource_browser_set_client (resource_browser,
189 g_value_get_object (value));
192 gssdp_resource_browser_set_target (resource_browser,
193 g_value_get_string (value));
196 gssdp_resource_browser_set_mx (resource_browser,
197 g_value_get_uint (value));
200 gssdp_resource_browser_set_active (resource_browser,
201 g_value_get_boolean (value));
204 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
210 gssdp_resource_browser_dispose (GObject *object)
212 GSSDPResourceBrowser *resource_browser;
214 resource_browser = GSSDP_RESOURCE_BROWSER (object);
216 if (resource_browser->priv->client) {
217 if (g_signal_handler_is_connected
218 (resource_browser->priv->client,
219 resource_browser->priv->message_received_id)) {
220 g_signal_handler_disconnect
221 (resource_browser->priv->client,
222 resource_browser->priv->message_received_id);
225 stop_discovery (resource_browser);
227 g_object_unref (resource_browser->priv->client);
228 resource_browser->priv->client = NULL;
231 clear_cache (resource_browser);
233 G_OBJECT_CLASS (gssdp_resource_browser_parent_class)->dispose (object);
237 gssdp_resource_browser_finalize (GObject *object)
239 GSSDPResourceBrowser *resource_browser;
241 resource_browser = GSSDP_RESOURCE_BROWSER (object);
243 if (resource_browser->priv->target_regex)
244 g_regex_unref (resource_browser->priv->target_regex);
246 g_free (resource_browser->priv->target);
248 g_hash_table_destroy (resource_browser->priv->resources);
250 G_OBJECT_CLASS (gssdp_resource_browser_parent_class)->finalize (object);
254 gssdp_resource_browser_class_init (GSSDPResourceBrowserClass *klass)
256 GObjectClass *object_class;
258 object_class = G_OBJECT_CLASS (klass);
260 object_class->set_property = gssdp_resource_browser_set_property;
261 object_class->get_property = gssdp_resource_browser_get_property;
262 object_class->dispose = gssdp_resource_browser_dispose;
263 object_class->finalize = gssdp_resource_browser_finalize;
265 g_type_class_add_private (klass, sizeof (GSSDPResourceBrowserPrivate));
268 * GSSDPResourceBrowser:client:
270 * The #GSSDPClient to use.
272 g_object_class_install_property
278 "The associated client.",
280 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
281 G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
282 G_PARAM_STATIC_BLURB));
285 * GSSDPResourceBrowser:target:
287 * The discovery target.
289 g_object_class_install_property
295 "The discovery target.",
298 G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
299 G_PARAM_STATIC_BLURB));
302 * GSSDPResourceBrowser:mx:
304 * The maximum number of seconds in which to request other parties
307 g_object_class_install_property
313 "The maximum number of seconds in which to request "
314 "other parties to respond.",
319 G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
320 G_PARAM_STATIC_BLURB));
323 * GSSDPResourceBrowser:active:
325 * Whether this browser is active or not.
327 g_object_class_install_property
333 "TRUE if the resource browser is active.",
336 G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
337 G_PARAM_STATIC_BLURB));
340 * GSSDPResourceBrowser::resource-available:
341 * @resource_browser: The #GSSDPResourceBrowser that received the
343 * @usn: The USN of the discovered resource
344 * @locations: (type GList*) (transfer none) (element-type utf8): A #GList of strings describing the locations of the
345 * discovered resource.
347 * The ::resource-available signal is emitted whenever a new resource
348 * has become available.
350 signals[RESOURCE_AVAILABLE] =
351 g_signal_new ("resource-available",
352 GSSDP_TYPE_RESOURCE_BROWSER,
354 G_STRUCT_OFFSET (GSSDPResourceBrowserClass,
357 gssdp_marshal_VOID__STRING_POINTER,
364 * GSSDPResourceBrowser::resource-unavailable:
365 * @resource_browser: The #GSSDPResourceBrowser that received the
367 * @usn: The USN of the resource
369 * The ::resource-unavailable signal is emitted whenever a resource
370 * is not available any more.
372 signals[RESOURCE_UNAVAILABLE] =
373 g_signal_new ("resource-unavailable",
374 GSSDP_TYPE_RESOURCE_BROWSER,
376 G_STRUCT_OFFSET (GSSDPResourceBrowserClass,
377 resource_unavailable),
379 gssdp_marshal_VOID__STRING,
386 * gssdp_resource_browser_new:
387 * @client: The #GSSDPClient to associate with
389 * Return value: A new #GSSDPResourceBrowser object.
391 GSSDPResourceBrowser *
392 gssdp_resource_browser_new (GSSDPClient *client,
395 return g_object_new (GSSDP_TYPE_RESOURCE_BROWSER,
402 * Sets the #GSSDPClient @resource_browser is associated with to @client
405 gssdp_resource_browser_set_client (GSSDPResourceBrowser *resource_browser,
408 g_return_if_fail (GSSDP_IS_RESOURCE_BROWSER (resource_browser));
409 g_return_if_fail (GSSDP_IS_CLIENT (client));
411 resource_browser->priv->client = g_object_ref (client);
413 resource_browser->priv->message_received_id =
414 g_signal_connect_object (resource_browser->priv->client,
416 G_CALLBACK (message_received_cb),
420 g_object_notify (G_OBJECT (resource_browser), "client");
424 * gssdp_resource_browser_get_client:
425 * @resource_browser: A #GSSDPResourceBrowser
427 * Returns: (transfer none): The #GSSDPClient @resource_browser is associated with.
430 gssdp_resource_browser_get_client (GSSDPResourceBrowser *resource_browser)
432 g_return_val_if_fail (GSSDP_IS_RESOURCE_BROWSER (resource_browser),
435 return resource_browser->priv->client;
439 * gssdp_resource_browser_set_target:
440 * @resource_browser: A #GSSDPResourceBrowser
441 * @target: The browser target
443 * Sets the browser target of @resource_browser to @target.
446 gssdp_resource_browser_set_target (GSSDPResourceBrowser *resource_browser,
451 char *version_pattern;
454 g_return_if_fail (GSSDP_IS_RESOURCE_BROWSER (resource_browser));
455 g_return_if_fail (target != NULL);
456 g_return_if_fail (!resource_browser->priv->active);
458 g_free (resource_browser->priv->target);
459 resource_browser->priv->target = g_strdup (target);
461 if (resource_browser->priv->target_regex)
462 g_regex_unref (resource_browser->priv->target_regex);
464 version_pattern = "([0-9]+)";
465 /* Make sure we have enough room for version pattern */
466 pattern = g_strndup (target,
467 strlen (target) + strlen (version_pattern));
469 version = g_strrstr (pattern, ":");
470 if (version != NULL &&
471 (g_strstr_len (pattern, -1, "uuid:") != pattern ||
472 version != g_strstr_len (pattern, -1, ":")) &&
473 g_regex_match_simple (version_pattern,
476 G_REGEX_MATCH_ANCHORED)) {
477 resource_browser->priv->version = atoi (version + 1);
478 strcpy (version + 1, version_pattern);
482 resource_browser->priv->target_regex = g_regex_new (pattern,
487 g_warning ("Error compiling regular expression '%s': %s",
491 g_error_free (error);
495 g_object_notify (G_OBJECT (resource_browser), "target");
499 * gssdp_resource_browser_get_target:
500 * @resource_browser: A #GSSDPResourceBrowser
502 * Return value: The browser target.
505 gssdp_resource_browser_get_target (GSSDPResourceBrowser *resource_browser)
507 g_return_val_if_fail (GSSDP_IS_RESOURCE_BROWSER (resource_browser),
510 return resource_browser->priv->target;
514 * gssdp_resource_browser_set_mx:
515 * @resource_browser: A #GSSDPResourceBrowser
516 * @mx: The to be used MX value
518 * Sets the used MX value of @resource_browser to @mx.
521 gssdp_resource_browser_set_mx (GSSDPResourceBrowser *resource_browser,
524 g_return_if_fail (GSSDP_IS_RESOURCE_BROWSER (resource_browser));
526 if (resource_browser->priv->mx == mx)
529 resource_browser->priv->mx = mx;
531 g_object_notify (G_OBJECT (resource_browser), "mx");
535 * gssdp_resource_browser_get_mx:
536 * @resource_browser: A #GSSDPResourceBrowser
538 * Return value: The used MX value.
541 gssdp_resource_browser_get_mx (GSSDPResourceBrowser *resource_browser)
543 g_return_val_if_fail (GSSDP_IS_RESOURCE_BROWSER (resource_browser), 0);
545 return resource_browser->priv->mx;
549 * gssdp_resource_browser_set_active:
550 * @resource_browser: A #GSSDPResourceBrowser
551 * @active: TRUE to activate @resource_browser
553 * (De)activates @resource_browser.
556 gssdp_resource_browser_set_active (GSSDPResourceBrowser *resource_browser,
559 g_return_if_fail (GSSDP_IS_RESOURCE_BROWSER (resource_browser));
561 if (resource_browser->priv->active == active)
564 resource_browser->priv->active = active;
567 start_discovery (resource_browser);
569 stop_discovery (resource_browser);
571 clear_cache (resource_browser);
574 g_object_notify (G_OBJECT (resource_browser), "active");
578 * gssdp_resource_browser_get_active:
579 * @resource_browser: A #GSSDPResourceBrowser
581 * Return value: TRUE if @resource_browser is active.
584 gssdp_resource_browser_get_active (GSSDPResourceBrowser *resource_browser)
586 g_return_val_if_fail (GSSDP_IS_RESOURCE_BROWSER (resource_browser), 0);
588 return resource_browser->priv->active;
592 * gssdp_resource_browser_rescan:
593 * @resource_browser: A #GSSDPResourceBrowser
595 * Begins discovery if @resource_browser is active and no discovery is
596 * performed. Otherwise does nothing.
598 * Return value: %TRUE if rescaning has been started.
601 gssdp_resource_browser_rescan (GSSDPResourceBrowser *resource_browser)
603 g_return_val_if_fail (GSSDP_IS_RESOURCE_BROWSER (resource_browser), 0);
605 if (resource_browser->priv->active &&
606 resource_browser->priv->timeout_src == NULL &&
607 resource_browser->priv->refresh_cache_src == NULL) {
608 start_discovery (resource_browser);
616 * Resource expired: Remove
619 resource_expire (gpointer user_data)
621 GSSDPResourceBrowser *resource_browser;
626 resource = user_data;
627 resource_browser = resource->resource_browser;
629 /* Steal the USN pointer from the resource as we need it for the signal
633 resource->usn = NULL;
635 if (resource_browser->priv->version > 0) {
638 version = g_strrstr (usn, ":");
639 canonical_usn = g_strndup (usn, version - usn);
641 canonical_usn = g_strdup (usn);
644 g_hash_table_remove (resource->resource_browser->priv->resources,
647 g_signal_emit (resource_browser,
648 signals[RESOURCE_UNAVAILABLE],
652 g_free (canonical_usn);
658 resource_available (GSSDPResourceBrowser *resource_browser,
659 SoupMessageHeaders *headers)
669 usn = soup_message_headers_get_one (headers, "USN");
671 return; /* No USN specified */
673 if (resource_browser->priv->version > 0) {
676 version = g_strrstr (usn, ":");
677 canonical_usn = g_strndup (usn, version - usn);
679 canonical_usn = g_strdup (usn);
682 /* Get from cache, if possible */
683 resource = g_hash_table_lookup (resource_browser->priv->resources,
685 /* Put usn into fresh resources, so this resource will not be
686 * removed on cache refreshing. */
687 if (resource_browser->priv->fresh_resources != NULL) {
688 g_hash_table_add (resource_browser->priv->fresh_resources,
689 g_strdup (canonical_usn));
693 /* Remove old timeout */
694 g_source_destroy (resource->timeout_src);
698 /* Create new Resource data structure */
699 resource = g_slice_new (Resource);
701 resource->resource_browser = resource_browser;
702 resource->usn = g_strdup (usn);
704 g_hash_table_insert (resource_browser->priv->resources,
710 /* hash-table takes ownership of this */
711 canonical_usn = NULL;
714 if (canonical_usn != NULL)
715 g_free (canonical_usn);
717 /* Calculate new timeout */
718 header = soup_message_headers_get_one (headers, "Cache-Control");
725 for (list = soup_header_parse_list (header);
728 res = sscanf (list->data,
736 g_warning ("Invalid 'Cache-Control' header. Assuming "
737 "default max-age of %d.\n"
739 SSDP_DEFAULT_MAX_AGE,
742 timeout = SSDP_DEFAULT_MAX_AGE;
745 soup_header_free_list (list);
749 expires = soup_message_headers_get_one (headers, "Expires");
751 SoupDate *soup_exp_time;
752 time_t exp_time, cur_time;
754 soup_exp_time = soup_date_new_from_string (expires);
755 exp_time = soup_date_to_time_t (soup_exp_time);
756 soup_date_free (soup_exp_time);
758 cur_time = time (NULL);
760 if (exp_time > cur_time)
761 timeout = exp_time - cur_time;
763 g_warning ("Invalid 'Expires' header. Assuming "
764 "default max-age of %d.\n"
766 SSDP_DEFAULT_MAX_AGE,
769 timeout = SSDP_DEFAULT_MAX_AGE;
772 g_warning ("No 'Cache-Control' nor any 'Expires' "
773 "header was specified. Assuming default "
774 "max-age of %d.", SSDP_DEFAULT_MAX_AGE);
776 timeout = SSDP_DEFAULT_MAX_AGE;
780 resource->timeout_src = g_timeout_source_new_seconds (timeout);
781 g_source_set_callback (resource->timeout_src,
785 g_source_attach (resource->timeout_src,
786 g_main_context_get_thread_default ());
788 g_source_unref (resource->timeout_src);
790 /* Only continue with signal emission if this resource was not
795 /* Build list of locations */
798 header = soup_message_headers_get_one (headers, "Location");
800 locations = g_list_append (locations, g_strdup (header));
802 header = soup_message_headers_get_one (headers, "AL");
804 /* Parse AL header. The format is:
806 const char *start, *end;
810 while ((start = strchr (start, '<'))) {
812 if (!start || !*start)
815 end = strchr (start, '>');
819 uri = g_strndup (start, end - start);
820 locations = g_list_append (locations, uri);
827 g_signal_emit (resource_browser,
828 signals[RESOURCE_AVAILABLE],
835 g_free (locations->data);
837 locations = g_list_delete_link (locations, locations);
842 resource_unavailable (GSSDPResourceBrowser *resource_browser,
843 SoupMessageHeaders *headers)
848 usn = soup_message_headers_get_one (headers, "USN");
850 return; /* No USN specified */
852 if (resource_browser->priv->version > 0) {
855 version = g_strrstr (usn, ":");
856 canonical_usn = g_strndup (usn, version - usn);
858 canonical_usn = g_strdup (usn);
861 /* Only process if we were cached */
862 if (!g_hash_table_lookup (resource_browser->priv->resources,
866 g_hash_table_remove (resource_browser->priv->resources,
869 g_signal_emit (resource_browser,
870 signals[RESOURCE_UNAVAILABLE],
875 g_free (canonical_usn);
879 check_target_compat (GSSDPResourceBrowser *resource_browser,
886 if (strcmp (resource_browser->priv->target,
887 GSSDP_ALL_RESOURCES) == 0)
890 if (!g_regex_match (resource_browser->priv->target_regex,
894 g_match_info_free (info);
899 /* If there was no version to match, we're done */
900 if (resource_browser->priv->version == 0) {
901 g_match_info_free (info);
906 if (g_match_info_get_match_count (info) != 2) {
907 g_match_info_free (info);
912 version = atoi ((tmp = g_match_info_fetch (info, 1)));
914 g_match_info_free (info);
920 return (uint) version >= resource_browser->priv->version;
924 received_discovery_response (GSSDPResourceBrowser *resource_browser,
925 SoupMessageHeaders *headers)
929 st = soup_message_headers_get_one (headers, "ST");
931 return; /* No target specified */
933 if (!check_target_compat (resource_browser, st))
934 return; /* Target doesn't match */
936 resource_available (resource_browser, headers);
940 received_announcement (GSSDPResourceBrowser *resource_browser,
941 SoupMessageHeaders *headers)
945 header = soup_message_headers_get_one (headers, "NT");
947 return; /* No target specified */
949 if (!check_target_compat (resource_browser, header))
950 return; /* Target doesn't match */
952 header = soup_message_headers_get_one (headers, "NTS");
954 return; /* No announcement type specified */
956 /* Check announcement type */
959 strlen (SSDP_ALIVE_NTS)) == 0)
960 resource_available (resource_browser, headers);
961 else if (strncmp (header,
963 strlen (SSDP_BYEBYE_NTS)) == 0)
964 resource_unavailable (resource_browser, headers);
971 message_received_cb (G_GNUC_UNUSED GSSDPClient *client,
972 G_GNUC_UNUSED const char *from_ip,
973 G_GNUC_UNUSED gushort from_port,
974 _GSSDPMessageType type,
975 SoupMessageHeaders *headers,
978 GSSDPResourceBrowser *resource_browser;
980 resource_browser = GSSDP_RESOURCE_BROWSER (user_data);
982 if (!resource_browser->priv->active)
986 case _GSSDP_DISCOVERY_RESPONSE:
987 received_discovery_response (resource_browser, headers);
989 case _GSSDP_ANNOUNCEMENT:
990 received_announcement (resource_browser, headers);
998 * Free a Resource structure and its contained data
1001 resource_free (gpointer data)
1007 g_free (resource->usn);
1009 g_source_destroy (resource->timeout_src);
1011 g_slice_free (Resource, resource);
1015 clear_cache_helper (G_GNUC_UNUSED gpointer key,
1017 G_GNUC_UNUSED gpointer data)
1023 g_signal_emit (resource->resource_browser,
1024 signals[RESOURCE_UNAVAILABLE],
1032 * Clears the cached resources hash
1035 clear_cache (GSSDPResourceBrowser *resource_browser)
1038 g_hash_table_foreach_remove (resource_browser->priv->resources,
1043 /* Sends discovery request */
1045 send_discovery_request (GSSDPResourceBrowser *resource_browser)
1049 message = g_strdup_printf (SSDP_DISCOVERY_REQUEST,
1050 resource_browser->priv->target,
1051 resource_browser->priv->mx,
1052 g_get_application_name () ?: "");
1054 _gssdp_client_send_message (resource_browser->priv->client,
1058 _GSSDP_DISCOVERY_REQUEST);
1064 discovery_timeout (gpointer data)
1066 GSSDPResourceBrowser *resource_browser;
1068 resource_browser = GSSDP_RESOURCE_BROWSER (data);
1070 send_discovery_request (resource_browser);
1072 resource_browser->priv->num_discovery += 1;
1074 if (resource_browser->priv->num_discovery >= MAX_DISCOVERY_MESSAGES) {
1075 resource_browser->priv->timeout_src = NULL;
1076 resource_browser->priv->num_discovery = 0;
1078 /* Setup cache refreshing */
1079 resource_browser->priv->refresh_cache_src =
1080 g_timeout_source_new_seconds (RESCAN_TIMEOUT);
1081 g_source_set_callback
1082 (resource_browser->priv->refresh_cache_src,
1086 g_source_attach (resource_browser->priv->refresh_cache_src,
1087 g_main_context_get_thread_default ());
1088 g_source_unref (resource_browser->priv->refresh_cache_src);
1095 /* Starts sending discovery requests */
1097 start_discovery (GSSDPResourceBrowser *resource_browser)
1100 send_discovery_request (resource_browser);
1102 /* And schedule the rest for later */
1103 resource_browser->priv->num_discovery = 1;
1104 resource_browser->priv->timeout_src =
1105 g_timeout_source_new (DISCOVERY_FREQUENCY);
1106 g_source_set_callback (resource_browser->priv->timeout_src,
1108 resource_browser, NULL);
1110 g_source_attach (resource_browser->priv->timeout_src,
1111 g_main_context_get_thread_default ());
1113 g_source_unref (resource_browser->priv->timeout_src);
1115 /* Setup a set of responsive resources for cache refreshing */
1116 resource_browser->priv->fresh_resources = g_hash_table_new_full
1123 /* Stops the sending of discovery messages */
1125 stop_discovery (GSSDPResourceBrowser *resource_browser)
1127 if (resource_browser->priv->timeout_src) {
1128 g_source_destroy (resource_browser->priv->timeout_src);
1129 resource_browser->priv->timeout_src = NULL;
1130 resource_browser->priv->num_discovery = 0;
1132 if (resource_browser->priv->refresh_cache_src) {
1133 g_source_destroy (resource_browser->priv->refresh_cache_src);
1134 resource_browser->priv->refresh_cache_src = NULL;
1136 if (resource_browser->priv->fresh_resources) {
1137 g_hash_table_unref (resource_browser->priv->fresh_resources);
1138 resource_browser->priv->fresh_resources = NULL;
1143 refresh_cache_helper (gpointer key, gpointer value, gpointer data)
1146 GHashTable *fresh_resources;
1149 fresh_resources = data;
1151 if (g_hash_table_contains (fresh_resources, key))
1154 g_signal_emit (resource->resource_browser,
1155 signals[RESOURCE_UNAVAILABLE],
1163 /* Removes non-responsive resources */
1165 refresh_cache (gpointer data)
1167 GSSDPResourceBrowser *resource_browser;
1169 resource_browser = GSSDP_RESOURCE_BROWSER (data);
1170 g_hash_table_foreach_remove (resource_browser->priv->resources,
1171 refresh_cache_helper,
1172 resource_browser->priv->fresh_resources);
1173 g_hash_table_unref (resource_browser->priv->fresh_resources);
1174 resource_browser->priv->fresh_resources = NULL;
1175 resource_browser->priv->refresh_cache_src = NULL;