tizen 2.3.1 release
[external/gupnp.git] / libgupnp / gupnp-service-introspection.c
1 /*
2  * Copyright (C) 2007 Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
3  * Copyright (C) 2006, 2007 OpenedHand Ltd.
4  *
5  * Author: Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
6  *         Jorn Baayen <jorn@openedhand.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 /**
25  * SECTION:gupnp-service-introspection
26  * @short_description: Service introspection class.
27  *
28  * The #GUPnPServiceIntrospection class provides methods for service
29  * introspection based on information contained in its service description
30  * document (SCPD). There is no constructor provided for this class, please use
31  * #gupnp_service_info_get_introspection or
32  * #gupnp_service_info_get_introspection_async to create an
33  * #GUPnPServiceIntrospection object for a specific service.
34  *
35  * Note that all the introspection information is retreived from the service
36  * description document (SCPD) provided by the service and hence can not be
37  * guaranteed to be complete. A UPnP service is required to provide an SCPD but
38  * unfortunately, many services either do not provide this document or the
39  * document does not provide any or all of the introspection information.
40  *
41  * This class exposes internals of the UPnP protocol and should not need
42  * to be used for regular device or control point development.
43  *
44  **/
45
46 #include <libsoup/soup.h>
47 #include <string.h>
48
49 #include "gupnp-service-introspection.h"
50 #include "gupnp-service-introspection-private.h"
51 #include "xml-util.h"
52 #include "gvalue-util.h"
53 #include "gupnp-types.h"
54 #include "gupnp-types-private.h"
55
56 #define MAX_FIXED_14_4 99999999999999.9999
57
58 G_DEFINE_TYPE (GUPnPServiceIntrospection,
59                gupnp_service_introspection,
60                G_TYPE_OBJECT);
61
62 struct _GUPnPServiceIntrospectionPrivate {
63         GList *variables;
64         GList *actions;
65
66         /* For caching purposes */
67         GList *action_names;
68         GList *variable_names;
69 };
70
71 enum {
72         PROP_0,
73         PROP_SCPD
74 };
75
76 static void
77 construct_introspection_info (GUPnPServiceIntrospection *introspection,
78                               xmlDoc                    *scpd);
79
80 /**
81  * gupnp_service_state_variable_info_free:
82  * @argument: A #GUPnPServiceStateVariableInfo
83  *
84  * Frees a #GUPnPServiceStateVariableInfo.
85  *
86  **/
87 static void
88 gupnp_service_state_variable_info_free
89                                 (GUPnPServiceStateVariableInfo *variable)
90 {
91         g_free (variable->name);
92         g_value_unset (&variable->default_value);
93         if (variable->is_numeric) {
94                 g_value_unset (&variable->minimum);
95                 g_value_unset (&variable->maximum);
96                 g_value_unset (&variable->step);
97         }
98         g_list_foreach (variable->allowed_values,
99                          (GFunc) g_free,
100                          NULL);
101         g_list_free (variable->allowed_values);
102
103         g_slice_free (GUPnPServiceStateVariableInfo, variable);
104 }
105
106 static void
107 gupnp_service_introspection_init (GUPnPServiceIntrospection *introspection)
108 {
109         introspection->priv =
110                 G_TYPE_INSTANCE_GET_PRIVATE (introspection,
111                                              GUPNP_TYPE_SERVICE_INTROSPECTION,
112                                              GUPnPServiceIntrospectionPrivate);
113 }
114
115 static void
116 gupnp_service_introspection_set_property (GObject      *object,
117                                           guint        property_id,
118                                           const GValue *value,
119                                           GParamSpec   *pspec)
120 {
121         GUPnPServiceIntrospection *introspection;
122
123         introspection = GUPNP_SERVICE_INTROSPECTION (object);
124
125         switch (property_id) {
126         case PROP_SCPD:
127                 /* Construct introspection data */
128                 construct_introspection_info (introspection,
129                                               g_value_get_pointer (value));
130                 break;
131         default:
132                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
133                 break;
134         }
135 }
136
137
138 /**
139  * gupnp_service_action_arg_info_free:
140  * @argument: A #GUPnPServiceActionArgInfo
141  *
142  * Frees a #GUPnPServiceActionArgInfo.
143  *
144  **/
145 static void
146 gupnp_service_action_arg_info_free (GUPnPServiceActionArgInfo *argument)
147 {
148         g_free (argument->name);
149         g_free (argument->related_state_variable);
150
151         g_slice_free (GUPnPServiceActionArgInfo, argument);
152 }
153
154 /**
155  * gupnp_service_action_info_free:
156  * @argument: A #GUPnPServiceActionInfo
157  *
158  * Frees a #GUPnPServiceActionInfo.
159  *
160  **/
161 static void
162 gupnp_service_action_info_free (GUPnPServiceActionInfo *action_info)
163 {
164         GList *iter;
165
166         g_free (action_info->name);
167
168         for (iter = action_info->arguments; iter; iter = iter->next) {
169                 gupnp_service_action_arg_info_free (
170                                 (GUPnPServiceActionArgInfo *) iter->data);
171         }
172         g_list_free (action_info->arguments);
173         g_slice_free (GUPnPServiceActionInfo, action_info);
174 }
175
176 static void
177 gupnp_service_introspection_finalize (GObject *object)
178 {
179         GUPnPServiceIntrospection *introspection;
180
181         introspection = GUPNP_SERVICE_INTROSPECTION (object);
182
183         if (introspection->priv->variables) {
184                 g_list_foreach (introspection->priv->variables,
185                                 (GFunc) gupnp_service_state_variable_info_free,
186                                 NULL);
187                 g_list_free (introspection->priv->variables);
188         }
189
190         if (introspection->priv->actions) {
191                 g_list_foreach (introspection->priv->actions,
192                                 (GFunc) gupnp_service_action_info_free,
193                                 NULL);
194                 g_list_free (introspection->priv->actions);
195         }
196
197         if (introspection->priv->variable_names)
198                 g_list_free (introspection->priv->variable_names);
199
200         if (introspection->priv->action_names)
201                 g_list_free (introspection->priv->action_names);
202 }
203
204 static void
205 gupnp_service_introspection_class_init (GUPnPServiceIntrospectionClass *klass)
206 {
207         GObjectClass *object_class;
208
209         object_class = G_OBJECT_CLASS (klass);
210
211         object_class->set_property = gupnp_service_introspection_set_property;
212         object_class->finalize     = gupnp_service_introspection_finalize;
213
214         g_type_class_add_private (klass,
215                                   sizeof (GUPnPServiceIntrospectionPrivate));
216
217         /**
218          * GUPnPServiceIntrospection:scpd:
219          *
220          * The scpd of the device description file.
221          **/
222         g_object_class_install_property
223                 (object_class,
224                  PROP_SCPD,
225                  g_param_spec_pointer ("scpd",
226                                        "SCPD",
227                                        "Pointer to SCPD",
228                                        G_PARAM_WRITABLE |
229                                        G_PARAM_CONSTRUCT_ONLY));
230 }
231
232 static void
233 set_default_value (xmlNodePtr                     variable_node,
234                    GUPnPServiceStateVariableInfo *variable)
235 {
236         xmlChar *default_str;
237
238         default_str = xml_util_get_child_element_content (variable_node,
239                                                           "defaultValue");
240         if (default_str) {
241                 gvalue_util_set_value_from_string (&variable->default_value,
242                                                    (char *) default_str);
243
244                 xmlFree (default_str);
245         }
246 }
247
248 static void
249 set_string_value_limits (xmlNodePtr   limit_node,
250                          GList     **limits)
251 {
252         xmlNodePtr value_node;
253
254         for (value_node = limit_node->children;
255              value_node;
256              value_node = value_node->next) {
257                 xmlChar *allowed_value;
258
259                 if (strcmp ("allowedValue", (char *) value_node->name) != 0)
260                         continue;
261
262                 allowed_value = xmlNodeGetContent (value_node);
263                 if (!allowed_value)
264                         continue;
265
266                 *limits = g_list_append (*limits,
267                                           g_strdup ((char *) allowed_value));
268                 xmlFree (allowed_value);
269         }
270 }
271
272 static void
273 set_value_limit_by_name (xmlNodePtr  limit_node,
274                          GValue     *limit,
275                          const char *limit_name)
276 {
277         xmlChar *limit_str;
278
279         limit_str = xml_util_get_child_element_content (limit_node,
280                                                         limit_name);
281         if (limit_str) {
282                 gvalue_util_set_value_from_string (limit, (char *) limit_str);
283
284                 xmlFree (limit_str);
285         }
286 }
287
288 static void
289 set_variable_limits (xmlNodePtr                     variable_node,
290                      GUPnPServiceStateVariableInfo *variable)
291 {
292         xmlNodePtr limit_node;
293
294         if (variable->is_numeric) {
295                 limit_node = xml_util_get_element (variable_node,
296                                                    "allowedValueRange",
297                                                    NULL);
298                 if (limit_node == NULL)
299                         return;
300
301                 set_value_limit_by_name (limit_node,
302                                 &(variable->minimum),
303                                 "minimum");
304                 set_value_limit_by_name (limit_node,
305                                 &(variable->maximum),
306                                 "maximum");
307                 set_value_limit_by_name (limit_node,
308                                 &(variable->step),
309                                 "step");
310         } else if (variable->type == G_TYPE_STRING) {
311                 limit_node = xml_util_get_element (variable_node,
312                                                    "allowedValueList",
313                                                    NULL);
314                 if (limit_node == NULL)
315                         return;
316
317                 set_string_value_limits (limit_node,
318                                          &(variable->allowed_values));
319        }
320 }
321
322 static gboolean
323 set_variable_type (GUPnPServiceStateVariableInfo *variable,
324                    char                          *data_type)
325 {
326         GType type;
327
328         if (strcmp ("string", data_type) == 0) {
329                 type = G_TYPE_STRING;
330         }
331
332         else if (strcmp ("char", data_type) == 0) {
333                 type = G_TYPE_CHAR;
334         }
335
336         else if (strcmp ("boolean", data_type) == 0) {
337                 type = G_TYPE_BOOLEAN;
338         }
339
340         else if (strcmp ("i1", data_type) == 0) {
341                 type = G_TYPE_INT;
342                 g_value_init (&variable->minimum, type);
343                 g_value_set_int (&variable->minimum, G_MININT8);
344                 g_value_init (&variable->maximum, type);
345                 g_value_set_int (&variable->maximum, G_MAXINT8);
346                 g_value_init (&variable->step, type);
347                 g_value_set_int (&variable->step, 1);
348                 variable->is_numeric = TRUE;
349         }
350
351         else if (strcmp ("i2", data_type) == 0) {
352                 type = G_TYPE_INT;
353                 g_value_init (&variable->minimum, type);
354                 g_value_set_int (&variable->minimum, G_MININT16);
355                 g_value_init (&variable->maximum, type);
356                 g_value_set_int (&variable->maximum, G_MAXINT16);
357                 g_value_init (&variable->step, type);
358                 g_value_set_int (&variable->step, 1);
359                 variable->is_numeric = TRUE;
360         }
361
362         else if (strcmp ("i4", data_type) == 0 ||
363                  strcmp ("int", data_type) == 0) {
364                 type = G_TYPE_INT;
365                 g_value_init (&variable->minimum, type);
366                 g_value_set_int (&variable->minimum, G_MININT32);
367                 g_value_init (&variable->maximum, type);
368                 g_value_set_int (&variable->maximum, G_MAXINT32);
369                 g_value_init (&variable->step, type);
370                 g_value_set_int (&variable->step, 1);
371                 variable->is_numeric = TRUE;
372         }
373
374         else if (strcmp ("ui1", data_type) == 0) {
375                 type = G_TYPE_UINT;
376                 g_value_init (&variable->minimum, type);
377                 g_value_set_uint (&variable->minimum, 0);
378                 g_value_init (&variable->maximum, type);
379                 g_value_set_uint (&variable->maximum, G_MAXUINT8);
380                 g_value_init (&variable->step, type);
381                 g_value_set_uint (&variable->step, 1);
382                 variable->is_numeric = TRUE;
383         }
384
385         else if (strcmp ("ui2", data_type) == 0) {
386                 type = G_TYPE_UINT;
387                 g_value_init (&variable->minimum, type);
388                 g_value_set_uint (&variable->minimum, 0);
389                 g_value_init (&variable->maximum, type);
390                 g_value_set_uint (&variable->maximum, G_MAXUINT16);
391                 g_value_init (&variable->step, type);
392                 g_value_set_uint (&variable->step, 1);
393                 variable->is_numeric = TRUE;
394         }
395
396         else if (strcmp ("ui4", data_type) == 0) {
397                 type = G_TYPE_UINT;
398                 g_value_init (&variable->minimum, type);
399                 g_value_set_uint (&variable->minimum, 0);
400                 g_value_init (&variable->maximum, type);
401                 g_value_set_uint (&variable->maximum, G_MAXUINT32);
402                 g_value_init (&variable->step, type);
403                 g_value_set_uint (&variable->step, 1);
404                 variable->is_numeric = TRUE;
405         }
406
407         else if (strcmp ("r4", data_type) == 0) {
408                 type = G_TYPE_FLOAT;
409                 g_value_init (&variable->minimum, type);
410                 g_value_set_float (&variable->minimum, -G_MAXFLOAT);
411                 g_value_init (&variable->maximum, type);
412                 g_value_set_float (&variable->maximum, G_MAXFLOAT);
413                 g_value_init (&variable->step, type);
414                 g_value_set_float (&variable->step, 1.0);
415                 variable->is_numeric = TRUE;
416         }
417
418         else if (strcmp ("r8", data_type) == 0 ||
419                  strcmp ("number", data_type) == 0) {
420                 type = G_TYPE_DOUBLE;
421                 g_value_init (&variable->minimum, type);
422                 g_value_set_double (&variable->minimum,  -G_MAXDOUBLE);
423                 g_value_init (&variable->maximum, type);
424                 g_value_set_double (&variable->maximum, G_MAXDOUBLE);
425                 g_value_init (&variable->step, type);
426                 g_value_set_double (&variable->step, 1.0);
427                 variable->is_numeric = TRUE;
428         }
429
430         else if (strcmp ("fixed.14.4", data_type) == 0) {
431                 /* Just how did this get into the UPnP specs? */
432                 type = G_TYPE_DOUBLE;
433                 g_value_init (&variable->minimum, type);
434                 g_value_set_double (&variable->minimum,  -MAX_FIXED_14_4);
435                 g_value_init (&variable->maximum, type);
436                 g_value_set_double (&variable->maximum, MAX_FIXED_14_4);
437                 g_value_init (&variable->step, type);
438                 g_value_set_double (&variable->step, 1.0);
439                 variable->is_numeric = TRUE;
440         }
441         /* TODO: "float", "int" */
442
443         else {
444                 type = gupnp_data_type_to_gtype (data_type);
445         }
446
447         if (type == G_TYPE_INVALID) {
448                 g_warning ("Unkown type '%s' in the SCPD", data_type);
449                 return FALSE;
450         }
451
452         g_value_init (&variable->default_value, type);
453         variable->type = type;
454
455         return TRUE;
456 }
457
458 /*
459  *
460  * Creates a #GUPnPServiceStateVariableInfo, constructed from the stateVariable
461  * node @variable_node in the SCPD document
462  *
463  */
464 static GUPnPServiceStateVariableInfo *
465 get_state_variable (xmlNodePtr variable_node)
466 {
467         GUPnPServiceStateVariableInfo *variable;
468         xmlChar *send_events;
469         char *data_type;
470         gboolean success;
471
472         data_type = xml_util_get_child_element_content_glib (variable_node,
473                                                              "dataType");
474         if (!data_type) {
475                 /* We can't report much about a state variable whose dataType
476                  * is not specified so better not report anything at all */
477                 return NULL;
478         }
479
480         variable = g_slice_new0 (GUPnPServiceStateVariableInfo);
481
482         success = set_variable_type (variable, data_type);
483         g_free (data_type);
484         if (!success)
485                 return NULL;
486
487         set_variable_limits (variable_node, variable);
488         set_default_value (variable_node, variable);
489
490         send_events = xml_util_get_child_element_content
491                                        (variable_node, "sendEventsAttribute");
492         if (send_events == NULL) {
493                 /* Some documents put it as attribute of the tag */
494                 send_events = xml_util_get_attribute_contents (variable_node,
495                                                                "sendEvents");
496         }
497
498         if (send_events) {
499                 if (strcmp ("yes", (char *) send_events) == 0)
500                         variable->send_events = TRUE;
501                 xmlFree (send_events);
502         }
503
504         return variable;
505 }
506
507 /*
508  *
509  * Creates a #GUPnPServiceActionArgInfo, constructed from the argument node
510  * @argument_node in the SCPD document
511  *
512  */
513 static GUPnPServiceActionArgInfo *
514 get_action_argument (xmlNodePtr argument_node)
515 {
516         GUPnPServiceActionArgInfo *argument;
517         char *name, *state_var;
518         xmlChar *direction;
519
520         name      = xml_util_get_child_element_content_glib
521                                        (argument_node, "name");
522         state_var = xml_util_get_child_element_content_glib
523                                        (argument_node, "relatedStateVariable");
524         direction = xml_util_get_child_element_content
525                                        (argument_node, "direction");
526
527         if (!name || !state_var || !direction) {
528                 g_free (name);
529                 g_free (state_var);
530
531                 xmlFree (direction);
532
533                 return NULL;
534         }
535
536         argument = g_slice_new0 (GUPnPServiceActionArgInfo);
537
538         argument->name = name;
539         argument->related_state_variable = state_var;
540
541         if (strcmp ("in", (char *) direction) == 0)
542                 argument->direction = GUPNP_SERVICE_ACTION_ARG_DIRECTION_IN;
543         else
544                 argument->direction = GUPNP_SERVICE_ACTION_ARG_DIRECTION_OUT;
545         xmlFree (direction);
546
547         if (xml_util_get_element (argument_node, "retval", NULL) != NULL)
548                 argument->retval = TRUE;
549
550         return argument;
551 }
552
553 /*
554  *
555  * Creates a #GList of all the arguments (of type #GUPnPServiceActionArgInfo)
556  * from the action node @action_node in the SCPD document
557  *
558  */
559 static GList *
560 get_action_arguments (xmlNodePtr action_node)
561 {
562         GList *argument_list = NULL;
563         xmlNodePtr arglist_node;
564         xmlNodePtr argument_node;
565
566         arglist_node = xml_util_get_element ((xmlNode *) action_node,
567                                              "argumentList",
568                                              NULL);
569         if (!arglist_node)
570                 return NULL;
571
572         /* Iterate over all the arguments */
573         for (argument_node = arglist_node->children;
574              argument_node;
575              argument_node = argument_node->next) {
576                 GUPnPServiceActionArgInfo *argument;
577
578                 if (strcmp ("argument", (char *) argument_node->name) != 0)
579                         continue;
580
581                 argument = get_action_argument (argument_node);
582                 if (argument) {
583                         argument_list = g_list_append (argument_list,
584                                                         argument);
585                 }
586         }
587
588         return argument_list;
589 }
590
591 /*
592  *
593  * Creates a #GList of all the actions (of type #GUPnPServiceActionInfo) from
594  * the SCPD document.
595  *
596  */
597 static GList *
598 get_actions (xmlNode *list_element)
599 {
600         GList *actions = NULL;
601         xmlNodePtr action_node;
602
603         /* Iterate over all action elements */
604         for (action_node = list_element->children;
605              action_node;
606              action_node = action_node->next) {
607                 GUPnPServiceActionInfo *action_info;
608                 GList *arguments;
609                 char *name;
610
611                 if (strcmp ("action", (char *) action_node->name) != 0)
612                         continue;
613
614                 name = xml_util_get_child_element_content_glib (action_node,
615                                                                 "name");
616                 if (!name)
617                         continue;
618
619                 arguments = get_action_arguments (action_node);
620                 if (!arguments) {
621                         g_free (name);
622
623                         continue;
624                 }
625
626                 action_info = g_slice_new0 (GUPnPServiceActionInfo);
627                 action_info->name = name;
628                 action_info->arguments = arguments;
629
630                 actions = g_list_append (actions, action_info);
631         }
632
633         return actions;
634 }
635
636 /*
637  *
638  * Creates a #GList of all the state variables (of type
639  * #GUPnPServiceStateVariableInfo) from the SCPD document.
640  *
641  */
642 static GList *
643 get_state_variables (xmlNode *list_element)
644 {
645         GList *variables = NULL;
646         xmlNodePtr variable_node;
647
648         /* Iterate over all variable elements */
649         for (variable_node = list_element->children;
650              variable_node;
651              variable_node = variable_node->next) {
652                 char *name;
653                 GUPnPServiceStateVariableInfo *variable;
654
655                 if (strcmp ("stateVariable",
656                             (char *) variable_node->name) != 0)
657                         continue;
658
659                 name = xml_util_get_child_element_content_glib (variable_node,
660                                                                 "name");
661                 if (!name)
662                         continue;
663
664                 variable = get_state_variable (variable_node);
665                 if (!variable) {
666                         g_free (name);
667
668                         continue;
669                 }
670
671                 variable->name = name;
672                 variables = g_list_append (variables, variable);
673         }
674
675         return variables;
676 }
677
678 /*
679  * Creates the #GHashTable's of action and state variable information
680  *
681  * */
682 static void
683 construct_introspection_info (GUPnPServiceIntrospection *introspection,
684                               xmlDoc                    *scpd)
685 {
686         xmlNode *element;
687
688         g_return_if_fail (scpd != NULL);
689
690         /* Get actionList element */
691         element = xml_util_get_element ((xmlNode *) scpd,
692                                         "scpd",
693                                         "actionList",
694                                         NULL);
695         if (element)
696                 introspection->priv->actions = get_actions (element);
697
698         /* Get serviceStateTable element */
699         element = xml_util_get_element ((xmlNode *) scpd,
700                                         "scpd",
701                                         "serviceStateTable",
702                                         NULL);
703         if (element)
704                 introspection->priv->variables = get_state_variables (element);
705 }
706
707 static void
708 collect_action_names (gpointer data,
709                       gpointer user_data)
710 {
711         GList **action_names = (GList **) user_data;
712         GUPnPServiceActionInfo *action_info = (GUPnPServiceActionInfo *) data;
713
714         *action_names = g_list_append (*action_names, action_info->name);
715 }
716
717 static void
718 collect_variable_names (gpointer data,
719                         gpointer user_data)
720 {
721         GList **variable_names = (GList **) user_data;
722         GUPnPServiceStateVariableInfo *variable =
723                 (GUPnPServiceStateVariableInfo *) data;
724
725         *variable_names = g_list_append (*variable_names, variable->name);
726 }
727
728 /**
729  * gupnp_service_introspection_new:
730  * @scpd: Pointer to the SCPD of the service to create a introspection for
731  *
732  * Create a new #GUPnPServiceIntrospection for the service created from the
733  * SCPD @scpd or %NULL.
734  *
735  * Return value: A new #GUPnPServiceIntrospection.
736  **/
737 GUPnPServiceIntrospection *
738 gupnp_service_introspection_new (xmlDoc *scpd)
739 {
740         GUPnPServiceIntrospection *introspection;
741
742         g_return_val_if_fail (scpd != NULL, NULL);
743
744         introspection = g_object_new (GUPNP_TYPE_SERVICE_INTROSPECTION,
745                                       "scpd", scpd,
746                                       NULL);
747
748         if (introspection->priv->actions == NULL &&
749             introspection->priv->variables == NULL) {
750                 g_object_unref (introspection);
751                 introspection = NULL;
752         }
753
754         return introspection;
755 }
756
757 /**
758  * gupnp_service_introspection_list_action_names:
759  * @introspection: A #GUPnPServiceIntrospection
760  *
761  * Returns a GList of names of all the actions in this service.
762  *
763  * Return value: (transfer none) (element-type utf8) : A GList of names of all
764  * the actions or %NULL. Do not modify or free it or its contents.
765  **/
766 const GList *
767 gupnp_service_introspection_list_action_names
768                         (GUPnPServiceIntrospection *introspection)
769 {
770         if (introspection->priv->actions == NULL)
771                 return NULL;
772
773         if (introspection->priv->action_names == NULL) {
774                 g_list_foreach (introspection->priv->actions,
775                                 collect_action_names,
776                                 &introspection->priv->action_names);
777         }
778
779         return introspection->priv->action_names;
780 }
781
782 /**
783  * gupnp_service_introspection_list_actions:
784  * @introspection: A #GUPnPServiceIntrospection
785  *
786  * Returns a #GList of all the actions (of type #GUPnPServiceActionInfo) in
787  * this service.
788  *
789  * Return value: (element-type GUPnP.ServiceActionInfo*) (transfer none): A
790  * #GList of all the actions or %NULL. Do not modify or free it or its
791  * contents.
792  **/
793 const GList *
794 gupnp_service_introspection_list_actions
795                         (GUPnPServiceIntrospection *introspection)
796 {
797         return introspection->priv->actions;
798 }
799
800 /**
801  * gupnp_service_introspection_list_state_variables:
802  * @introspection: A #GUPnPServiceIntrospection
803  *
804  * Returns a GList of all the state variables (of type
805  * #GUPnPServiceStateVariableInfo) in this service.
806  *
807  * Return value: (element-type GUPnP.ServiceStateVariableInfo) (transfer none):
808  * A #GList of all the state variables or %NULL. Do not modify or free it or
809  * its contents.
810  *
811  **/
812 const GList *
813 gupnp_service_introspection_list_state_variables
814                         (GUPnPServiceIntrospection *introspection)
815 {
816         return introspection->priv->variables;
817 }
818
819 /**
820  * gupnp_service_introspection_list_state_variable_names:
821  * @introspection: A #GUPnPServiceIntrospection
822  *
823  * Returns a #GList of names of all the state variables in this service.
824  *
825  * Return value: (element-type utf8) (transfer none): A #GList of names of all
826  * the state variables or %NULL. Do not modify or free it or its contents.
827  **/
828 const GList *
829 gupnp_service_introspection_list_state_variable_names
830                         (GUPnPServiceIntrospection *introspection)
831 {
832         if (introspection->priv->variables == NULL)
833                 return NULL;
834
835         if (introspection->priv->variable_names == NULL) {
836                 g_list_foreach (introspection->priv->variables,
837                                 collect_variable_names,
838                                 &introspection->priv->variable_names);
839         }
840
841         return introspection->priv->variable_names;
842 }
843
844 static gint
845 state_variable_search_func (GUPnPServiceStateVariableInfo *variable,
846                             const gchar                   *variable_name)
847 {
848         return strcmp (variable->name, variable_name);
849 }
850
851 /**
852  * gupnp_service_introspection_get_state_variable:
853  * @introspection: A #GUPnPServiceIntrospection
854  * @variable_name: The name of the variable to retreive
855  *
856  * Returns the state variable by the name @variable_name in this service.
857  *
858  * Return value: the state variable or %NULL. Do not modify or free it.
859  **/
860 const GUPnPServiceStateVariableInfo *
861 gupnp_service_introspection_get_state_variable
862                         (GUPnPServiceIntrospection *introspection,
863                          const gchar               *variable_name)
864 {
865         GList *variable_node;
866
867         if (introspection->priv->variables == NULL)
868                 return NULL;
869
870         variable_node = g_list_find_custom (
871                                 introspection->priv->variables,
872                                 (gpointer) variable_name,
873                                 (GCompareFunc) state_variable_search_func);
874         if (variable_node == NULL)
875                 return NULL;
876
877         return (GUPnPServiceStateVariableInfo *) variable_node->data;
878 }
879
880 static gint
881 action_search_func (GUPnPServiceActionInfo *action,
882                     const gchar            *action_name)
883 {
884         return strcmp (action->name, action_name);
885 }
886
887 /**
888  * gupnp_service_introspection_get_action:
889  * @introspection: A #GUPnPServiceIntrospection
890  * @action_name: The name of the action to retreive
891  *
892  * Returns the action by the name @action_name in this service.
893  *
894  * Return value: (transfer none): the action or %NULL. Do not modify or free
895  * it.
896  **/
897 const GUPnPServiceActionInfo *
898 gupnp_service_introspection_get_action
899                         (GUPnPServiceIntrospection *introspection,
900                          const gchar               *action_name)
901 {
902         GList *action_node;
903
904         if (introspection->priv->variables == NULL)
905                 return NULL;
906
907         action_node = g_list_find_custom (
908                                 introspection->priv->actions,
909                                 (gpointer) action_name,
910                                 (GCompareFunc) action_search_func);
911         if (action_node == NULL)
912                 return NULL;
913
914         return (GUPnPServiceActionInfo *) action_node->data;
915 }