Update gupnp to 0.20.5 (fdeb6f9f)
[profile/ivi/GUPnP.git] / doc / client-tutorial.xml
1 <?xml version="1.0"?>
2 <!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
3
4 <chapter id="client-tutorial">
5   <title>Writing a UPnP Client</title>
6
7   <simplesect>
8     <title>Introduction</title>
9     <para>
10       This chapter explains how to write an application which fetches the
11       external IP address from an UPnP-compliant modem.  To do this a
12       <glossterm>Control Point</glossterm> is created, which searches for
13       services of the type
14       <literal>urn:schemas-upnp-org:service:WANIPConnection:1</literal> (part of
15       the <ulink url="http://upnp.org/standardizeddcps/igd.asp">Internet Gateway
16       Device</ulink> specification).  As services are discovered
17       <firstterm>Service Proxy</firstterm> objects are created by GUPnP to allow
18       interaction with the service, on which we can invoke the action
19       <function>GetExternalIPAddress</function> to fetch the external IP
20       address.
21     </para>
22   </simplesect>
23
24   <simplesect>
25     <title>Finding Services</title>
26     <para>
27       First, we initialize GUPnP and create a control point targeting the
28       service type.  Then we connect a signal handler so that we are notified
29       when services we are interested in are found.
30     </para>
31     <programlisting>#include &lt;libgupnp/gupnp-control-point.h&gt;
32
33 static GMainLoop *main_loop;
34
35 static void
36 service_proxy_available_cb (GUPnPControlPoint *cp,
37                             GUPnPServiceProxy *proxy,
38                             gpointer           userdata)
39 {
40   /* &hellip; */
41 }
42
43 int
44 main (int argc, char **argv)
45 {
46   GUPnPContext *context;
47   GUPnPControlPoint *cp;
48   
49   /* Required initialisation */
50   #if !GLIB_CHECK_VERSION(2,35,0)
51     g_type_init ();
52   #endif
53
54   /* Create a new GUPnP Context.  By here we are using the default GLib main
55      context, and connecting to the current machine's default IP on an
56      automatically generated port. */
57   context = gupnp_context_new (NULL, NULL, 0, NULL);
58
59   /* Create a Control Point targeting WAN IP Connection services */
60   cp = gupnp_control_point_new
61     (context, "urn:schemas-upnp-org:service:WANIPConnection:1");
62
63   /* The service-proxy-available signal is emitted when any services which match
64      our target are found, so connect to it */
65   g_signal_connect (cp,
66                     "service-proxy-available",
67                     G_CALLBACK (service_proxy_available_cb),
68                     NULL);
69
70   /* Tell the Control Point to start searching */
71   gssdp_resource_browser_set_active (GSSDP_RESOURCE_BROWSER (cp), TRUE);
72   
73   /* Enter the main loop. This will start the search and result in callbacks to
74      service_proxy_available_cb. */
75   main_loop = g_main_loop_new (NULL, FALSE);
76   g_main_loop_run (main_loop);
77
78   /* Clean up */
79   g_main_loop_unref (main_loop);
80   g_object_unref (cp);
81   g_object_unref (context);
82   
83   return 0;
84 }</programlisting>
85   </simplesect>
86
87   <simplesect>
88     <title>Invoking Actions</title>
89     <para>
90       Now we have an application which searches for the service we specified and
91       calls <function>service_proxy_available_cb</function> for each one it
92       found.  To get the external IP address we need to invoke the
93       <literal>GetExternalIPAddress</literal> action.  This action takes no in
94       arguments, and has a single out argument called "NewExternalIPAddress".
95       GUPnP has a set of methods to invoke actions (which will be very familiar
96       to anyone who has used <literal>dbus-glib</literal>) where you pass a
97       <constant>NULL</constant>-terminated varargs list of (name, GType, value)
98       tuples for the in arguments, then a <constant>NULL</constant>-terminated
99       varargs list of (name, GType, return location) tuples for the out
100       arguments.
101     </para>
102     <programlisting>static void
103 service_proxy_available_cb (GUPnPControlPoint *cp,
104                             GUPnPServiceProxy *proxy,
105                             gpointer           userdata)
106 {
107   GError *error = NULL;
108   char *ip = NULL;
109   
110   gupnp_service_proxy_send_action (proxy,
111                                    /* Action name and error location */
112                                    "GetExternalIPAddress", &amp;error,
113                                    /* IN args */
114                                    NULL,
115                                    /* OUT args */
116                                    "NewExternalIPAddress",
117                                    G_TYPE_STRING, &amp;ip,
118                                    NULL);
119   
120   if (error == NULL) {
121     g_print ("External IP address is %s\n", ip);
122     g_free (ip);
123   } else {
124     g_printerr ("Error: %s\n", error-&gt;message);
125     g_error_free (error);
126   }
127   g_main_loop_quit (main_loop);
128 }</programlisting>
129     <para>
130       Note that gupnp_service_proxy_send_action() blocks until the service has
131       replied.  If you need to make non-blocking calls then use
132       gupnp_service_proxy_begin_action(), which takes a callback that will be
133       called from the mainloop when the reply is received.
134     </para>
135   </simplesect>
136
137   <simplesect>
138     <title>Subscribing to state variable change notifications</title>
139     <para>
140       It is possible to get change notifications for the service state variables 
141       that have attribute <literal>sendEvents="yes"</literal>. We'll demonstrate
142       this by modifying <function>service_proxy_available_cb</function> and using
143       gupnp_service_proxy_add_notify() to setup a notification callback:
144     </para>
145     <programlisting>static void
146 external_ip_address_changed (GUPnPServiceProxy *proxy,
147                              const char        *variable,
148                              GValue            *value,
149                              gpointer           userdata)
150 {
151   g_print ("External IP address changed: %s\n", g_value_get_string (value));
152 }
153
154 static void
155 service_proxy_available_cb (GUPnPControlPoint *cp,
156                             GUPnPServiceProxy *proxy,
157                             gpointer           userdata)
158 {
159   g_print ("Found a WAN IP Connection service\n");
160   
161   gupnp_service_proxy_set_subscribed (proxy, TRUE);
162   if (!gupnp_service_proxy_add_notify (proxy,
163                                        "ExternalIPAddress",
164                                        G_TYPE_STRING,
165                                        external_ip_address_changed,
166                                        NULL)) {
167     g_printerr ("Failed to add notify");
168   }
169 }</programlisting>
170   </simplesect>
171
172   <simplesect>
173     <title>Generating Wrappers</title>
174     <para>
175       Using gupnp_service_proxy_send_action() and gupnp_service_proxy_add_notify ()
176       can become tedious, because of the requirement to specify the types and deal
177       with GValues.  An
178       alternative is to use <xref linkend="gupnp-binding-tool"/>, which
179       generates wrappers that hide the boilerplate code from you.  Using a 
180       wrapper generated with prefix 'ipconn' would replace 
181       gupnp_service_proxy_send_action() with this code:
182     </para>
183     <programlisting>ipconn_get_external_ip_address (proxy, &amp;ip, &amp;error);</programlisting>
184     <para>
185       State variable change notifications are friendlier with wrappers as well:
186     </para>
187     <programlisting>static void
188 external_ip_address_changed (GUPnPServiceProxy *proxy,
189                              const gchar       *external_ip_address,
190                              gpointer           userdata)
191 {
192   g_print ("External IP address changed: '%s'\n", external_ip_address);
193 }
194
195 static void
196 service_proxy_available_cb (GUPnPControlPoint *cp,
197                             GUPnPServiceProxy *proxy
198                             gpointer           userdata)
199 {
200   g_print ("Found a WAN IP Connection service\n");
201   
202   gupnp_service_proxy_set_subscribed (proxy, TRUE);
203   if (!ipconn_external_ip_address_add_notify (proxy,
204                                               external_ip_address_changed,
205                                               NULL)) {
206     g_printerr ("Failed to add notify");
207   }
208 }</programlisting>
209   </simplesect>
210 </chapter>