summaryrefslogtreecommitdiffstats
blob: ea4ccd4289db41c3945510d05d42bbdf8dc79701 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
<?xml version="1.0" ?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">

<book>
  <title>Swiften Developer's Guide</title>

  <chapter>
    <title>Introduction</title>
    <section>
      <title>Prerequisites</title>
      <para>
        We assume that the reader is familiar with the basics of the
        XMPP protocol. For an overview of the XMPP protocol and its
        workings, see
        <citetitle>XMPP: The Definitive Guide</citetitle> 
        <citation><biblioref linkend="XMPP-TDG"/></citation>
      </para>
    </section>

    <section>
      <title>Boost</title>
      <para>
        Swiften makes heavy use of <emphasis>Boost</emphasis> (<ulink url="http://boost.org"><uri>http://boost.org</uri></ulink>) libraries, including <emphasis>Signal</emphasis>,
        <emphasis>Bind</emphasis>, <emphasis>Optional</emphasis>, and 
        <emphasis>Smart Pointers</emphasis>. We
        introduce the basic usage of these libraries in our API throughout
        this manual. For detailed documentation, we refer to the <ulink url="http://boost.org">Boost</ulink> website.
      </para>
    </section>
  </chapter>

  <chapter>
    <title>Tutorial: Writing an Echo Bot</title>
    
    <para>
      In this chapter, we guide you through the Swiften API by building an
      example XMPP application: an EchoBot. This example program, taken from
      <citetitle>XMPP: The Definitive Guide</citetitle> 
      <citation><biblioref linkend="XMPP-TDG"/></citation>, connects to
      an XMPP server, logs in, and responds to all incoming messages with
      the exact same message. We build up our application using Swiften's
      basic building blocks for XMPP development, to help get a good
      understanding of how Swiften fundamental classes work and can be
      extended. In the last stage of this example, we 
      introduce some of Swiften's convenience classes for standard
      XMPP tasks such as roster management.
    </para>

    <sect1>
      <title>Connecting to a server: Clients &amp; event loops</title>
      <para>
        As a first step, we create an application that connects to a server.
        The code can be seen in <xref linkend="Example-EchoBot1"/>.
      </para>
      <example id="Example-EchoBot1">
        <title>Connecting to a server</title>
        <include xmlns="http://www.w3.org/2001/XInclude" href="Examples/EchoBot/EchoBot1.cpp.xml" xpointer="xpointer(//programlisting|//calloutlist)"/>
      </example>
      <para>
        The first thing this program does is construct an 
        <emphasis>Event Loop</emphasis>. An event loop is a seemingly infinite
        loop that waits for external events (e.g. incoming network packets,
        timers being activated, input happening) to happen; when such an event 
        comes in, it notifies interested parties of this event, and then 
        continues listening for the next event. Since many application 
        frameworks (such as Qt, GLib, Cocoa) use their own event loop,
        Swiften comes prepackaged with classes that integrate with these
        event loops. These classes can be found in 
        <literal>Swiften/EventLoop</literal>. 
        In this example, however, we don't use such a framework,
        so we use Swiften's own <literal>SimpleEventLoop</literal>. This
        class is used by simply instantiating it at the beginning of the
        application, and calling <literal>run()</literal> after everything
        is set up, which will go into an infinite loop. Apart from constructing,
        passing, and (if necessary) starting the event loop, you will probably 
        have no other contact with it in the rest of the application.
      </para>

      <para>
        Another prerequisite of Swiften's classes is an implementation of 
        network interaction, provided through the
        <literal>NetworkFactories</literal> class. Swiften comes with a
        Boost-based network implementation, implemented in
        <literal>BoostNetworkFactories</literal>. As with the event loop,
        you probably will have no need to interact with this class apart
        from constructing it.
      </para>

      <para>
        Swiften's central class for implementing XMPP applications is
        <literal>Client</literal>. This class handles all the interaction
        with the XMPP network. After constructing it with the JID and
        password with which we want to connect, we call 
        <literal>connect()</literal> to instruct the client to connect to 
        the XMPP server with the given credentials. Note that this call returns
        immediately; it is only when starting the event loop that network
        the actual connection process will start.
      </para>
    </sect1>

    <sect1>
      <title>Building EchoBot</title>
      <para>
        To build your application, you will need to set up your build 
        environment to use the correct include and library paths for
        Swiften, and link against the Swiften library. This depends
        on both the compiler you are using, and the flags you used to build 
        Swiften. To get the list of compiler options, Swiften comes with
        a program <literal>swiften-config</literal> (located in 
        <literal>Swiften/Config</literal> in the Swiften tree). Calling this
        with the <literal>--libs</literal> option will return the list of link flags, 
        whereas calling it with the <literal>--cflags</literal> option will return the
        list of C(++) compiler flags.
      </para>
    </sect1>

    <sect1>
      <title>Reacting to events: Signals, Slots &amp; Bind</title>
      <para>
        Up to this point, our client doesn't do anything useful. In this
        section, we make the client react to XMPP events. The code can
        be seen in <xref linkend="Example-EchoBot2"/>.
      </para>
      <example id="Example-EchoBot2">
        <title>Reacting to events: Notify whenever the client is connected to the network, and echo back incoming messages</title>
        <include xmlns="http://www.w3.org/2001/XInclude" href="Examples/EchoBot/EchoBot2.cpp.xml" xpointer="xpointer(//programlisting|//calloutlist)"/>
      </example>

      <para>
        A first thing we want to do is print out a message when the client
        is connected to the server. Swiften uses the 
        <emphasis>signal/slot</emphasis> paradigm for notifying interested
        parties of events. A <emphasis>signal</emphasis> is an object 
        representing a type of event. For example, <literal>Client</literal>
        has an <literal>onConnected</literal> signal for notifying whenever
        the client is connected to the network. If you are interested in 
        a particular signal, you connect a <emphasis>slot</emphasis> to the 
        signal. A slot represents a callback that will be called whenever a 
        signal is emitted. Since we want to print out a message whenever
        we're connected to the network, we connect to the client's signal,
        and tell it to call <literal>handleConnected</literal> (which prints
        out a message):
        <programlisting>client->onConnected.connect(&amp;handleConnected)</programlisting>
      </para>

      <para>
        Another event we're interested in is whenever a message comes in. 
        For this purpose, <literal>Client</literal> provides a signal called
        <literal>onMessageReceived</literal>. The major difference with the
        previous <literal>onConnected</literal> signal is that this signal
        also can provide extra information to the callback: the actual
        message received. A signal can provide this extra information through
        one or more arguments, which will be passed to the slot's parameters.
        To be able to handle parameters to slots, there needs to be a more 
        general representation of callbacks than just function pointers. This
        is where Boost's <literal>bind</literal> comes in: <literal>bind</literal> 
        provides a way to construct <emphasis>functors</emphasis> (callbacks, slots, &hellip;), by combining function pointers and parameter values. 
        For example, to connect the signal to our slot, we call:
        <programlisting>client->onMessageReceived.connect(bind(&amp;handleMessageReceived, _1))</programlisting>
        This is essentially saying: when the 
        <literal>onMessageReceived</literal> signal is emitted, call
        <literal>handleMessageReceived</literal>, and pass it the first
        parameter provided by the slot (which, in this case, is the actual
        message received).
      </para>
      
      <para>
        The implementation of <literal>handleMessageReceived</literal> should be straightforward: put the <emphasis>To</emphasis> address in place of the <emphasis>From</emphasis> address, and send the message to the server. One
        thing to note is that <literal>Message::ref</literal> represents a
        <emphasis>shared pointer</emphasis> to a <literal>Message</literal>
        stanza. Shared pointers behave the same as regular pointers, except that,
        when the last copy of the pointer goes away, the object it points to is
        deleted as well. <literal>Message::ref</literal> is in fact a
        typedef for <literal>boost::shared_ptr&lt;Message&gt;</literal>. 
        Although Swiften tends to prefer the use of the <literal>::ref</literal>
        notation, you will see both forms used intermixed.
      </para>

      <para>
        Before moving on to the next step, we are going to rearrange our
        code a bit, to make it a bit cleaner. Instead of using global 
        variables, we are going to create an <literal>EchoBot</literal>
        class with the current code in it. The resulting code can be found
        in <xref linkend="Example-EchoBot3"/>.
      </para>
      <example id="Example-EchoBot3">
        <title>Creating an EchoBot class</title>
        <include xmlns="http://www.w3.org/2001/XInclude" href="Examples/EchoBot/EchoBot3.cpp.xml" xpointer="xpointer(//programlisting|//calloutlist)"/>
      </example>
      <para>
        The important thing to consider in this step are the changes to the signal
        connections. Since we are now passing member variables of a class
        to the signal, we need to use <literal>bind</literal> to pass
        in the actual object on which this member variable is called as
        the first parameter.
      </para>
      <para>
        The only thing we added to this version is the 
        <literal>ClientXMLTracer</literal>. This class will dump all
        incoming and outgoing XMPP messages to the console, which can be handy
        for debugging our bot.
      </para>
    </sect1>

    <sect1>
      <title>Presence Management: Requests</title>
      <para>
        The current version of our EchoBot does what it is supposed to do:
        it answers all incoming messages. However, although users can add
        the bot to their contact list, they will not see when it is 
        online, since the bot doesn't do any presence handling yet. In 
        this section, we explain the different steps involved in adding
        presence management, resulting in the code in 
        <xref linkend="Example-EchoBot4"/>.
      </para>
      <example id="Example-EchoBot4">
        <title>Adding presence management: Requesting the initial roster, and auto-approving incoming subscription requests.</title>
        <include xmlns="http://www.w3.org/2001/XInclude" href="Examples/EchoBot/EchoBot4.cpp.xml" xpointer="xpointer(//programlisting|//calloutlist)"/>
      </example>
      <para>
        First of all, our bot needs to listen to incoming subscription requests
        from users who want to add it to their roster, and automatically 
        approve them. This is done by connecting to 
        the <literal>onPresenceReceived</literal> signal, checking whether
        the incoming presence is a subscription request, and if so, 
        respond to it with an approval (in <literal>handlePresenceReceived</literal>).
      </para>

      <para>
        The first version of the XMPP protocol states that a client will not 
        get any presence
        subscriptions until it requests the roster. To make sure we handle
        this, we want to make sure our bot requests the roster at login.
        After getting the <literal>onConnected</literal> signal, we 
        therefore send a 
        <emphasis>request</emphasis> to retrieve the roster. Swiften's
        <literal>Request</literal> classes correspond to XMPP IQ Get or
        Set actions. Swiften provides a set of built-in request classes for
        the most common tasks in <literal>Swiften/Queries/Requests</literal>,
        and can be easily extended to use add your own (see <xref linkend="Section-Extending"/>). Requests have an <literal>onResponse</literal> signal,
        which is emitted when a response comes in. This signal has 2 parameters:
        the actual response data (the <emphasis>Payload</emphasis>), and an
        optional error payload in case there was an error executing the
        request. To use a <literal>Request</literal> class, you construct
        it with the correct parameters, connect to the 
        <literal>onResponse</literal> signal, and then send the request by
        calling <literal>send()</literal> on it. In this case, we're not
        interested in the actual payload of the response (passed as the
        first parameter), so we pass it a slot with only the second parameter
        (the error payload). When we get the roster back, we send initial
        presence to all our subscribers, announcing them we're online.
      </para>
    </sect1>

    <sect1>
      <title>Publishing version information: Responders</title>
      <para>
        Most XMPP clients have support for querying software version information
        of a client through 
        <citation><biblioref linkend="XEP-0092"/></citation>. These clients
        send out an IQ-Get request to an entity, which responds with the
        requested information. We would like our bot to listen to these 
        requests, and respond with the correct information. Swiften uses
        <literal>Responder</literal> classes for the purpose of responding
        to IQ requests, and are therefore the dual of the 
        <literal>Request</literal> clients discussed in the previous section.
      </para>

      <example id="Example-EchoBot5">
        <title>Adding presence management: Requesting the initial roster, and auto-approving incoming subscription requests.</title>
        <include xmlns="http://www.w3.org/2001/XInclude" href="Examples/EchoBot/EchoBot5.cpp.xml" xpointer="xpointer(//programlisting|//calloutlist)"/>
      </example>

      <para>
        Using <literal>SoftwareVersionResponder</literal> is pretty 
        straightforward, as can be seen in <xref linkend="Example-EchoBot5"/>: 
        simply construct the responder, set the correct
        parameters, call <literal>start()</literal>, and it will automatically respond to 
        the incoming 
        requests. Other <literal>Responder</literal> classes may provide
        signals to notify of incoming requests, or may have some other
        behavior. For a detailed explanation of responders, see 
        <xref linkend="Section-CustomQueries"/>.
      </para>
    </sect1>

    <sect1 id="Section-Extending">
      <title>Extending Swiften with new payloads: Payloads, Parsers, and Serializers</title>
      <para>
        Swiften uses abstract datastructures for all the data that is received
        and sent over the XMPP network. The declaration of these datastructures
        can all be found in <literal>Swiften/Elements</literal>. For 
        representing the XMPP stanzas, Swiften uses the 
        <literal>Message</literal>, <literal>Presence</literal>, and 
        <literal>IQ</literal> classes. Each stanza can have an arbitrary
        amount of child <emphasis>payloads</emphasis>, represented by the
        <literal>Payload</literal> class. A payload typically corresponds
        to a (namespaced) child XML element of a stanza; for example, the
        <literal>&lt;query xmlns="jabber:iq:roster"/&gt;</literal> 
        element used for managing the roster is represented as a
        <literal>RosterPayload</literal>.
      </para>

      <para>
        If you want to extend Swiften with your own XMPP extension, you will first
        need to create a payload for this extension. For example, suppose we want to
        reate an extension for use in our Echo bot that contains a special textual
        message, and add this to all our outgoing messages,
        we create the <literal>EchoPayload</literal> illustrated in
        <xref linkend="Example-EchoPayload"/>. We can then append or retrieve this
        payload from the stanzas using <literal>Stanza::getPayload()</literal> and 
        <literal>Stanza::addPayload()</literal>. For example, the version of our
        bot in <xref linkend="Example-EchoBot6"/> checks whether an incoming 
        message contains the <literal>EchoPayload</literal>, and if not, 
        echoes back the message, and adds an extension to the message with a 
        descriptive text.
      </para>
      
      <example id="Example-EchoPayload">
        <title>Extending Swiften with a new payload: <literal>EchoPayload</literal></title>
        <include xmlns="http://www.w3.org/2001/XInclude" href="Examples/EchoBot/EchoPayload.h.xml" xpointer="xpointer(//programlisting|//calloutlist)"/>
      </example>
      
      <example id="Example-EchoBot6">
        <title>Adding a custom extension: Using a custom element, and registering a parser (factory) and serializer for the element.</title>
        <include xmlns="http://www.w3.org/2001/XInclude" href="Examples/EchoBot/EchoBot6.cpp.xml" xpointer="xpointer(//programlisting|//calloutlist)"/>
      </example>

      <para>
        However, having the element is not enough; Swiften also needs to know how to
        extract this payload from the incoming stanzas, and know how to send it on
        outgoing stanzas. In order to do this, Swiften uses XML parsers and serializers
        for the payload. We therefore need to create a parser and serializer for our
        new payload, and register it with <literal>Client</literal>. Serializers are
        implemented as subclasses from <literal>PayloadSerializer</literal>, and provide
        the basic methods <literal>canSerialize()</literal> and 
        <literal>serialize()</literal>.  The serializer
        is registered using <literal>Client::addPayloadSerializer()</literal> 
        (and unregistered using <literal>Client::removePayloadSerializer()</literal>).
        Parsers consist of 2 parts: a subclass of <literal>PayloadParser</literal>, which
        parses incoming XML in an event-driven way and builds up the payload, and
        a subclass of <literal>PayloadParserFactory</literal>, which is responsible
        for detecting whether a given parser can parse an incoming element, and 
        creates a parser. The parser factory is registered with the client using
        <literal>Client::addPayloadParserFactory()</literal> (and unregistered
        using <literal>Client::removePayloadParserFactory()</literal>).
      </para>

      <para>
        Although you can subclass the base classes for parsers and serializers
        directly, Swiften comes with utility classes that contain common
        functionality for parsers and serializers. For example, for our EchoBot,
        the parser and serializer using these utility classes is shown in 
        <xref linkend="Example-EchoPayloadParser"/> and 
        <xref linkend="Example-EchoPayloadSerializer"/> respectively. Registration
        of the parser and serializer is shown in the constructor of our EchoBot in
        <xref linkend="Example-EchoBot6"/>. 
      </para>
      
      <example id="Example-EchoPayloadParser">
        <title>The parser and parser factory for <literal>EchoPayload</literal></title>
        <include xmlns="http://www.w3.org/2001/XInclude" href="Examples/EchoBot/EchoPayloadParserFactory.h.xml" xpointer="xpointer(//programlisting|//calloutlist)"/>
      </example>
            
      <example id="Example-EchoPayloadSerializer">
        <title>The serializer for <literal>EchoPayload</literal></title>
        <include xmlns="http://www.w3.org/2001/XInclude" href="Examples/EchoBot/EchoPayloadSerializer.h.xml" xpointer="xpointer(//programlisting|//calloutlist)"/>
      </example>

      <para>
        If you want to create your own parser and serializers, you can look at the
        built-in parsers and serializers in the Swiften library, located in
        <literal>Swiften/Parser/PayloadParsers</literal> and <literal>Swiften/Serializer/PayloadSerializers</literal>.
      </para>
      
    </sect1>

    <sect1 id="Section-CustomQueries">
      <title>Extending Swiften with new queries and responders</title>
      <remark>TODO</remark>
    </sect1>

    <sect1>
      <title>Using Swiften's convenience classes</title>
      <para>
      	Swiften comes with flavors of the Client class: <literal>CoreClient</literal>, which 
      	implements only the basics of connecting to the XMPP server, without any built-in 
      	responders. If you want to build a client from scratch, this class is probably your
      	preferred Swiften interface. However, most users of Swiften will not want to bother with
      	explicitly instantiating responders to basic functionality such as software version
      	information etc., and will want to have the convenience of built-in responders and 
      	utility classes. In this case, you can use the <literal>Client</literal> class (a
      	subclass of <literal>CoreClient</literal>, which
      	implements most of the common XMPP client functionality, including roster and subscription
      	management, VCards, Avatars, Service Discovery, Multi-User Chats, ...
      </para>
    </sect1>
  </chapter>

  <bibliography>
    <title>Bibliography</title>

    <biblioentry id="XMPP-TDG">
      <abbrev>XMPP-TDG</abbrev>
      <title><ulink url="http://oreilly.com/catalog/9780596157197/">XMPP: The
      Definitive Guide</ulink></title>
      <author>
        <firstname>Peter</firstname>
        <surname>Saint-Andre</surname>
      </author>
      <author>
        <firstname>Kevin</firstname>
        <surname>Smith</surname>
      </author>
      <author>
        <firstname>Remko</firstname>
        <surname>Tronçon</surname>
      </author>
    </biblioentry>

    <biblioentry id='XEP-0092'>
      <abbrev>XEP-0092</abbrev>
      <title><ulink url='http://www.xmpp.org/extensions/xep-0092.html'>Software Version</ulink></title>
      <author>
        <firstname>Peter</firstname>
        <surname>Saint-Andre</surname>
      </author>
    </biblioentry>
  </bibliography>
</book>