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
parent0a84186a22fd48485deed77bc067877ac332d0b4 (diff)
downloadswift-c5bb67eab6f97ae0f5f7e673ff0ba9b1111191f4.zip
swift-c5bb67eab6f97ae0f5f7e673ff0ba9b1111191f4.tar.bz2
Added EchoBot walkthrough example.
-rwxr-xr-xBuildTools/Copyrighter.py2
-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
-rw-r--r--Swift/Controllers/Chat/ChatControllerBase.cpp2
-rw-r--r--Swift/Controllers/Chat/MUCSearchController.cpp4
-rw-r--r--Swift/Controllers/MainController.cpp2
-rw-r--r--Swift/Controllers/RosterController.cpp8
-rw-r--r--Swiften/.gitignore1
-rw-r--r--Swiften/Client/Client.h1
-rw-r--r--Swiften/Client/ClientXMLTracer.h2
-rw-r--r--Swiften/Disco/CapsManager.cpp2
-rw-r--r--Swiften/Elements/Presence.h12
-rw-r--r--Swiften/Elements/RosterPayload.h8
-rw-r--r--Swiften/Examples/ConnectivityTest/ConnectivityTest.cpp2
-rw-r--r--Swiften/Examples/EchoBot/.gitignore1
-rw-r--r--Swiften/Examples/EchoBot/EchoBot.cpp63
-rw-r--r--Swiften/Examples/EchoBot/SConscript14
-rw-r--r--Swiften/Examples/SConscript1
-rw-r--r--Swiften/MUC/MUCBookmarkManager.cpp4
-rw-r--r--Swiften/Queries/Request.h6
-rw-r--r--Swiften/Queries/Requests/GetDiscoInfoRequest.h11
-rw-r--r--Swiften/Queries/Requests/GetDiscoItemsRequest.h7
-rw-r--r--Swiften/Queries/Requests/GetPrivateStorageRequest.h7
-rw-r--r--Swiften/Queries/Requests/GetRosterRequest.h8
-rw-r--r--Swiften/Queries/Requests/GetSecurityLabelsCatalogRequest.h8
-rw-r--r--Swiften/Queries/Requests/GetVCardRequest.h5
-rw-r--r--Swiften/Queries/Requests/SetPrivateStorageRequest.h7
-rw-r--r--Swiften/Queries/Requests/SetRosterRequest.h8
-rw-r--r--Swiften/Queries/Requests/UnitTest/GetPrivateStorageRequestTest.cpp16
-rw-r--r--Swiften/Roster/XMPPRosterController.cpp2
-rw-r--r--Swiften/SConscript18
-rw-r--r--Swiften/VCards/VCardManager.cpp2
40 files changed, 811 insertions, 129 deletions
diff --git a/BuildTools/Copyrighter.py b/BuildTools/Copyrighter.py
index 7f4d9f9..b83c3d6 100755
--- a/BuildTools/Copyrighter.py
+++ b/BuildTools/Copyrighter.py
@@ -136,7 +136,7 @@ elif sys.argv[1] == "check-all-copyrights" :
for (path, dirs, files) in os.walk(".") :
if "3rdParty" in path or ".sconf" in path or "Swift.app" in path :
continue
- for filename in [os.path.join(path, file) for file in files if (file.endswith(".cpp") or file.endswith(".h")) and not "ui_" in file and not "moc_" in file and not "qrc_" in file and not "BuildVersion.h" in file] :
+ for filename in [os.path.join(path, file) for file in files if (file.endswith(".cpp") or file.endswith(".h")) and not "ui_" in file and not "moc_" in file and not "qrc_" in file and not "BuildVersion.h" in file and not "Swiften.h" in file ] :
ok &= check_copyright(filename)
if not ok :
sys.exit(-1)
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>
diff --git a/Swift/Controllers/Chat/ChatControllerBase.cpp b/Swift/Controllers/Chat/ChatControllerBase.cpp
index 9619722..de74150 100644
--- a/Swift/Controllers/Chat/ChatControllerBase.cpp
+++ b/Swift/Controllers/Chat/ChatControllerBase.cpp
@@ -62,7 +62,7 @@ void ChatControllerBase::setAvailableServerFeatures(boost::shared_ptr<DiscoInfo>
if (iqRouter_->isAvailable() && info->hasFeature(DiscoInfo::SecurityLabels)) {
//chatWindow_->setSecurityLabelsEnabled(true);
//chatWindow_->setSecurityLabelsError();
- boost::shared_ptr<GetSecurityLabelsCatalogRequest> request(new GetSecurityLabelsCatalogRequest(JID(toJID_.toBare()), iqRouter_));
+ GetSecurityLabelsCatalogRequest::ref request = GetSecurityLabelsCatalogRequest::create(JID(toJID_.toBare()), iqRouter_);
request->onResponse.connect(boost::bind(&ChatControllerBase::handleSecurityLabelsCatalogResponse, this, _1, _2));
request->send();
//labelsEnabled_ = true;
diff --git a/Swift/Controllers/Chat/MUCSearchController.cpp b/Swift/Controllers/Chat/MUCSearchController.cpp
index 304de85..dc81031 100644
--- a/Swift/Controllers/Chat/MUCSearchController.cpp
+++ b/Swift/Controllers/Chat/MUCSearchController.cpp
@@ -63,7 +63,7 @@ void MUCSearchController::handleAddService(const JID& jid, bool userTriggered) {
//Set Window to say error this isn't valid
return;
}
- boost::shared_ptr<GetDiscoInfoRequest> discoInfoRequest(new GetDiscoInfoRequest(jid, iqRouter_));
+ GetDiscoInfoRequest::ref discoInfoRequest = GetDiscoInfoRequest::create(jid, iqRouter_);
discoInfoRequest->onResponse.connect(boost::bind(&MUCSearchController::handleDiscoInfoResponse, this, _1, _2, jid));
discoInfoRequest->send();
}
@@ -79,7 +79,7 @@ void MUCSearchController::handleDiscoInfoResponse(boost::shared_ptr<DiscoInfo> i
handleDiscoError(jid, error.get());
return;
}
- boost::shared_ptr<GetDiscoItemsRequest> discoItemsRequest(new GetDiscoItemsRequest(jid, iqRouter_));
+ GetDiscoItemsRequest::ref discoItemsRequest = GetDiscoItemsRequest::create(jid, iqRouter_);
bool mucService = false;
bool couldContainServices = false;
String name;
diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp
index 06b231c..ddf9848 100644
--- a/Swift/Controllers/MainController.cpp
+++ b/Swift/Controllers/MainController.cpp
@@ -306,7 +306,7 @@ void MainController::handleConnected() {
xmppRosterController_->requestRoster();
- boost::shared_ptr<GetDiscoInfoRequest> discoInfoRequest(new GetDiscoInfoRequest(JID(), client_->getIQRouter()));
+ GetDiscoInfoRequest::ref discoInfoRequest = GetDiscoInfoRequest::create(JID(), client_->getIQRouter());
discoInfoRequest->onResponse.connect(boost::bind(&MainController::handleServerDiscoInfoResponse, this, _1, _2));
discoInfoRequest->send();
diff --git a/Swift/Controllers/RosterController.cpp b/Swift/Controllers/RosterController.cpp
index 15ed6a4..e79fad1 100644
--- a/Swift/Controllers/RosterController.cpp
+++ b/Swift/Controllers/RosterController.cpp
@@ -162,7 +162,7 @@ void RosterController::handleUIEvent(boost::shared_ptr<UIEvent> event) {
item.setJID(addContactEvent->getJID());
boost::shared_ptr<RosterPayload> roster(new RosterPayload());
roster->addItem(item);
- boost::shared_ptr<SetRosterRequest> request(new SetRosterRequest(roster, iqRouter_));
+ SetRosterRequest::ref request = SetRosterRequest::create(roster, iqRouter_);
request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster));
request->send();
presenceSender_->requestSubscription(addContactEvent->getJID());
@@ -173,7 +173,7 @@ void RosterController::handleUIEvent(boost::shared_ptr<UIEvent> event) {
RosterItemPayload item(removeEvent->getJID(), "", RosterItemPayload::Remove);
boost::shared_ptr<RosterPayload> roster(new RosterPayload());
roster->addItem(item);
- boost::shared_ptr<SetRosterRequest> request(new SetRosterRequest(roster, iqRouter_));
+ SetRosterRequest::ref request = SetRosterRequest::create(roster, iqRouter_);
request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster));
request->send();
@@ -186,7 +186,7 @@ void RosterController::handleUIEvent(boost::shared_ptr<UIEvent> event) {
item.setGroups(xmppRoster_->getGroupsForJID(contact));
boost::shared_ptr<RosterPayload> roster(new RosterPayload());
roster->addItem(item);
- boost::shared_ptr<SetRosterRequest> request(new SetRosterRequest(roster, iqRouter_));
+ SetRosterRequest::ref request = SetRosterRequest::create(roster, iqRouter_);
request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster));
request->send();
return;
@@ -210,7 +210,7 @@ void RosterController::handleUIEvent(boost::shared_ptr<UIEvent> event) {
item.setGroups(newGroups);
boost::shared_ptr<RosterPayload> roster(new RosterPayload());
roster->addItem(item);
- boost::shared_ptr<SetRosterRequest> request(new SetRosterRequest(roster, iqRouter_));
+ SetRosterRequest::ref request = SetRosterRequest::create(roster, iqRouter_);
request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster));
request->send();
return;
diff --git a/Swiften/.gitignore b/Swiften/.gitignore
index 9eca6c8..c21d6aa 100644
--- a/Swiften/.gitignore
+++ b/Swiften/.gitignore
@@ -1,2 +1,3 @@
*.a
*.o
+Swiften.h
diff --git a/Swiften/Client/Client.h b/Swiften/Client/Client.h
index 7e55289..5432920 100644
--- a/Swiften/Client/Client.h
+++ b/Swiften/Client/Client.h
@@ -22,6 +22,7 @@
#include "Swiften/Client/StanzaChannel.h"
#include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h"
#include "Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h"
+#include "Swiften/Base/Shared.h"
namespace Swift {
class IQRouter;
diff --git a/Swiften/Client/ClientXMLTracer.h b/Swiften/Client/ClientXMLTracer.h
index 8bcb6e7..f3cfd08 100644
--- a/Swiften/Client/ClientXMLTracer.h
+++ b/Swiften/Client/ClientXMLTracer.h
@@ -6,6 +6,8 @@
#pragma once
+#include <boost/bind.hpp>
+
#include "Swiften/Client/Client.h"
namespace Swift {
diff --git a/Swiften/Disco/CapsManager.cpp b/Swiften/Disco/CapsManager.cpp
index a5023d3..a9920a2 100644
--- a/Swiften/Disco/CapsManager.cpp
+++ b/Swiften/Disco/CapsManager.cpp
@@ -69,7 +69,7 @@ void CapsManager::handleDiscoInfoReceived(const JID& from, const String& hash, D
}
void CapsManager::requestDiscoInfo(const JID& jid, const String& node, const String& hash) {
- boost::shared_ptr<GetDiscoInfoRequest> request(new GetDiscoInfoRequest(jid, node + "#" + hash, iqRouter));
+ GetDiscoInfoRequest::ref request = GetDiscoInfoRequest::create(jid, node + "#" + hash, iqRouter);
request->onResponse.connect(boost::bind(&CapsManager::handleDiscoInfoReceived, this, jid, hash, _1, _2));
requestedDiscoInfos.insert(hash);
request->send();
diff --git a/Swiften/Elements/Presence.h b/Swiften/Elements/Presence.h
index 7297339..45638b9 100644
--- a/Swiften/Elements/Presence.h
+++ b/Swiften/Elements/Presence.h
@@ -22,6 +22,18 @@ namespace Swift {
setStatus(status);
}
+ static ref create() {
+ return ref(new Presence());
+ }
+
+ static ref create(const String& status) {
+ return ref(new Presence(status));
+ }
+
+ static ref create(Presence::ref presence) {
+ return ref(new Presence(*presence));
+ }
+
Type getType() const { return type_; }
void setType(Type type) { type_ = type; }
diff --git a/Swiften/Elements/RosterPayload.h b/Swiften/Elements/RosterPayload.h
index 58c5726..0c987c4 100644
--- a/Swiften/Elements/RosterPayload.h
+++ b/Swiften/Elements/RosterPayload.h
@@ -4,17 +4,17 @@
* See Documentation/Licenses/GPLv3.txt for more information.
*/
-#ifndef SWIFTEN_RosterPayload_H
-#define SWIFTEN_RosterPayload_H
+#pragma once
#include <vector>
#include <boost/optional.hpp>
#include "Swiften/Elements/RosterItemPayload.h"
#include "Swiften/Elements/Payload.h"
+#include "Swiften/Base/Shared.h"
namespace Swift {
- class RosterPayload : public Payload {
+ class RosterPayload : public Payload, public Shared<RosterPayload> {
public:
typedef std::vector<RosterItemPayload> RosterItemPayloads;
@@ -35,5 +35,3 @@ namespace Swift {
RosterItemPayloads items_;
};
}
-
-#endif
diff --git a/Swiften/Examples/ConnectivityTest/ConnectivityTest.cpp b/Swiften/Examples/ConnectivityTest/ConnectivityTest.cpp
index af08a9c..3d814a8 100644
--- a/Swiften/Examples/ConnectivityTest/ConnectivityTest.cpp
+++ b/Swiften/Examples/ConnectivityTest/ConnectivityTest.cpp
@@ -41,7 +41,7 @@ void handleServerDiscoInfoResponse(boost::shared_ptr<DiscoInfo> /*info*/, const
void handleConnected() {
exitCode = NO_RESPONSE;
- boost::shared_ptr<GetDiscoInfoRequest> discoInfoRequest(new GetDiscoInfoRequest(JID(), client->getIQRouter()));
+ GetDiscoInfoRequest::ref discoInfoRequest = GetDiscoInfoRequest::create(JID(), client->getIQRouter());
discoInfoRequest->onResponse.connect(handleServerDiscoInfoResponse);
discoInfoRequest->send();
}
diff --git a/Swiften/Examples/EchoBot/.gitignore b/Swiften/Examples/EchoBot/.gitignore
deleted file mode 100644
index 9200f42..0000000
--- a/Swiften/Examples/EchoBot/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-EchoBot
diff --git a/Swiften/Examples/EchoBot/EchoBot.cpp b/Swiften/Examples/EchoBot/EchoBot.cpp
deleted file mode 100644
index 0474287..0000000
--- a/Swiften/Examples/EchoBot/EchoBot.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (c) 2010 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
- */
-
-#include <boost/bind.hpp>
-#include <iostream>
-
-#include "Swiften/Client/Client.h"
-#include "Swiften/EventLoop/SimpleEventLoop.h"
-#include "Swiften/Queries/Requests/GetRosterRequest.h"
-
-using namespace Swift;
-using namespace boost;
-
-class EchoBot {
- public:
- EchoBot(const JID& jid, const String& pass) {
- client = new Client(jid, pass);
- client->onConnected.connect(bind(&EchoBot::handleConnected, this));
- client->onMessageReceived.connect(bind(&EchoBot::handleMessageReceived, this, _1));
- client->connect();
- }
-
- ~EchoBot() {
- delete client;
- }
-
- private:
- void handleConnected() {
- shared_ptr<GetRosterRequest> rosterRequest(new GetRosterRequest(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.";
- }
- client->sendPresence(shared_ptr<Presence>(new Presence("Send me a message")));
- }
-
- void handleMessageReceived(shared_ptr<Message> message) {
- message->setTo(message->getFrom());
- message->setFrom(JID());
- client->sendMessage(message);
- }
-
- private:
- Client* client;
-};
-
-int main(int argc, char* argv[]) {
- if (argc != 3) {
- std::cerr << "Usage: " << argv[0] << " <jid> <pass>" << std::endl;
- return -1;
- }
- SimpleEventLoop eventLoop;
- EchoBot bot(JID(argv[1]), argv[2]);
- eventLoop.run();
- return 0;
-}
diff --git a/Swiften/Examples/EchoBot/SConscript b/Swiften/Examples/EchoBot/SConscript
deleted file mode 100644
index fb7749d..0000000
--- a/Swiften/Examples/EchoBot/SConscript
+++ /dev/null
@@ -1,14 +0,0 @@
-Import("env")
-
-myenv = env.Clone()
-myenv.MergeFlags(myenv["SWIFTEN_FLAGS"])
-myenv.MergeFlags(myenv["LIBIDN_FLAGS"])
-myenv.MergeFlags(myenv["BOOST_FLAGS"])
-myenv.MergeFlags(myenv.get("SQLITE_FLAGS", {}))
-myenv.MergeFlags(myenv["ZLIB_FLAGS"])
-myenv.MergeFlags(myenv["OPENSSL_FLAGS"])
-myenv.MergeFlags(myenv.get("LIBXML_FLAGS", ""))
-myenv.MergeFlags(myenv.get("EXPAT_FLAGS", ""))
-myenv.MergeFlags(myenv["PLATFORM_FLAGS"])
-
-myenv.Program("EchoBot", ["EchoBot.cpp"])
diff --git a/Swiften/Examples/SConscript b/Swiften/Examples/SConscript
index 64d1859..61bedfb 100644
--- a/Swiften/Examples/SConscript
+++ b/Swiften/Examples/SConscript
@@ -6,6 +6,5 @@ SConscript(dirs = [
"SendMessage",
"ConnectivityTest",
"LinkLocalTool",
- "EchoBot",
"ParserTester",
])
diff --git a/Swiften/MUC/MUCBookmarkManager.cpp b/Swiften/MUC/MUCBookmarkManager.cpp
index 64615e4..0f5f3bb 100644
--- a/Swiften/MUC/MUCBookmarkManager.cpp
+++ b/Swiften/MUC/MUCBookmarkManager.cpp
@@ -19,7 +19,7 @@ namespace Swift {
MUCBookmarkManager::MUCBookmarkManager(IQRouter* iqRouter) {
iqRouter_ = iqRouter;
ready_ = false;
- boost::shared_ptr<GetPrivateStorageRequest<Storage> > request(new GetPrivateStorageRequest<Storage>(iqRouter_));
+ GetPrivateStorageRequest<Storage>::ref request = GetPrivateStorageRequest<Storage>::create(iqRouter_);
request->onResponse.connect(boost::bind(&MUCBookmarkManager::handleBookmarksReceived, this, _1, _2));
request->send();
}
@@ -106,7 +106,7 @@ void MUCBookmarkManager::flush() {
}
// Send an iq to save the storage element
- boost::shared_ptr<SetPrivateStorageRequest<Storage> > request(new SetPrivateStorageRequest<Storage>(storage, iqRouter_));
+ SetPrivateStorageRequest<Storage>::ref request = SetPrivateStorageRequest<Storage>::create(storage, iqRouter_);
// FIXME: We should care about the result
//request->onResponse.connect(boost::bind(&MUCBookmarkManager::handleBookmarksSet, this, _1, _2));
request->send();
diff --git a/Swiften/Queries/Request.h b/Swiften/Queries/Request.h
index 450e311..9184dea 100644
--- a/Swiften/Queries/Request.h
+++ b/Swiften/Queries/Request.h
@@ -21,6 +21,9 @@
namespace Swift {
class Request : public IQHandler, public boost::enable_shared_from_this<Request> {
public:
+ void send();
+
+ protected:
Request(
IQ::Type type,
const JID& receiver,
@@ -31,9 +34,6 @@ namespace Swift {
const JID& receiver,
IQRouter* router);
- void send();
-
- protected:
virtual void setPayload(boost::shared_ptr<Payload> p) {
payload_ = p;
}
diff --git a/Swiften/Queries/Requests/GetDiscoInfoRequest.h b/Swiften/Queries/Requests/GetDiscoInfoRequest.h
index 9ec1050..d1ed279 100644
--- a/Swiften/Queries/Requests/GetDiscoInfoRequest.h
+++ b/Swiften/Queries/Requests/GetDiscoInfoRequest.h
@@ -10,8 +10,17 @@
#include "Swiften/Elements/DiscoInfo.h"
namespace Swift {
- class GetDiscoInfoRequest : public GenericRequest<DiscoInfo> {
+ class GetDiscoInfoRequest : public GenericRequest<DiscoInfo>, public Shared<GetDiscoInfoRequest> {
public:
+ static ref create(const JID& jid, IQRouter* router) {
+ return ref(new GetDiscoInfoRequest(jid, router));
+ }
+
+ static ref create(const JID& jid, const String& node, IQRouter* router) {
+ return ref(new GetDiscoInfoRequest(jid, node, router));
+ }
+
+ private:
GetDiscoInfoRequest(const JID& jid, IQRouter* router) :
GenericRequest<DiscoInfo>(IQ::Get, jid, boost::shared_ptr<DiscoInfo>(new DiscoInfo()), router) {
}
diff --git a/Swiften/Queries/Requests/GetDiscoItemsRequest.h b/Swiften/Queries/Requests/GetDiscoItemsRequest.h
index 453eab4..ed565ac 100644
--- a/Swiften/Queries/Requests/GetDiscoItemsRequest.h
+++ b/Swiften/Queries/Requests/GetDiscoItemsRequest.h
@@ -10,8 +10,13 @@
#include "Swiften/Elements/DiscoItems.h"
namespace Swift {
- class GetDiscoItemsRequest : public GenericRequest<DiscoItems> {
+ class GetDiscoItemsRequest : public GenericRequest<DiscoItems>, public Shared<GetDiscoItemsRequest> {
public:
+ static ref create(const JID& jid, IQRouter* router) {
+ return ref(new GetDiscoItemsRequest(jid, router));
+ }
+
+ private:
GetDiscoItemsRequest(const JID& jid, IQRouter* router) :
GenericRequest<DiscoItems>(IQ::Get, jid, boost::shared_ptr<DiscoItems>(new DiscoItems()), router) {
}
diff --git a/Swiften/Queries/Requests/GetPrivateStorageRequest.h b/Swiften/Queries/Requests/GetPrivateStorageRequest.h
index d44d5d8..b593495 100644
--- a/Swiften/Queries/Requests/GetPrivateStorageRequest.h
+++ b/Swiften/Queries/Requests/GetPrivateStorageRequest.h
@@ -17,6 +17,13 @@ namespace Swift {
template<typename PAYLOAD_TYPE>
class GetPrivateStorageRequest : public Request {
public:
+ typedef boost::shared_ptr<GetPrivateStorageRequest<PAYLOAD_TYPE> > ref;
+
+ static ref create(IQRouter* router) {
+ return ref(new GetPrivateStorageRequest(router));
+ }
+
+ private:
GetPrivateStorageRequest(IQRouter* router) : Request(IQ::Get, JID(), boost::shared_ptr<PrivateStorage>(new PrivateStorage(boost::shared_ptr<Payload>(new PAYLOAD_TYPE()))), router) {
}
diff --git a/Swiften/Queries/Requests/GetRosterRequest.h b/Swiften/Queries/Requests/GetRosterRequest.h
index 59cefe4..271b2fb 100644
--- a/Swiften/Queries/Requests/GetRosterRequest.h
+++ b/Swiften/Queries/Requests/GetRosterRequest.h
@@ -8,10 +8,16 @@
#include "Swiften/Queries/GenericRequest.h"
#include "Swiften/Elements/RosterPayload.h"
+#include "Swiften/Base/Shared.h"
namespace Swift {
- class GetRosterRequest : public GenericRequest<RosterPayload> {
+ class GetRosterRequest : public GenericRequest<RosterPayload>, public Shared<GetRosterRequest> {
public:
+ static ref create(IQRouter* router) {
+ return ref(new GetRosterRequest(router));
+ }
+
+ private:
GetRosterRequest(IQRouter* router) :
GenericRequest<RosterPayload>(IQ::Get, JID(), boost::shared_ptr<Payload>(new RosterPayload()), router) {
}
diff --git a/Swiften/Queries/Requests/GetSecurityLabelsCatalogRequest.h b/Swiften/Queries/Requests/GetSecurityLabelsCatalogRequest.h
index 41225fb..ec04f80 100644
--- a/Swiften/Queries/Requests/GetSecurityLabelsCatalogRequest.h
+++ b/Swiften/Queries/Requests/GetSecurityLabelsCatalogRequest.h
@@ -8,10 +8,16 @@
#include "Swiften/Queries/GenericRequest.h"
#include "Swiften/Elements/SecurityLabelsCatalog.h"
+#include "Swiften/Base/Shared.h"
namespace Swift {
- class GetSecurityLabelsCatalogRequest : public GenericRequest<SecurityLabelsCatalog> {
+ class GetSecurityLabelsCatalogRequest : public GenericRequest<SecurityLabelsCatalog>, public Shared<GetSecurityLabelsCatalogRequest> {
public:
+ static ref create(const JID& recipient, IQRouter* router) {
+ return ref(new GetSecurityLabelsCatalogRequest(recipient, router));
+ }
+
+ private:
GetSecurityLabelsCatalogRequest(
const JID& recipient,
IQRouter* router) :
diff --git a/Swiften/Queries/Requests/GetVCardRequest.h b/Swiften/Queries/Requests/GetVCardRequest.h
index f369cdc..2c40cd1 100644
--- a/Swiften/Queries/Requests/GetVCardRequest.h
+++ b/Swiften/Queries/Requests/GetVCardRequest.h
@@ -13,6 +13,11 @@
namespace Swift {
class GetVCardRequest : public GenericRequest<VCard>, public Shared<GetVCardRequest> {
public:
+ static ref create(const JID& jid, IQRouter* router) {
+ return ref(new GetVCardRequest(jid, router));
+ }
+
+ private:
GetVCardRequest(const JID& jid, IQRouter* router) : GenericRequest<VCard>(IQ::Get, jid, boost::shared_ptr<Payload>(new VCard()), router) {
}
};
diff --git a/Swiften/Queries/Requests/SetPrivateStorageRequest.h b/Swiften/Queries/Requests/SetPrivateStorageRequest.h
index ebdfca8..1931f3a 100644
--- a/Swiften/Queries/Requests/SetPrivateStorageRequest.h
+++ b/Swiften/Queries/Requests/SetPrivateStorageRequest.h
@@ -17,6 +17,13 @@ namespace Swift {
template<typename PAYLOAD_TYPE>
class SetPrivateStorageRequest : public Request {
public:
+ typedef boost::shared_ptr<SetPrivateStorageRequest<PAYLOAD_TYPE> > ref;
+
+ static ref create(boost::shared_ptr<PAYLOAD_TYPE> payload, IQRouter* router) {
+ return ref(new SetPrivateStorageRequest<PAYLOAD_TYPE>(payload, router));
+ }
+
+ private:
SetPrivateStorageRequest(boost::shared_ptr<PAYLOAD_TYPE> payload, IQRouter* router) : Request(IQ::Set, JID(), boost::shared_ptr<PrivateStorage>(new PrivateStorage(payload)), router) {
}
diff --git a/Swiften/Queries/Requests/SetRosterRequest.h b/Swiften/Queries/Requests/SetRosterRequest.h
index 1f47cfe..7b1bf8c 100644
--- a/Swiften/Queries/Requests/SetRosterRequest.h
+++ b/Swiften/Queries/Requests/SetRosterRequest.h
@@ -11,10 +11,16 @@
#include "Swiften/Queries/Request.h"
#include "Swiften/Elements/RosterPayload.h"
+#include "Swiften/Base/Shared.h"
namespace Swift {
- class SetRosterRequest : public Request {
+ class SetRosterRequest : public Request, public Shared<SetRosterRequest> {
public:
+ static ref create(RosterPayload::ref payload, IQRouter* router) {
+ return ref(new SetRosterRequest(payload, router));
+ }
+
+ private:
SetRosterRequest(boost::shared_ptr<RosterPayload> payload, IQRouter* router) : Request(IQ::Set, JID(), boost::shared_ptr<RosterPayload>(payload), router) {
}
diff --git a/Swiften/Queries/Requests/UnitTest/GetPrivateStorageRequestTest.cpp b/Swiften/Queries/Requests/UnitTest/GetPrivateStorageRequestTest.cpp
index d90a114..fe8b0a0 100644
--- a/Swiften/Queries/Requests/UnitTest/GetPrivateStorageRequestTest.cpp
+++ b/Swiften/Queries/Requests/UnitTest/GetPrivateStorageRequestTest.cpp
@@ -45,8 +45,8 @@ class GetPrivateStorageRequestTest : public CppUnit::TestFixture
}
void testSend() {
- GetPrivateStorageRequest<MyPayload> request(router);
- request.send();
+ GetPrivateStorageRequest<MyPayload>::ref request = GetPrivateStorageRequest<MyPayload>::create(router);
+ request->send();
CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel->iqs_.size()));
CPPUNIT_ASSERT_EQUAL(JID(), channel->iqs_[0]->getTo());
@@ -58,9 +58,9 @@ class GetPrivateStorageRequestTest : public CppUnit::TestFixture
}
void testHandleResponse() {
- GetPrivateStorageRequest<MyPayload> testling(router);
- testling.onResponse.connect(boost::bind(&GetPrivateStorageRequestTest::handleResponse, this, _1, _2));
- testling.send();
+ GetPrivateStorageRequest<MyPayload>::ref testling = GetPrivateStorageRequest<MyPayload>::create(router);
+ testling->onResponse.connect(boost::bind(&GetPrivateStorageRequestTest::handleResponse, this, _1, _2));
+ testling->send();
channel->onIQReceived(createResponse("test-id", "foo"));
CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(responses.size()));
@@ -68,9 +68,9 @@ class GetPrivateStorageRequestTest : public CppUnit::TestFixture
}
void testHandleResponse_Error() {
- GetPrivateStorageRequest<MyPayload> testling(router);
- testling.onResponse.connect(boost::bind(&GetPrivateStorageRequestTest::handleResponse, this, _1, _2));
- testling.send();
+ GetPrivateStorageRequest<MyPayload>::ref testling = GetPrivateStorageRequest<MyPayload>::create(router);
+ testling->onResponse.connect(boost::bind(&GetPrivateStorageRequestTest::handleResponse, this, _1, _2));
+ testling->send();
channel->onIQReceived(createError("test-id"));
CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(responses.size()));
diff --git a/Swiften/Roster/XMPPRosterController.cpp b/Swiften/Roster/XMPPRosterController.cpp
index dca74c0..7743ec8 100644
--- a/Swiften/Roster/XMPPRosterController.cpp
+++ b/Swiften/Roster/XMPPRosterController.cpp
@@ -29,7 +29,7 @@ XMPPRosterController::XMPPRosterController(IQRouter* iqRouter, XMPPRoster* xmppR
void XMPPRosterController::requestRoster() {
xmppRoster_->clear();
- boost::shared_ptr<GetRosterRequest> rosterRequest(new GetRosterRequest(iqRouter_));
+ GetRosterRequest::ref rosterRequest = GetRosterRequest::create(iqRouter_);
rosterRequest->onResponse.connect(boost::bind(&XMPPRosterController::handleRosterReceived, this, _1));
rosterRequest->send();
}
diff --git a/Swiften/SConscript b/Swiften/SConscript
index c5ad9a6..7665fb0 100644
--- a/Swiften/SConscript
+++ b/Swiften/SConscript
@@ -1,3 +1,5 @@
+import os
+
Import("env")
################################################################################
@@ -244,3 +246,19 @@ if env["SCONS_STAGE"] == "build" :
File("StringCodecs/UnitTest/PBKDF2Test.cpp"),
File("VCards/UnitTest/VCardManagerTest.cpp"),
])
+
+ # Generate the Swiften header
+ swiften_header = "#pragma once\n"
+ top_path = env.Dir("..").abspath
+ public_dirs = ["Queries", "Client", "Elements"]
+ for public_dir in public_dirs :
+ for root, dirs, files in os.walk(env.Dir(public_dir).abspath) :
+ if root.endswith("UnitTest") :
+ continue
+ for file in files :
+ if not file.endswith(".h") :
+ continue
+ swiften_header += "#include \"" + os.path.relpath(os.path.join(root, file), top_path) + "\"\n"
+ for file in ["EventLoop/SimpleEventLoop.h"] :
+ swiften_header += "#include \"Swiften/" + file + "\"\n"
+ swiften_env.WriteVal("Swiften.h", swiften_env.Value(swiften_header)) \ No newline at end of file
diff --git a/Swiften/VCards/VCardManager.cpp b/Swiften/VCards/VCardManager.cpp
index 673b937..c189717 100644
--- a/Swiften/VCards/VCardManager.cpp
+++ b/Swiften/VCards/VCardManager.cpp
@@ -34,7 +34,7 @@ void VCardManager::requestVCard(const JID& requestedJID) {
if (requestedVCards.find(jid) != requestedVCards.end()) {
return;
}
- GetVCardRequest::ref request(new GetVCardRequest(jid, iqRouter));
+ GetVCardRequest::ref request = GetVCardRequest::create(jid, iqRouter);
request->onResponse.connect(boost::bind(&VCardManager::handleVCardReceived, this, jid, _1, _2));
request->send();
requestedVCards.insert(jid);