summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRemko Tronçon <git@el-tramo.be>2010-10-09 09:52:51 (GMT)
committerRemko Tronçon <git@el-tramo.be>2010-10-09 14:20:47 (GMT)
commitc5bb67eab6f97ae0f5f7e673ff0ba9b1111191f4 (patch)
treead17bd2ee34263af472340a839a4757859ddaea1 /Documentation/SwiftenDevelopersGuide
parent0a84186a22fd48485deed77bc067877ac332d0b4 (diff)
downloadswift-contrib-c5bb67eab6f97ae0f5f7e673ff0ba9b1111191f4.zip
swift-contrib-c5bb67eab6f97ae0f5f7e673ff0ba9b1111191f4.tar.bz2
Added EchoBot walkthrough example.
Diffstat (limited to 'Documentation/SwiftenDevelopersGuide')
-rw-r--r--Documentation/SwiftenDevelopersGuide/Examples/EchoBot/.gitignore2
-rw-r--r--Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot1.cpp20
-rw-r--r--Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot2.cpp40
-rw-r--r--Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot3.cpp53
-rw-r--r--Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot4.cpp89
-rw-r--r--Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot5.cpp95
-rw-r--r--Documentation/SwiftenDevelopersGuide/Examples/EchoBot/SConscript15
-rw-r--r--Documentation/SwiftenDevelopersGuide/Examples/SConscript1
-rw-r--r--Documentation/SwiftenDevelopersGuide/SConscript85
-rw-r--r--Documentation/SwiftenDevelopersGuide/Swiften Developers Guide.xml306
10 files changed, 692 insertions, 14 deletions
diff --git a/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/.gitignore b/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/.gitignore
new file mode 100644
index 0000000..28275ad
--- /dev/null
+++ b/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/.gitignore
@@ -0,0 +1,2 @@
+EchoBot?
+*.cpp.xml
diff --git a/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot1.cpp b/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot1.cpp
new file mode 100644
index 0000000..584ec9d
--- /dev/null
+++ b/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot1.cpp
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include "Swiften/Swiften.h"
+
+using namespace Swift;
+
+int main(int, char*) {
+ SimpleEventLoop eventLoop;
+
+ Client client(JID("echobot@wonderland.lit"), "mypass");
+ client.connect();
+
+ eventLoop.run();
+
+ return 0;
+}
diff --git a/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot2.cpp b/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot2.cpp
new file mode 100644
index 0000000..a03c3db
--- /dev/null
+++ b/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot2.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <iostream>
+#include <boost/bind.hpp>
+
+#include "Swiften/Swiften.h"
+
+using namespace Swift;
+using namespace boost;
+
+Client* client;
+
+void handleConnected() {
+ std::cout << "Connected" << std::endl;
+}
+
+void handleMessageReceived(Message::ref message) {
+ // Echo back the incoming message
+ message->setTo(message->getFrom());
+ message->setFrom(JID());
+ client->sendMessage(message);
+}
+
+int main(int, char*) {
+ SimpleEventLoop eventLoop;
+
+ client = new Client(JID("echobot@wonderland.lit"), "mypass");
+ client->onConnected.connect(&handleConnected);
+ client->onMessageReceived.connect(bind(&handleMessageReceived, _1));
+ client->connect();
+
+ eventLoop.run();
+
+ delete client;
+ return 0;
+}
diff --git a/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot3.cpp b/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot3.cpp
new file mode 100644
index 0000000..a9c611f
--- /dev/null
+++ b/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot3.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <iostream>
+#include <boost/bind.hpp>
+
+#include "Swiften/Swiften.h"
+
+using namespace Swift;
+using namespace boost;
+
+class EchoBot {
+ public:
+ EchoBot() {
+ client = new Client(JID("echobot@wonderland.lit"), "mypass");
+ client->onConnected.connect(bind(&EchoBot::handleConnected, this));
+ client->onMessageReceived.connect(
+ bind(&EchoBot::handleMessageReceived, this, _1));
+ tracer = new ClientXMLTracer(client);
+ client->connect();
+ }
+
+ ~EchoBot() {
+ delete tracer;
+ delete client;
+ }
+
+ private:
+ void handleConnected() {
+ std::cout << "Connected" << std::endl;
+ }
+
+ void handleMessageReceived(Message::ref message) {
+ // Echo back the incoming message
+ message->setTo(message->getFrom());
+ message->setFrom(JID());
+ client->sendMessage(message);
+ }
+
+ private:
+ Client* client;
+ ClientXMLTracer* tracer;
+};
+
+int main(int, char*) {
+ SimpleEventLoop eventLoop;
+ EchoBot bot;
+ eventLoop.run();
+ return 0;
+}
diff --git a/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot4.cpp b/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot4.cpp
new file mode 100644
index 0000000..b01fecd
--- /dev/null
+++ b/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot4.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+//...
+#include <iostream>
+#include <boost/bind.hpp>
+
+#include "Swiften/Swiften.h"
+
+using namespace Swift;
+using namespace boost;
+//...
+class EchoBot {
+ public:
+ EchoBot() {
+ //...
+ client = new Client(JID("echobot@wonderland.lit"), "mypass");
+ client->onConnected.connect(bind(&EchoBot::handleConnected, this));
+ client->onMessageReceived.connect(
+ bind(&EchoBot::handleMessageReceived, this, _1));
+ //...
+ client->onPresenceReceived.connect(
+ bind(&EchoBot::handlePresenceReceived, this, _1));
+ //...
+ tracer = new ClientXMLTracer(client);
+ client->connect();
+ //...
+ }
+
+ //...
+ ~EchoBot() {
+ delete tracer;
+ delete client;
+ }
+
+ private:
+ //...
+ void handlePresenceReceived(Presence::ref presence) {
+ // Automatically approve subscription requests
+ if (presence->getType() == Presence::Subscribe) {
+ Presence::ref response = Presence::create();
+ response->setTo(presence->getFrom());
+ response->setType(Presence::Subscribed);
+ client->sendPresence(response);
+ }
+ }
+
+ void handleConnected() {
+ // Request the roster
+ GetRosterRequest::ref rosterRequest =
+ GetRosterRequest::create(client->getIQRouter());
+ rosterRequest->onResponse.connect(
+ bind(&EchoBot::handleRosterReceived, this, _2));
+ rosterRequest->send();
+ }
+
+ void handleRosterReceived(const optional<ErrorPayload>& error) {
+ if (error) {
+ std::cerr << "Error receiving roster. Continuing anyway.";
+ }
+ // Send initial available presence
+ client->sendPresence(Presence::create("Send me a message"));
+ }
+ //...
+
+ void handleMessageReceived(Message::ref message) {
+ // Echo back the incoming message
+ message->setTo(message->getFrom());
+ message->setFrom(JID());
+ client->sendMessage(message);
+ }
+
+ private:
+ Client* client;
+ ClientXMLTracer* tracer;
+ //...
+};
+//...
+
+int main(int, char*) {
+ SimpleEventLoop eventLoop;
+ EchoBot bot;
+ eventLoop.run();
+ return 0;
+}
+//...
diff --git a/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot5.cpp b/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot5.cpp
new file mode 100644
index 0000000..72d0eb1
--- /dev/null
+++ b/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot5.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+//...
+#include <iostream>
+#include <boost/bind.hpp>
+
+#include "Swiften/Swiften.h"
+
+using namespace Swift;
+using namespace boost;
+//...
+class EchoBot {
+ public:
+ EchoBot() {
+ //...
+ client = new Client(JID("echobot@wonderland.lit"), "mypass");
+ client->onConnected.connect(bind(&EchoBot::handleConnected, this));
+ client->onMessageReceived.connect(
+ bind(&EchoBot::handleMessageReceived, this, _1));
+ client->onPresenceReceived.connect(
+ bind(&EchoBot::handlePresenceReceived, this, _1));
+ tracer = new ClientXMLTracer(client);
+ //...
+ softwareVersionResponder = new SoftwareVersionResponder(
+ "EchoBot", "1.0", client->getIQRouter());
+ //...
+ client->connect();
+ //...
+ }
+
+ ~EchoBot() {
+ delete softwareVersionResponder;
+ //...
+ delete tracer;
+ delete client;
+ //...
+ }
+ //...
+
+ private:
+ void handlePresenceReceived(Presence::ref presence) {
+ // Automatically approve subscription requests
+ if (presence->getType() == Presence::Subscribe) {
+ Presence::ref response = Presence::create();
+ response->setTo(presence->getFrom());
+ response->setType(Presence::Subscribed);
+ client->sendPresence(response);
+ }
+ }
+
+ void handleConnected() {
+ // Request the roster
+ GetRosterRequest::ref rosterRequest =
+ GetRosterRequest::create(client->getIQRouter());
+ rosterRequest->onResponse.connect(
+ bind(&EchoBot::handleRosterReceived, this, _2));
+ rosterRequest->send();
+ }
+
+ void handleRosterReceived(const optional<ErrorPayload>& error) {
+ if (error) {
+ std::cerr << "Error receiving roster. Continuing anyway.";
+ }
+ // Send initial available presence
+ client->sendPresence(Presence::create("Send me a message"));
+ }
+
+ void handleMessageReceived(Message::ref message) {
+ // Echo back the incoming message
+ message->setTo(message->getFrom());
+ message->setFrom(JID());
+ client->sendMessage(message);
+ }
+
+ //...
+ private:
+ //...
+ Client* client;
+ ClientXMLTracer* tracer;
+ //...
+ SoftwareVersionResponder* softwareVersionResponder;
+};
+//...
+
+int main(int, char*) {
+ SimpleEventLoop eventLoop;
+ EchoBot bot;
+ eventLoop.run();
+ return 0;
+}
+//...
diff --git a/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/SConscript b/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/SConscript
new file mode 100644
index 0000000..ceead6b
--- /dev/null
+++ b/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/SConscript
@@ -0,0 +1,15 @@
+Import("env")
+
+example_env = env.Clone()
+example_env.MergeFlags(example_env["SWIFTEN_FLAGS"])
+example_env.MergeFlags(example_env["LIBIDN_FLAGS"])
+example_env.MergeFlags(example_env["BOOST_FLAGS"])
+example_env.MergeFlags(example_env.get("SQLITE_FLAGS", {}))
+example_env.MergeFlags(example_env["ZLIB_FLAGS"])
+example_env.MergeFlags(example_env["OPENSSL_FLAGS"])
+example_env.MergeFlags(example_env.get("LIBXML_FLAGS", ""))
+example_env.MergeFlags(example_env.get("EXPAT_FLAGS", ""))
+example_env.MergeFlags(example_env["PLATFORM_FLAGS"])
+
+for i in range(1,6) :
+ example_env.Program("EchoBot" + str(i), ["EchoBot" + str(i) + ".cpp"])
diff --git a/Documentation/SwiftenDevelopersGuide/Examples/SConscript b/Documentation/SwiftenDevelopersGuide/Examples/SConscript
new file mode 100644
index 0000000..1284b34
--- /dev/null
+++ b/Documentation/SwiftenDevelopersGuide/Examples/SConscript
@@ -0,0 +1 @@
+SConscript(dirs = ["EchoBot"])
diff --git a/Documentation/SwiftenDevelopersGuide/SConscript b/Documentation/SwiftenDevelopersGuide/SConscript
index 145663f..fb08a8f 100644
--- a/Documentation/SwiftenDevelopersGuide/SConscript
+++ b/Documentation/SwiftenDevelopersGuide/SConscript
@@ -2,4 +2,89 @@ Import("env")
env.Tool("DocBook", toolpath = ["#/BuildTools/DocBook/SCons"])
+################################################################################
+# Code generation helper
+################################################################################
+
+import sys, re, os.path
+
+def generateDocBookCode(env, target, source) :
+ # Strips empty lines from the beginning & end of a program
+ def stripEmptyLines(program) :
+ programLines = program.split('\n')
+ newProgramLines = []
+ inProgram = False
+ for line in programLines :
+ if not re.match("^\s*$", line) or inProgram :
+ inProgram = True
+ newProgramLines.append(line)
+ return '\n'.join(newProgramLines).rstrip()
+
+ def createCallouts(program, calloutPrefix) :
+ newProgramLines = []
+ calloutLines = []
+ nextID = 0
+ for line in program.split("\n") :
+ # FIXME: Takes the largest match
+ m = re.match(".*\/* \(\*\) (.*) \*/.*", line)
+ if m :
+ cobID = "cob-" + calloutPrefix + "-" + str(nextID)
+ coID = "co-" + calloutPrefix + "-" + str(nextID)
+ nextID += 1
+ line = re.sub("/\*.*\*/", "]]><co id=\"%(cobID)s\" linkends=\"%(coID)s\"/><![CDATA[" % {"cobID" : cobID, "coID" : coID}, line)
+ calloutLines.append("<callout arearefs=\"%(cobID)s\" id=\"%(coID)s\"><para>%(text)s</para></callout>" % {"cobID": cobID, "coID": coID, "text": m.group(1)})
+ newProgramLines.append(line)
+ callouts = "<calloutlist>" + "\n".join(calloutLines) + "</calloutlist>" if len(calloutLines) > 0 else ""
+ return ("\n".join(newProgramLines), callouts)
+
+ # Parse program
+ filename = source[0].abspath
+ filenameBase = os.path.basename(filename).replace(".cpp", "")
+ inputfile = open(filename)
+ program = ""
+ programs = {}
+ programName = ""
+ inEllipsis = False
+ for line in inputfile.readlines() :
+ if inEllipsis :
+ if "//..." in line :
+ inEllipsis = False
+ else :
+ if line.startswith("/*") or line.startswith(" *") :
+ continue
+ if "//..." in line :
+ inEllipsis = True
+ line = line.replace("//...", "]]>&#x2026;<![CDATA[")
+ else :
+ m = re.match("^/// (.*)", line)
+ if m :
+ if programName :
+ programs[programName] = program
+ program = ""
+ programName = m.group(1).strip().replace(" ", "")
+ continue
+ line = re.sub("\t", " ", line)
+ program += line
+ programs[programName] = program
+ inputfile.close()
+
+ for programName, program in programs.items() :
+ program = stripEmptyLines(program)
+ (program, callouts) = createCallouts(program, filenameBase + "-" + programName)
+
+ document = "<foo><programlisting><![CDATA[" + program + "]]></programlisting>" + callouts + "</foo>"
+
+ # Generate code
+ output = open(target[0].abspath, 'w')
+ output.write(document)
+ output.close()
+
+################################################################################
+
env.DocBook("Swiften Developers Guide.xml")
+
+for i in range(1, 6) :
+ source = "Examples/EchoBot/EchoBot" + str(i) + ".cpp"
+ env.Command(source + ".xml", source, Action(generateDocBookCode, cmdstr = "$GENCOMSTR"))
+
+SConscript(dirs = ["Examples"])
diff --git a/Documentation/SwiftenDevelopersGuide/Swiften Developers Guide.xml b/Documentation/SwiftenDevelopersGuide/Swiften Developers Guide.xml
index e0a63ae..1df3781 100644
--- a/Documentation/SwiftenDevelopersGuide/Swiften Developers Guide.xml
+++ b/Documentation/SwiftenDevelopersGuide/Swiften Developers Guide.xml
@@ -6,44 +6,322 @@
<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>
- <para>
- This is an introduction
- </para>
-
- <sect1>
- <title>Section</title>
-
+ <section>
+ <title>Boost</title>
<para>
- This is a section
+ 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 Boost
+ website.
</para>
- </sect1>
+ </section>
</chapter>
<chapter>
<title>Tutorial: Writing an Echo Bot</title>
<para>
- In this chapter, we build a simple echo bot, illustrating how
- to use and extend Swiften for your own purposes.
+ 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>Setting up the build environment</title>
+ <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
+ and (if necessary) starting the event loop, you will probably have
+ no other contact with it in the rest of the application.
+ </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>
+ <remark>TODO: Explain linking against the static library.</remark>
+ </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>Connecting to the network</title>
+ <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>T</title>
+ <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 with the correct
+ parameters, 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>
+
+ <remark>TODO</remark>
+ </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>
+ <remark>TODO</remark>
</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>