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., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
23 * SECTION:gupnp-service-info
24 * @short_description: Base abstract class for querying service information.
26 * The #GUPnPDeviceInfo base abstract class provides methods for querying
27 * service information.
30 #include <libsoup/soup.h>
33 #include "gupnp-service-info.h"
34 #include "gupnp-service-introspection-private.h"
35 #include "gupnp-context-private.h"
36 #include "gupnp-error.h"
37 #include "gupnp-error-private.h"
38 #include "gupnp-xml-doc.h"
39 #include "http-headers.h"
42 G_DEFINE_ABSTRACT_TYPE (GUPnPServiceInfo,
46 struct _GUPnPServiceInfoPrivate {
47 GUPnPContext *context;
59 /* For async downloads */
75 GUPnPServiceInfo *info;
77 GUPnPServiceIntrospectionCallback callback;
84 get_scpd_url_data_free (GetSCPDURLData *data)
86 g_slice_free (GetSCPDURLData, data);
90 gupnp_service_info_init (GUPnPServiceInfo *info)
92 info->priv = G_TYPE_INSTANCE_GET_PRIVATE (info,
93 GUPNP_TYPE_SERVICE_INFO,
94 GUPnPServiceInfoPrivate);
98 gupnp_service_info_set_property (GObject *object,
103 GUPnPServiceInfo *info;
105 info = GUPNP_SERVICE_INFO (object);
107 switch (property_id) {
109 info->priv->context = g_object_ref (g_value_get_object (value));
112 info->priv->location = g_value_dup_string (value);
115 info->priv->udn = g_value_dup_string (value);
117 case PROP_SERVICE_TYPE:
118 info->priv->service_type = g_value_dup_string (value);
121 info->priv->url_base = g_value_get_pointer (value);
122 if (info->priv->url_base)
123 info->priv->url_base =
124 soup_uri_copy (info->priv->url_base);
128 info->priv->doc = g_value_dup_object (value);
131 info->priv->element = g_value_get_pointer (value);
134 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
140 gupnp_service_info_get_property (GObject *object,
145 GUPnPServiceInfo *info;
147 info = GUPNP_SERVICE_INFO (object);
149 switch (property_id) {
151 g_value_set_object (value,
152 info->priv->context);
155 g_value_set_string (value,
156 info->priv->location);
159 g_value_set_string (value,
162 case PROP_SERVICE_TYPE:
163 g_value_set_string (value,
164 gupnp_service_info_get_service_type (info));
167 g_value_set_pointer (value,
168 info->priv->url_base);
171 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
177 gupnp_service_info_dispose (GObject *object)
179 GUPnPServiceInfo *info;
181 info = GUPNP_SERVICE_INFO (object);
183 /* Cancel any pending SCPD GETs */
184 if (info->priv->context) {
185 SoupSession *session;
187 session = gupnp_context_get_session (info->priv->context);
189 while (info->priv->pending_gets) {
190 GetSCPDURLData *data;
192 data = info->priv->pending_gets->data;
194 soup_session_cancel_message (session,
196 SOUP_STATUS_CANCELLED);
198 g_object_unref (data->message);
199 get_scpd_url_data_free (data);
201 info->priv->pending_gets =
202 g_list_delete_link (info->priv->pending_gets,
203 info->priv->pending_gets);
207 g_object_unref (info->priv->context);
208 info->priv->context = NULL;
211 if (info->priv->doc) {
212 g_object_unref (info->priv->doc);
213 info->priv->doc = NULL;
216 G_OBJECT_CLASS (gupnp_service_info_parent_class)->dispose (object);
220 gupnp_service_info_finalize (GObject *object)
222 GUPnPServiceInfo *info;
224 info = GUPNP_SERVICE_INFO (object);
226 g_free (info->priv->location);
227 g_free (info->priv->udn);
228 g_free (info->priv->service_type);
230 soup_uri_free (info->priv->url_base);
232 G_OBJECT_CLASS (gupnp_service_info_parent_class)->finalize (object);
236 gupnp_service_info_class_init (GUPnPServiceInfoClass *klass)
238 GObjectClass *object_class;
240 object_class = G_OBJECT_CLASS (klass);
242 object_class->set_property = gupnp_service_info_set_property;
243 object_class->get_property = gupnp_service_info_get_property;
244 object_class->dispose = gupnp_service_info_dispose;
245 object_class->finalize = gupnp_service_info_finalize;
247 g_type_class_add_private (klass, sizeof (GUPnPServiceInfoPrivate));
250 * GUPnPServiceInfo:context:
252 * The #GUPnPContext to use.
254 g_object_class_install_property
257 g_param_spec_object ("context",
262 G_PARAM_CONSTRUCT_ONLY |
263 G_PARAM_STATIC_NAME |
264 G_PARAM_STATIC_NICK |
265 G_PARAM_STATIC_BLURB));
268 * GUPnPServiceInfo:location:
270 * The location of the device description file.
272 g_object_class_install_property
275 g_param_spec_string ("location",
277 "The location of the device description "
281 G_PARAM_CONSTRUCT_ONLY |
282 G_PARAM_STATIC_NAME |
283 G_PARAM_STATIC_NICK |
284 G_PARAM_STATIC_BLURB));
287 * GUPnPServiceInfo:udn:
289 * The UDN of the containing device.
291 g_object_class_install_property
294 g_param_spec_string ("udn",
296 "The UDN of the containing device",
299 G_PARAM_CONSTRUCT_ONLY |
300 G_PARAM_STATIC_NAME |
301 G_PARAM_STATIC_NICK |
302 G_PARAM_STATIC_BLURB));
305 * GUPnPServiceInfo:service-type
309 g_object_class_install_property
312 g_param_spec_string ("service-type",
317 G_PARAM_CONSTRUCT_ONLY |
318 G_PARAM_STATIC_NAME |
319 G_PARAM_STATIC_NICK |
320 G_PARAM_STATIC_BLURB));
323 * GUPnPServiceInfo:url-base:
325 * The URL base (#SoupURI).
327 g_object_class_install_property
330 g_param_spec_pointer ("url-base",
334 G_PARAM_CONSTRUCT_ONLY |
335 G_PARAM_STATIC_NAME |
336 G_PARAM_STATIC_NICK |
337 G_PARAM_STATIC_BLURB));
340 * GUPnPServiceInfo:document:
346 g_object_class_install_property
349 g_param_spec_object ("document",
351 "The XML document related to this "
355 G_PARAM_CONSTRUCT_ONLY |
356 G_PARAM_STATIC_NAME |
357 G_PARAM_STATIC_NICK |
358 G_PARAM_STATIC_BLURB));
361 * GUPnPServiceInfo:element:
367 g_object_class_install_property
370 g_param_spec_pointer ("element",
372 "The XML element related to this "
375 G_PARAM_CONSTRUCT_ONLY |
376 G_PARAM_STATIC_NAME |
377 G_PARAM_STATIC_NICK |
378 G_PARAM_STATIC_BLURB));
382 * gupnp_service_info_get_context:
383 * @info: A #GUPnPServiceInfo
385 * Get the #GUPnPContext associated with @info.
387 * Returns: A #GUPnPContext.
390 gupnp_service_info_get_context (GUPnPServiceInfo *info)
392 g_return_val_if_fail (GUPNP_IS_SERVICE_INFO (info), NULL);
394 return info->priv->context;
398 * gupnp_service_info_get_location:
399 * @info: A #GUPnPServiceInfo
401 * Get the location of the device description file.
403 * Returns: A constant string.
406 gupnp_service_info_get_location (GUPnPServiceInfo *info)
408 g_return_val_if_fail (GUPNP_IS_SERVICE_INFO (info), NULL);
410 return info->priv->location;
414 * gupnp_service_info_get_url_base:
415 * @info: A #GUPnPServiceInfo
417 * Get the URL base of this service.
419 * Returns: A constant #SoupURI.
422 gupnp_service_info_get_url_base (GUPnPServiceInfo *info)
424 g_return_val_if_fail (GUPNP_IS_SERVICE_INFO (info), NULL);
426 return info->priv->url_base;
430 * gupnp_service_info_get_udn:
431 * @info: A #GUPnPServiceInfo
433 * Get the Unique Device Name of the containing device.
435 * Returns: A constant string.
438 gupnp_service_info_get_udn (GUPnPServiceInfo *info)
440 g_return_val_if_fail (GUPNP_IS_SERVICE_INFO (info), NULL);
442 return info->priv->udn;
446 * gupnp_service_info_get_service_type:
447 * @info: A #GUPnPServiceInfo
449 * Get the UPnP service type, or %NULL.
451 * Returns: A constant string.
454 gupnp_service_info_get_service_type (GUPnPServiceInfo *info)
456 g_return_val_if_fail (GUPNP_IS_SERVICE_INFO (info), NULL);
458 if (!info->priv->service_type) {
459 info->priv->service_type =
460 xml_util_get_child_element_content_glib
461 (info->priv->element, "serviceType");
464 return info->priv->service_type;
468 * gupnp_service_info_get_id:
469 * @info: A #GUPnPServiceInfo
471 * Get the ID of this service, or %NULL if there is no ID.
473 * Return value: A string. This string should be freed with g_free() after use.
476 gupnp_service_info_get_id (GUPnPServiceInfo *info)
478 g_return_val_if_fail (GUPNP_IS_SERVICE_INFO (info), NULL);
480 return xml_util_get_child_element_content_glib (info->priv->element,
485 * gupnp_service_info_get_scpd_url:
486 * @info: A #GUPnPServiceInfo
488 * Get the SCPD URL for this service, or %NULL if there is no SCPD.
490 * Return value: A string. This string should be freed with g_free() after use.
493 gupnp_service_info_get_scpd_url (GUPnPServiceInfo *info)
495 g_return_val_if_fail (GUPNP_IS_SERVICE_INFO (info), NULL);
497 return xml_util_get_child_element_content_url (info->priv->element,
499 info->priv->url_base);
503 * gupnp_service_info_get_control_url:
504 * @info: A #GUPnPServiceInfo
506 * Get the control URL for this service, or %NULL..
508 * Return value: A string. This string should be freed with g_free() after use.
511 gupnp_service_info_get_control_url (GUPnPServiceInfo *info)
513 g_return_val_if_fail (GUPNP_IS_SERVICE_INFO (info), NULL);
515 return xml_util_get_child_element_content_url (info->priv->element,
517 info->priv->url_base);
521 * gupnp_service_info_get_event_subscription_url:
522 * @info: A #GUPnPServiceInfo
524 * Get the event subscription URL for this service, or %NULL.
526 * Return value: A string. This string should be freed with g_free() after use.
529 gupnp_service_info_get_event_subscription_url (GUPnPServiceInfo *info)
531 g_return_val_if_fail (GUPNP_IS_SERVICE_INFO (info), NULL);
533 return xml_util_get_child_element_content_url (info->priv->element,
535 info->priv->url_base);
539 * gupnp_service_info_get_introspection:
540 * @info: A #GUPnPServiceInfo
541 * @error: return location for a #GError, or %NULL
543 * Note that introspection object is created from the information in service
544 * description document (SCPD) provided by the service so it can not be created
545 * if the service does not provide an SCPD.
547 * Warning: You should use gupnp_service_info_get_introspection_async()
548 * instead, this function re-enter the GMainloop before returning.
550 * Return value: (transfer full): A new #GUPnPServiceIntrospection for this
551 * service or %NULL. Unref after use.
553 GUPnPServiceIntrospection *
554 gupnp_service_info_get_introspection (GUPnPServiceInfo *info,
557 GUPnPServiceIntrospection *introspection;
558 SoupSession *session;
564 g_return_val_if_fail (GUPNP_IS_SERVICE_INFO (info), NULL);
566 introspection = NULL;
568 scpd_url = gupnp_service_info_get_scpd_url (info);
571 if (scpd_url != NULL) {
572 msg = soup_message_new (SOUP_METHOD_GET, scpd_url);
580 GUPNP_SERVER_ERROR_INVALID_URL,
581 "No valid SCPD URL defined");
586 /* Send off the message */
587 session = gupnp_context_get_session (info->priv->context);
589 status = soup_session_send_message (session, msg);
590 if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
591 _gupnp_error_set_server_error (error, msg);
593 g_object_unref (msg);
598 scpd = xmlRecoverMemory (msg->response_body->data,
599 msg->response_body->length);
601 g_object_unref (msg);
604 introspection = gupnp_service_introspection_new (scpd);
609 if (!introspection) {
612 GUPNP_SERVER_ERROR_INVALID_RESPONSE,
613 "Could not parse SCPD");
616 return introspection;
620 * SCPD URL downloaded.
623 got_scpd_url (SoupSession *session,
625 GetSCPDURLData *data)
627 GUPnPServiceIntrospection *introspection;
630 introspection = NULL;
633 if (msg->status_code == SOUP_STATUS_CANCELLED)
636 if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
639 scpd = xmlRecoverMemory (msg->response_body->data,
640 msg->response_body->length);
642 introspection = gupnp_service_introspection_new (scpd);
647 if (!introspection) {
650 GUPNP_SERVER_ERROR_INVALID_RESPONSE,
651 "Could not parse SCPD");
654 error = _gupnp_error_new_server_error (msg);
656 data->info->priv->pending_gets =
657 g_list_remove (data->info->priv->pending_gets, data);
659 data->callback (data->info,
665 g_error_free (error);
667 get_scpd_url_data_free (data);
671 * gupnp_service_info_get_introspection_async:
672 * @info: A #GUPnPServiceInfo
673 * @callback: (scope async) : callback to be called when introspection object is ready.
674 * @user_data: user_data to be passed to the callback.
676 * Note that introspection object is created from the information in service
677 * description document (SCPD) provided by the service so it can not be created
678 * if the service does not provide an SCPD.
681 gupnp_service_info_get_introspection_async
682 (GUPnPServiceInfo *info,
683 GUPnPServiceIntrospectionCallback callback,
686 GetSCPDURLData *data;
688 SoupSession *session;
690 g_return_if_fail (GUPNP_IS_SERVICE_INFO (info));
691 g_return_if_fail (callback != NULL);
693 data = g_slice_new (GetSCPDURLData);
695 scpd_url = gupnp_service_info_get_scpd_url (info);
697 data->message = NULL;
698 if (scpd_url != NULL) {
699 data->message = soup_message_new (SOUP_METHOD_GET, scpd_url);
704 if (data->message == NULL) {
709 GUPNP_SERVER_ERROR_INVALID_URL,
710 "No valid SCPD URL defined");
712 callback (info, NULL, error, user_data);
714 g_error_free (error);
716 g_slice_free (GetSCPDURLData, data);
722 data->callback = callback;
723 data->user_data = user_data;
725 /* Send off the message */
726 info->priv->pending_gets =
727 g_list_prepend (info->priv->pending_gets,
730 session = gupnp_context_get_session (info->priv->context);
732 soup_session_queue_message (session,
734 (SoupSessionCallback) got_scpd_url,