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: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_dup_boxed (value);
124 info->priv->doc = g_value_dup_object (value);
127 info->priv->element = g_value_get_pointer (value);
130 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
136 gupnp_service_info_get_property (GObject *object,
141 GUPnPServiceInfo *info;
143 info = GUPNP_SERVICE_INFO (object);
145 switch (property_id) {
147 g_value_set_object (value,
148 info->priv->context);
151 g_value_set_string (value,
152 info->priv->location);
155 g_value_set_string (value,
158 case PROP_SERVICE_TYPE:
159 g_value_set_string (value,
160 gupnp_service_info_get_service_type (info));
163 g_value_set_boxed (value,
164 info->priv->url_base);
167 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
173 gupnp_service_info_dispose (GObject *object)
175 GUPnPServiceInfo *info;
177 info = GUPNP_SERVICE_INFO (object);
179 /* Cancel any pending SCPD GETs */
180 if (info->priv->context) {
181 SoupSession *session;
183 session = gupnp_context_get_session (info->priv->context);
185 while (info->priv->pending_gets) {
186 GetSCPDURLData *data;
188 data = info->priv->pending_gets->data;
190 soup_session_cancel_message (session,
192 SOUP_STATUS_CANCELLED);
194 get_scpd_url_data_free (data);
196 info->priv->pending_gets =
197 g_list_delete_link (info->priv->pending_gets,
198 info->priv->pending_gets);
202 g_object_unref (info->priv->context);
203 info->priv->context = NULL;
206 if (info->priv->doc) {
207 g_object_unref (info->priv->doc);
208 info->priv->doc = NULL;
211 G_OBJECT_CLASS (gupnp_service_info_parent_class)->dispose (object);
215 gupnp_service_info_finalize (GObject *object)
217 GUPnPServiceInfo *info;
219 info = GUPNP_SERVICE_INFO (object);
221 g_free (info->priv->location);
222 g_free (info->priv->udn);
223 g_free (info->priv->service_type);
225 soup_uri_free (info->priv->url_base);
227 G_OBJECT_CLASS (gupnp_service_info_parent_class)->finalize (object);
231 gupnp_service_info_class_init (GUPnPServiceInfoClass *klass)
233 GObjectClass *object_class;
235 object_class = G_OBJECT_CLASS (klass);
237 object_class->set_property = gupnp_service_info_set_property;
238 object_class->get_property = gupnp_service_info_get_property;
239 object_class->dispose = gupnp_service_info_dispose;
240 object_class->finalize = gupnp_service_info_finalize;
242 g_type_class_add_private (klass, sizeof (GUPnPServiceInfoPrivate));
245 * GUPnPServiceInfo:context:
247 * The #GUPnPContext to use.
249 g_object_class_install_property
252 g_param_spec_object ("context",
257 G_PARAM_CONSTRUCT_ONLY |
258 G_PARAM_STATIC_NAME |
259 G_PARAM_STATIC_NICK |
260 G_PARAM_STATIC_BLURB));
263 * GUPnPServiceInfo:location:
265 * The location of the device description file.
267 g_object_class_install_property
270 g_param_spec_string ("location",
272 "The location of the device description "
276 G_PARAM_CONSTRUCT_ONLY |
277 G_PARAM_STATIC_NAME |
278 G_PARAM_STATIC_NICK |
279 G_PARAM_STATIC_BLURB));
282 * GUPnPServiceInfo:udn:
284 * The UDN of the containing device.
286 g_object_class_install_property
289 g_param_spec_string ("udn",
291 "The UDN of the containing device",
294 G_PARAM_CONSTRUCT_ONLY |
295 G_PARAM_STATIC_NAME |
296 G_PARAM_STATIC_NICK |
297 G_PARAM_STATIC_BLURB));
300 * GUPnPServiceInfo:service-type:
304 g_object_class_install_property
307 g_param_spec_string ("service-type",
312 G_PARAM_CONSTRUCT_ONLY |
313 G_PARAM_STATIC_NAME |
314 G_PARAM_STATIC_NICK |
315 G_PARAM_STATIC_BLURB));
318 * GUPnPServiceInfo:url-base:
320 * The URL base (#SoupURI).
322 g_object_class_install_property
325 g_param_spec_boxed ("url-base",
330 G_PARAM_CONSTRUCT_ONLY |
331 G_PARAM_STATIC_NAME |
332 G_PARAM_STATIC_NICK |
333 G_PARAM_STATIC_BLURB));
336 * GUPnPServiceInfo:document:
342 g_object_class_install_property
345 g_param_spec_object ("document",
347 "The XML document related to this "
351 G_PARAM_CONSTRUCT_ONLY |
352 G_PARAM_STATIC_NAME |
353 G_PARAM_STATIC_NICK |
354 G_PARAM_STATIC_BLURB));
357 * GUPnPServiceInfo:element:
363 g_object_class_install_property
366 g_param_spec_pointer ("element",
368 "The XML element related to this "
371 G_PARAM_CONSTRUCT_ONLY |
372 G_PARAM_STATIC_NAME |
373 G_PARAM_STATIC_NICK |
374 G_PARAM_STATIC_BLURB));
378 * gupnp_service_info_get_context:
379 * @info: A #GUPnPServiceInfo
381 * Get the #GUPnPContext associated with @info.
383 * Returns: (transfer none): A #GUPnPContext.
386 gupnp_service_info_get_context (GUPnPServiceInfo *info)
388 g_return_val_if_fail (GUPNP_IS_SERVICE_INFO (info), NULL);
390 return info->priv->context;
394 * gupnp_service_info_get_location:
395 * @info: A #GUPnPServiceInfo
397 * Get the location of the device description file.
399 * Returns: A constant string.
402 gupnp_service_info_get_location (GUPnPServiceInfo *info)
404 g_return_val_if_fail (GUPNP_IS_SERVICE_INFO (info), NULL);
406 return info->priv->location;
410 * gupnp_service_info_get_url_base:
411 * @info: A #GUPnPServiceInfo
413 * Get the URL base of this service.
415 * Returns: A constant #SoupURI.
418 gupnp_service_info_get_url_base (GUPnPServiceInfo *info)
420 g_return_val_if_fail (GUPNP_IS_SERVICE_INFO (info), NULL);
422 return info->priv->url_base;
426 * gupnp_service_info_get_udn:
427 * @info: A #GUPnPServiceInfo
429 * Get the Unique Device Name of the containing device.
431 * Returns: A constant string.
434 gupnp_service_info_get_udn (GUPnPServiceInfo *info)
436 g_return_val_if_fail (GUPNP_IS_SERVICE_INFO (info), NULL);
438 return info->priv->udn;
442 * gupnp_service_info_get_service_type:
443 * @info: A #GUPnPServiceInfo
445 * Get the UPnP service type, or %NULL.
447 * Returns: A constant string.
450 gupnp_service_info_get_service_type (GUPnPServiceInfo *info)
452 g_return_val_if_fail (GUPNP_IS_SERVICE_INFO (info), NULL);
454 if (!info->priv->service_type) {
455 info->priv->service_type =
456 xml_util_get_child_element_content_glib
457 (info->priv->element, "serviceType");
460 return info->priv->service_type;
464 * gupnp_service_info_get_id:
465 * @info: A #GUPnPServiceInfo
467 * Get the ID of this service, or %NULL if there is no ID.
469 * Return value: A string. This string should be freed with g_free() after use.
472 gupnp_service_info_get_id (GUPnPServiceInfo *info)
474 g_return_val_if_fail (GUPNP_IS_SERVICE_INFO (info), NULL);
476 return xml_util_get_child_element_content_glib (info->priv->element,
481 * gupnp_service_info_get_scpd_url:
482 * @info: A #GUPnPServiceInfo
484 * Get the SCPD URL for this service, or %NULL if there is no SCPD.
486 * Return value: A string. This string should be freed with g_free() after use.
489 gupnp_service_info_get_scpd_url (GUPnPServiceInfo *info)
491 g_return_val_if_fail (GUPNP_IS_SERVICE_INFO (info), NULL);
493 return xml_util_get_child_element_content_url (info->priv->element,
495 info->priv->url_base);
499 * gupnp_service_info_get_control_url:
500 * @info: A #GUPnPServiceInfo
502 * Get the control URL for this service, or %NULL..
504 * Return value: A string. This string should be freed with g_free() after use.
507 gupnp_service_info_get_control_url (GUPnPServiceInfo *info)
509 g_return_val_if_fail (GUPNP_IS_SERVICE_INFO (info), NULL);
511 return xml_util_get_child_element_content_url (info->priv->element,
513 info->priv->url_base);
517 * gupnp_service_info_get_event_subscription_url:
518 * @info: A #GUPnPServiceInfo
520 * Get the event subscription URL for this service, or %NULL.
522 * Return value: A string. This string should be freed with g_free() after use.
525 gupnp_service_info_get_event_subscription_url (GUPnPServiceInfo *info)
527 g_return_val_if_fail (GUPNP_IS_SERVICE_INFO (info), NULL);
529 return xml_util_get_child_element_content_url (info->priv->element,
531 info->priv->url_base);
535 * gupnp_service_info_get_introspection:
536 * @info: A #GUPnPServiceInfo
537 * @error: return location for a #GError, or %NULL
539 * Note that introspection object is created from the information in service
540 * description document (SCPD) provided by the service so it can not be created
541 * if the service does not provide an SCPD.
543 * Warning: You should use gupnp_service_info_get_introspection_async()
544 * instead, this function re-enter the GMainloop before returning.
546 * Return value: (transfer full): A new #GUPnPServiceIntrospection for this
547 * service or %NULL. Unref after use.
549 GUPnPServiceIntrospection *
550 gupnp_service_info_get_introspection (GUPnPServiceInfo *info,
553 GUPnPServiceIntrospection *introspection;
554 SoupSession *session;
560 g_return_val_if_fail (GUPNP_IS_SERVICE_INFO (info), NULL);
562 introspection = NULL;
564 scpd_url = gupnp_service_info_get_scpd_url (info);
567 if (scpd_url != NULL) {
568 msg = soup_message_new (SOUP_METHOD_GET, scpd_url);
576 GUPNP_SERVER_ERROR_INVALID_URL,
577 "No valid SCPD URL defined");
582 /* Send off the message */
583 session = gupnp_context_get_session (info->priv->context);
585 status = soup_session_send_message (session, msg);
586 if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
587 _gupnp_error_set_server_error (error, msg);
589 g_object_unref (msg);
594 scpd = xmlRecoverMemory (msg->response_body->data,
595 msg->response_body->length);
597 g_object_unref (msg);
600 introspection = gupnp_service_introspection_new (scpd);
605 if (!introspection) {
608 GUPNP_SERVER_ERROR_INVALID_RESPONSE,
609 "Could not parse SCPD");
612 return introspection;
616 * SCPD URL downloaded.
619 got_scpd_url (G_GNUC_UNUSED SoupSession *session,
621 GetSCPDURLData *data)
623 GUPnPServiceIntrospection *introspection;
626 introspection = NULL;
629 if (msg->status_code == SOUP_STATUS_CANCELLED)
632 if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
635 scpd = xmlRecoverMemory (msg->response_body->data,
636 msg->response_body->length);
638 introspection = gupnp_service_introspection_new (scpd);
643 if (!introspection) {
646 GUPNP_SERVER_ERROR_INVALID_RESPONSE,
647 "Could not parse SCPD");
650 error = _gupnp_error_new_server_error (msg);
652 data->info->priv->pending_gets =
653 g_list_remove (data->info->priv->pending_gets, data);
655 data->callback (data->info,
661 g_error_free (error);
663 get_scpd_url_data_free (data);
667 * gupnp_service_info_get_introspection_async:
668 * @info: A #GUPnPServiceInfo
669 * @callback: (scope async) : callback to be called when introspection object is ready.
670 * @user_data: user_data to be passed to the callback.
672 * Note that introspection object is created from the information in service
673 * description document (SCPD) provided by the service so it can not be created
674 * if the service does not provide an SCPD.
677 gupnp_service_info_get_introspection_async
678 (GUPnPServiceInfo *info,
679 GUPnPServiceIntrospectionCallback callback,
682 GetSCPDURLData *data;
684 SoupSession *session;
686 g_return_if_fail (GUPNP_IS_SERVICE_INFO (info));
687 g_return_if_fail (callback != NULL);
689 data = g_slice_new (GetSCPDURLData);
691 scpd_url = gupnp_service_info_get_scpd_url (info);
693 data->message = NULL;
694 if (scpd_url != NULL) {
695 data->message = soup_message_new (SOUP_METHOD_GET, scpd_url);
700 if (data->message == NULL) {
705 GUPNP_SERVER_ERROR_INVALID_URL,
706 "No valid SCPD URL defined");
708 callback (info, NULL, error, user_data);
710 g_error_free (error);
712 g_slice_free (GetSCPDURLData, data);
718 data->callback = callback;
719 data->user_data = user_data;
721 /* Send off the message */
722 info->priv->pending_gets =
723 g_list_prepend (info->priv->pending_gets,
726 session = gupnp_context_get_session (info->priv->context);
728 soup_session_queue_message (session,
730 (SoupSessionCallback) got_scpd_url,