4a60e3b9c7941e1b969acfcf6d8b3f22e53ab051
[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   g_type_init ();
51
52   /* Create a new GUPnP Context.  By here we are using the default GLib main
53      context, and connecting to the current machine's default IP on an
54      automatically generated port. */
55   context = gupnp_context_new (NULL, NULL, 0, NULL);
56
57   /* Create a Control Point targeting WAN IP Connection services */
58   cp = gupnp_control_point_new
59     (context, "urn:schemas-upnp-org:service:WANIPConnection:1");
60
61   /* The service-proxy-available signal is emitted when any services which match
62      our target are found, so connect to it */
63   g_signal_connect (cp,
64                     "service-proxy-available",
65                     G_CALLBACK (service_proxy_available_cb),
66                     NULL);
67
68   /* Tell the Control Point to start searching */
69   gssdp_resource_browser_set_active (GSSDP_RESOURCE_BROWSER (cp), TRUE);
70   
71   /* Enter the main loop. This will start the search and result in callbacks to
72      service_proxy_available_cb. */
73   main_loop = g_main_loop_new (NULL, FALSE);
74   g_main_loop_run (main_loop);
75
76   /* Clean up */
77   g_main_loop_unref (main_loop);
78   g_object_unref (cp);
79   g_object_unref (context);
80   
81   return 0;
82 }</programlisting>
83   </simplesect>
84
85   <simplesect>
86     <title>Invoking Actions</title>
87     <para>
88       Now we have an application which searches for the service we specified and
89       calls <function>service_proxy_available_cb</function> for each one it
90       found.  To get the external IP address we need to invoke the
91       <literal>GetExternalIPAddress</literal> action.  This action takes no in
92       arguments, and has a single out argument called "NewExternalIPAddress".
93       GUPnP has a set of methods to invoke actions (which will be very familiar
94       to anyone who has used <literal>dbus-glib</literal>) where you pass a
95       <constant>NULL</constant>-terminated varargs list of (name, GType, value)
96       tuples for the in arguments, then a <constant>NULL</constant>-terminated
97       varargs list of (name, GType, return location) tuples for the out
98       arguments.
99     </para>
100     <programlisting>static void
101 service_proxy_available_cb (GUPnPControlPoint *cp,
102                             GUPnPServiceProxy *proxy,
103                             gpointer           userdata)
104 {
105   GError *error = NULL;
106   char *ip = NULL;
107   
108   gupnp_service_proxy_send_action (proxy,
109                                    /* Action name and error location */
110                                    "GetExternalIPAddress", &amp;error,
111                                    /* IN args */
112                                    NULL,
113                                    /* OUT args */
114                                    "NewExternalIPAddress",
115                                    G_TYPE_STRING, &amp;ip,
116                                    NULL);
117   
118   if (error == NULL) {
119     g_print ("External IP address is %s\n", ip);
120     g_free (ip);
121   } else {
122     g_printerr ("Error: %s\n", error-&gt;message);
123     g_error_free (error);
124   }
125   g_main_loop_quit (main_loop);
126 }</programlisting>
127     <para>
128       Note that gupnp_service_proxy_send_action() blocks until the service has
129       replied.  If you need to make non-blocking calls then use
130       gupnp_service_proxy_begin_action(), which takes a callback that will be
131       called from the mainloop when the reply is received.
132     </para>
133   </simplesect>
134
135   <simplesect>
136     <title>Subscribing to state variable change notifications</title>
137     <para>
138       It is possible to get change notifications for the service state variables 
139       that have attribute <literal>sendEvents="yes"</literal>. We'll demonstrate
140       this by modifying <function>service_proxy_available_cb()</function> and using 
141       gupnp_service_proxy_add_notify() to setup a notification callback:
142     </para>
143     <programlisting>static void
144 external_ip_address_changed (GUPnPServiceProxy *proxy,
145                              const char        *variable,
146                              GValue            *value,
147                              gpointer           userdata)
148 {
149   g_print ("External IP address changed: %s\n", g_value_get_string (value));
150 }
151
152 static void
153 service_proxy_available_cb (GUPnPControlPoint *cp,
154                             GUPnPServiceProxy *proxy,
155                             gpointer           userdata)
156 {
157   g_print ("Found a WAN IP Connection service\n");
158   
159   gupnp_service_proxy_set_subscribed (proxy, TRUE);
160   if (!gupnp_service_proxy_add_notify (proxy,
161                                        "ExternalIPAddress",
162                                        G_TYPE_STRING,
163                                        external_ip_address_changed,
164                                        NULL)) {
165     g_printerr ("Failed to add notify");
166   }
167 }</programlisting>
168   </simplesect>
169
170   <simplesect>
171     <title>Generating Wrappers</title>
172     <para>
173       Using gupnp_service_proxy_send_action() and gupnp_service_proxy_add_notify ()
174       can become tedious, because of the requirement to specify the types and deal
175       with GValues.  An
176       alternative is to use <xref linkend="gupnp-binding-tool"/>, which
177       generates wrappers that hide the boilerplate code from you.  Using a 
178       wrapper generated with prefix 'ipconn' would replace 
179       gupnp_service_proxy_send_action() with this code:
180     </para>
181     <programlisting>ipconn_get_external_ip_address (proxy, &amp;ip, &amp;error);</programlisting>
182     <para>
183       State variable change notifications are friendlier with wrappers as well:
184     </para>
185     <programlisting>static void
186 external_ip_address_changed (GUPnPServiceProxy *proxy,
187                              const gchar       *external_ip_address,
188                              gpointer           userdata)
189 {
190   g_print ("External IP address changed: '%s'\n", external_ip_address);
191 }
192
193 static void
194 service_proxy_available_cb (GUPnPControlPoint *cp,
195                             GUPnPServiceProxy *proxy
196                             gpointer           userdata)
197 {
198   g_print ("Found a WAN IP Connection service\n");
199   
200   gupnp_service_proxy_set_subscribed (proxy, TRUE);
201   if (!ipconn_external_ip_address_add_notify (proxy,
202                                               external_ip_address_changed,
203                                               NULL)) {
204     g_printerr ("Failed to add notify");
205   }
206 }</programlisting>
207   </simplesect>
208 </chapter>