summaryrefslogtreecommitdiffstats
path: root/sandbox/sebastien/cpp/apr-2/components/chat
diff options
context:
space:
mode:
Diffstat (limited to 'sandbox/sebastien/cpp/apr-2/components/chat')
-rw-r--r--sandbox/sebastien/cpp/apr-2/components/chat/Makefile.am68
-rw-r--r--sandbox/sebastien/cpp/apr-2/components/chat/chat-sender.componentType29
-rw-r--r--sandbox/sebastien/cpp/apr-2/components/chat/chat-sender.cpp150
-rw-r--r--sandbox/sebastien/cpp/apr-2/components/chat/chat-sendreceiver.componentType30
-rw-r--r--sandbox/sebastien/cpp/apr-2/components/chat/chat-sendreceiver.cpp164
-rw-r--r--sandbox/sebastien/cpp/apr-2/components/chat/chat.composite52
-rw-r--r--sandbox/sebastien/cpp/apr-2/components/chat/client-test.cpp111
-rwxr-xr-xsandbox/sebastien/cpp/apr-2/components/chat/echo-test31
-rwxr-xr-xsandbox/sebastien/cpp/apr-2/components/chat/server-test39
-rw-r--r--sandbox/sebastien/cpp/apr-2/components/chat/server-test.scm20
-rw-r--r--sandbox/sebastien/cpp/apr-2/components/chat/test/TestVysperServer.java79
-rwxr-xr-xsandbox/sebastien/cpp/apr-2/components/chat/vysper-classpath29
-rwxr-xr-xsandbox/sebastien/cpp/apr-2/components/chat/vysper-start25
-rwxr-xr-xsandbox/sebastien/cpp/apr-2/components/chat/vysper-stop25
-rw-r--r--sandbox/sebastien/cpp/apr-2/components/chat/xmpp-test.cpp103
-rw-r--r--sandbox/sebastien/cpp/apr-2/components/chat/xmpp.hpp320
16 files changed, 1275 insertions, 0 deletions
diff --git a/sandbox/sebastien/cpp/apr-2/components/chat/Makefile.am b/sandbox/sebastien/cpp/apr-2/components/chat/Makefile.am
new file mode 100644
index 0000000000..35fb57daf3
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/components/chat/Makefile.am
@@ -0,0 +1,68 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+JAVAROOT = $(top_builddir)/components/chat
+
+if WANT_CHAT
+
+INCLUDES = -I${LIBSTROPHE_INCLUDE}
+
+incl_HEADERS = *.hpp
+incldir = $(prefix)/include/components/chat
+
+dist_comp_SCRIPTS = vysper-start vysper-stop vysper-classpath
+compdir=$(prefix)/components/chat
+
+comp_DATA = vysper.prefix
+vysper.prefix: $(top_builddir)/config.status
+ echo ${VYSPER_PREFIX} >vysper.prefix
+
+EXTRA_DIST = chat.composite chat-sendreceiver.componentType chat-sender.componentType *.scm
+
+comp_LTLIBRARIES = libchat-sendreceiver.la libchat-sender.la
+noinst_DATA = libchat-sendreceiver.so libchat-sender.so
+
+libchat_sendreceiver_la_SOURCES = chat-sendreceiver.cpp
+libchat_sendreceiver_la_LDFLAGS = -L${LIBSTROPHE_LIB} -R${LIBSTROPHE_LIB} -lstrophe -lssl -lresolv
+libchat-sendreceiver.so:
+ ln -s .libs/libchat-sendreceiver.so
+
+libchat_sender_la_SOURCES = chat-sender.cpp
+libchat_sender_la_LDFLAGS = -L${LIBSTROPHE_LIB} -R${LIBSTROPHE_LIB} -lstrophe -lssl -lresolv
+libchat-sender.so:
+ ln -s .libs/libchat-sender.so
+
+xmpp_test_SOURCES = xmpp-test.cpp
+xmpp_test_LDFLAGS = -L${LIBSTROPHE_LIB} -R${LIBSTROPHE_LIB} -lstrophe -lssl -lresolv
+
+client_test_SOURCES = client-test.cpp
+client_test_LDFLAGS = -lxml2 -lcurl -lmozjs -L${LIBSTROPHE_LIB} -R${LIBSTROPHE_LIB} -lstrophe -lssl -lresolv
+
+noinst_PROGRAMS = xmpp-test client-test
+dist_noinst_SCRIPTS = server-test
+
+if WANT_VYSPER
+
+AM_JAVACFLAGS = -cp `${top_builddir}/components/chat/vysper-classpath ${VYSPER_PREFIX}`${JAVAROOT}
+dist_noinst_JAVA = test/*.java
+CLEANFILES = test/*.class
+
+dist_noinst_SCRIPTS += echo-test
+TESTS = echo-test
+endif
+
+endif
diff --git a/sandbox/sebastien/cpp/apr-2/components/chat/chat-sender.componentType b/sandbox/sebastien/cpp/apr-2/components/chat/chat-sender.componentType
new file mode 100644
index 0000000000..01838f0260
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/components/chat/chat-sender.componentType
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<componentType xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace="http://tuscany.apache.org/xmlns/sca/components">
+
+ <service name="send"/>
+ <property name="jid" type="xsd:string"/>
+ <property name="password" type="xsd:string"/>
+
+</composite>
diff --git a/sandbox/sebastien/cpp/apr-2/components/chat/chat-sender.cpp b/sandbox/sebastien/cpp/apr-2/components/chat/chat-sender.cpp
new file mode 100644
index 0000000000..d3726a4e1c
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/components/chat/chat-sender.cpp
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * XMPP chat sender component implementation.
+ */
+
+#include "string.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "parallel.hpp"
+#include "xmpp.hpp"
+
+namespace tuscany {
+namespace chat {
+namespace sender {
+
+/**
+ * Post an item to an XMPP JID.
+ */
+const failable<value> post(const list<value>& params, XMPPClient& xc) {
+ const value to = car<value>(car(params));
+ const value val = cadr(params);
+ debug(to, "chat::post::jid");
+ debug(val, "chat::post::value");
+ const failable<bool> r = post(to, val, xc);
+ if (!hasContent(r))
+ return mkfailure<value>(reason(r));
+ return value(mklist<value>(to));
+}
+
+/**
+ * Subscribe and listen to an XMPP session.
+ */
+class noop {
+public:
+ noop() {
+ }
+
+ const failable<bool> operator()(unused const value& jid, unused const value& val, unused XMPPClient& xc) const {
+ return true;
+ }
+};
+
+class subscribe {
+public:
+ subscribe(XMPPClient& xc) : xc(xc) {
+ }
+
+ const failable<bool> operator()() const {
+ gc_pool pool;
+ debug("chat::subscribe::listen");
+ const failable<bool> r = listen(noop(), const_cast<XMPPClient&>(xc));
+ debug("chat::subscribe::stopped");
+ return r;
+ }
+
+private:
+ const lambda<failable<bool>(const value&, const value&, XMPPClient&)> l;
+ XMPPClient xc;
+};
+
+/**
+ * Chatter component lambda function
+ */
+class chatSender {
+public:
+ chatSender(XMPPClient& xc, worker& w) : xc(xc), w(w) {
+ }
+
+ const value operator()(const list<value>& params) const {
+ const tuscany::value func(car(params));
+ if (func == "post")
+ return post(cdr(params), const_cast<XMPPClient&>(xc));
+
+ // Stop the chat sender component
+ if (func != "stop")
+ return tuscany::mkfailure<tuscany::value>();
+ debug("chat::sender::stop");
+
+ // Disconnect and shutdown the worker thread
+ disconnect(const_cast<XMPPClient&>(xc));
+ cancel(const_cast<worker&>(w));
+ debug("chat::sender::stopped");
+
+ return failable<value>(value(lambda<value(const list<value>&)>()));
+ }
+
+private:
+ const XMPPClient xc;
+ worker w;
+};
+
+/**
+ * Start the component.
+ */
+const failable<value> start(const list<value>& params) {
+ // Extract the the XMPP JID and password
+ const list<value> props = params;
+ const value jid = ((lambda<value(list<value>)>)car(props))(list<value>());
+ const value pass = ((lambda<value(list<value>)>)cadr(props))(list<value>());
+
+ // Create an XMPP client session
+ XMPPClient xc(jid, pass, false);
+ const failable<bool> r = connect(xc);
+ if (!hasContent(r))
+ return mkfailure<value>(reason(r));
+
+ // Listen and relay messages in a worker thread
+ worker w(3);
+ submit<failable<bool> >(w, lambda<failable<bool>()>(subscribe(xc)));
+
+ // Return the chat sender component lambda function
+ return value(lambda<value(const list<value>&)>(chatSender(xc, w)));
+}
+
+}
+}
+}
+
+extern "C" {
+
+const tuscany::value apply(const tuscany::list<tuscany::value>& params) {
+ const tuscany::value func(car(params));
+ if (func == "start")
+ return tuscany::chat::sender::start(cdr(params));
+ return tuscany::mkfailure<tuscany::value>();
+}
+
+}
diff --git a/sandbox/sebastien/cpp/apr-2/components/chat/chat-sendreceiver.componentType b/sandbox/sebastien/cpp/apr-2/components/chat/chat-sendreceiver.componentType
new file mode 100644
index 0000000000..0367c38f55
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/components/chat/chat-sendreceiver.componentType
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<componentType xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace="http://tuscany.apache.org/xmlns/sca/components">
+
+ <service name="send"/>
+ <reference name="relay"/>
+ <property name="jid" type="xsd:string"/>
+ <property name="password" type="xsd:string"/>
+
+</composite>
diff --git a/sandbox/sebastien/cpp/apr-2/components/chat/chat-sendreceiver.cpp b/sandbox/sebastien/cpp/apr-2/components/chat/chat-sendreceiver.cpp
new file mode 100644
index 0000000000..610f3cc9cc
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/components/chat/chat-sendreceiver.cpp
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * XMPP chat sender/receiver component implementation.
+ */
+
+#include "string.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "parallel.hpp"
+#include "xmpp.hpp"
+
+namespace tuscany {
+namespace chat {
+namespace sendreceiver {
+
+/**
+ * Post an item to an XMPP JID.
+ */
+const failable<value> post(const list<value>& params, XMPPClient& xc) {
+ const value to = car<value>(car(params));
+ const value val = cadr(params);
+ debug(to, "chat::post::jid");
+ debug(val, "chat::post::value");
+ const failable<bool> r = post(to, val, xc);
+ if (!hasContent(r))
+ return mkfailure<value>(reason(r));
+ return value(mklist<value>(to));
+}
+
+/**
+ * A relay function that posts the XMPP messages it receives to a relay component reference.
+ */
+class relay {
+public:
+ relay(const lambda<value(const list<value>&)>& rel) : rel(rel) {
+ }
+
+ const failable<bool> operator()(const value& jid, const value& val, unused XMPPClient& xc) const {
+ if (isNil(rel))
+ return true;
+ debug(jid, "chat::relay::jid");
+ debug(val, "chat::relay::value");
+ const value res = rel(mklist<value>("post", mklist<value>(jid), val));
+ return true;
+ }
+
+private:
+ const lambda<value(const list<value>&)> rel;
+};
+
+/**
+ * Subscribe and listen to an XMPP session.
+ */
+class subscribe {
+public:
+ subscribe(const lambda<failable<bool>(const value&, const value&, XMPPClient&)>& l, XMPPClient& xc) : l(l), xc(xc) {
+ }
+
+ const failable<bool> operator()() const {
+ gc_pool pool;
+ debug("chat::subscribe::listen");
+ const failable<bool> r = listen(l, const_cast<XMPPClient&>(xc));
+ debug("chat::subscribe::stopped");
+ return r;
+ }
+
+private:
+ const lambda<failable<bool>(const value&, const value&, XMPPClient&)> l;
+ XMPPClient xc;
+};
+
+/**
+ * Chat sender/receiver component lambda function
+ */
+class chatSenderReceiver {
+public:
+ chatSenderReceiver(XMPPClient& xc, worker& w) : xc(xc), w(w) {
+ }
+
+ const value operator()(const list<value>& params) const {
+ const tuscany::value func(car(params));
+ if (func == "post")
+ return post(cdr(params), const_cast<XMPPClient&>(xc));
+
+ // Stop the chat sender/receiver component
+ if (func != "stop")
+ return tuscany::mkfailure<tuscany::value>();
+ debug("chat::sendreceiver::stop");
+
+ // Disconnect and shutdown the worker thread
+ disconnect(const_cast<XMPPClient&>(xc));
+ cancel(const_cast<worker&>(w));
+ debug("chat::sendreceiver::stopped");
+
+ return failable<value>(value(lambda<value(const list<value>&)>()));
+ }
+
+private:
+ const XMPPClient xc;
+ worker w;
+};
+
+/**
+ * Start the component.
+ */
+const failable<value> start(const list<value>& params) {
+ // Extract the relay reference and the XMPP JID and password
+ const bool hasRelay = !isNil(cddr(params));
+ const value rel = hasRelay? car(params) : value(lambda<value(const list<value>&)>());
+ const list<value> props = hasRelay? cdr(params) : params;
+ const value jid = ((lambda<value(list<value>)>)car(props))(list<value>());
+ const value pass = ((lambda<value(list<value>)>)cadr(props))(list<value>());
+
+ // Create an XMPP client session
+ XMPPClient xc(jid, pass, false);
+ const failable<bool> r = connect(xc);
+ if (!hasContent(r))
+ return mkfailure<value>(reason(r));
+
+ // Listen and relay messages in a worker thread
+ worker w(3);
+ const lambda<failable<bool>(const value&, const value&, XMPPClient&)> rl = relay(rel);
+ submit<failable<bool> >(w, lambda<failable<bool>()>(subscribe(rl, xc)));
+
+ // Return the chat sender/receiver component lambda function
+ return value(lambda<value(const list<value>&)>(chatSenderReceiver(xc, w)));
+}
+
+}
+}
+}
+
+extern "C" {
+
+const tuscany::value apply(const tuscany::list<tuscany::value>& params) {
+ const tuscany::value func(car(params));
+ if (func == "start")
+ return tuscany::chat::sendreceiver::start(cdr(params));
+ return tuscany::mkfailure<tuscany::value>();
+}
+
+}
diff --git a/sandbox/sebastien/cpp/apr-2/components/chat/chat.composite b/sandbox/sebastien/cpp/apr-2/components/chat/chat.composite
new file mode 100644
index 0000000000..9eada5c372
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/components/chat/chat.composite
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<composite xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace="http://tuscany.apache.org/xmlns/sca/components"
+ name="chat">
+
+ <component name="print-sender">
+ <implementation.cpp path="." library="libchat-sender"/>
+ <property name="jid">sca1@localhost</property>
+ <property name="password">sca1</property>
+ <service name="print-sender">
+ <t:binding.http uri="print-sender"/>
+ </service>
+ </component>
+
+ <component name="print-chatter">
+ <implementation.cpp path="." library="libchat-sendreceiver"/>
+ <property name="jid">sca2@localhost</property>
+ <property name="password">sca2</property>
+ <service name="print-chatter">
+ <t:binding.http uri="print-chatter"/>
+ </service>
+ <reference name="relay" target="print"/>
+ </component>
+
+ <component name="print">
+ <t:implementation.scheme script="server-test.scm"/>
+ <service name="print">
+ <t:binding.http uri="print"/>
+ </service>
+ <reference name="report" target="print-chatter"/>
+ </component>
+
+</composite>
diff --git a/sandbox/sebastien/cpp/apr-2/components/chat/client-test.cpp b/sandbox/sebastien/cpp/apr-2/components/chat/client-test.cpp
new file mode 100644
index 0000000000..2c91fda1f2
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/components/chat/client-test.cpp
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * Test chat component.
+ */
+
+#include <assert.h>
+#include "stream.hpp"
+#include "string.hpp"
+#include "list.hpp"
+#include "element.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "perf.hpp"
+#include "parallel.hpp"
+#include "../../modules/http/http.hpp"
+#include "xmpp.hpp"
+
+namespace tuscany {
+namespace chat {
+
+const value jid1("sca1@localhost");
+const value pass1("sca1");
+const value jid2("sca2@localhost");
+const value pass2("sca2");
+const value jid3("sca3@localhost");
+const value pass3("sca3");
+
+const list<value> item = list<value>()
+ + (list<value>() + "name" + string("Apple"))
+ + (list<value>() + "price" + string("$2.99"));
+const list<value> entry = mklist<value>(string("item"), string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), item);
+
+worker w(2);
+bool received;
+
+const failable<bool> listener(const value& from, const value& val, unused XMPPClient& xc) {
+ assert(contains(from, "sca2@localhost"));
+ assert(val == entry);
+ received = true;
+ return false;
+}
+
+struct subscribe {
+ XMPPClient& xc;
+ subscribe(XMPPClient& xc) : xc(xc) {
+ }
+ const failable<bool> operator()() const {
+ const lambda<failable<bool>(const value&, const value&, XMPPClient&)> l(listener);
+ listen(l, xc);
+ return true;
+ }
+};
+
+bool testListen() {
+ received = false;
+ XMPPClient& xc = *(new (gc_new<XMPPClient>()) XMPPClient(jid3, pass3));
+ const failable<bool> c = connect(xc);
+ assert(hasContent(c));
+ const lambda<failable<bool>()> subs = subscribe(xc);
+ submit(w, subs);
+ return true;
+}
+
+bool testPost() {
+ gc_scoped_pool pool;
+ http::CURLSession ch("", "", "");
+ const failable<value> id = http::post(entry, "http://localhost:8090/print-sender/sca2@localhost", ch);
+ assert(hasContent(id));
+ return true;
+}
+
+bool testReceived() {
+ shutdown(w);
+ assert(received == true);
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::chat::testListen();
+ tuscany::chat::testPost();
+ tuscany::chat::testReceived();
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sandbox/sebastien/cpp/apr-2/components/chat/echo-test b/sandbox/sebastien/cpp/apr-2/components/chat/echo-test
new file mode 100755
index 0000000000..271d40d122
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/components/chat/echo-test
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Setup
+./vysper-start
+sleep 3
+
+# Test
+./xmpp-test 2>/dev/null
+rc=$?
+
+# Cleanup
+./vysper-stop
+sleep 1
+return $rc
diff --git a/sandbox/sebastien/cpp/apr-2/components/chat/server-test b/sandbox/sebastien/cpp/apr-2/components/chat/server-test
new file mode 100755
index 0000000000..dd1dc6faa7
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/components/chat/server-test
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Setup
+../../modules/http/httpd-conf tmp localhost 8090 ../../modules/http/htdocs
+../../modules/server/server-conf tmp
+../../modules/server/scheme-conf tmp
+cat >>tmp/conf/httpd.conf <<EOF
+SCAContribution `pwd`/
+SCAComposite chat.composite
+EOF
+
+../../modules/http/httpd-start tmp
+sleep 2
+
+# Test
+./client-test 2>/dev/null
+rc=$?
+
+# Cleanup
+../../modules/http/httpd-stop tmp
+sleep 1
+return $rc
diff --git a/sandbox/sebastien/cpp/apr-2/components/chat/server-test.scm b/sandbox/sebastien/cpp/apr-2/components/chat/server-test.scm
new file mode 100644
index 0000000000..a6023708e1
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/components/chat/server-test.scm
@@ -0,0 +1,20 @@
+; Licensed to the Apache Software Foundation (ASF) under one
+; or more contributor license agreements. See the NOTICE file
+; distributed with this work for additional information
+; regarding copyright ownership. The ASF licenses this file
+; to you under the Apache License, Version 2.0 (the
+; "License"); you may not use this file except in compliance
+; with the License. You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing,
+; software distributed under the License is distributed on an
+; "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+; KIND, either express or implied. See the License for the
+; specific language governing permissions and limitations
+; under the License.
+
+; Chat test case
+
+(define (post key val report) (report "post" '("sca3@localhost") val))
diff --git a/sandbox/sebastien/cpp/apr-2/components/chat/test/TestVysperServer.java b/sandbox/sebastien/cpp/apr-2/components/chat/test/TestVysperServer.java
new file mode 100644
index 0000000000..3d2b7d7c3e
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/components/chat/test/TestVysperServer.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package test;
+
+/**
+ * A test XMPP server, using Apache Vysper.
+ */
+import static java.lang.System.*;
+
+import java.io.File;
+
+import org.apache.vysper.mina.TCPEndpoint;
+import org.apache.vysper.stanzasession.StanzaSessionFactory;
+import org.apache.vysper.storage.StorageProviderRegistry;
+import org.apache.vysper.storage.inmemory.MemoryStorageProviderRegistry;
+import org.apache.vysper.xmpp.authorization.AccountManagement;
+import org.apache.vysper.xmpp.modules.extension.xep0049_privatedata.PrivateDataModule;
+import org.apache.vysper.xmpp.modules.extension.xep0054_vcardtemp.VcardTempModule;
+import org.apache.vysper.xmpp.modules.extension.xep0092_software_version.SoftwareVersionModule;
+import org.apache.vysper.xmpp.modules.extension.xep0119_xmppping.XmppPingModule;
+import org.apache.vysper.xmpp.modules.extension.xep0202_entity_time.EntityTimeModule;
+import org.apache.vysper.xmpp.server.XMPPServer;
+
+class TestVysperServer {
+ public static void main(final String args[]) throws Exception {
+ out.println("Starting test Vysper server...");
+
+ // Add the XMPP users used by the xmpp-test and server-test test cases
+ // If you're using your own XMPP server you need to add these users manually
+ final StorageProviderRegistry providerRegistry = new MemoryStorageProviderRegistry();
+ final AccountManagement accountManagement = (AccountManagement)providerRegistry.retrieve(AccountManagement.class);
+ accountManagement.addUser("sca1@localhost", "sca1");
+ accountManagement.addUser("sca2@localhost", "sca2");
+ accountManagement.addUser("sca3@localhost", "sca3");
+
+ // Create and start XMPP server for domain: localhost
+ final XMPPServer server = new org.apache.vysper.xmpp.server.XMPPServer("localhost");
+ server.addEndpoint(new TCPEndpoint());
+ server.addEndpoint(new StanzaSessionFactory());
+ server.setStorageProviderRegistry(providerRegistry);
+ final File cert = new File(TestVysperServer.class.getClassLoader().getResource("bogus_mina_tls.cert").getPath());
+ server.setTLSCertificateInfo(cert, "boguspw");
+ server.start();
+ server.addModule(new SoftwareVersionModule());
+ server.addModule(new EntityTimeModule());
+ server.addModule(new VcardTempModule());
+ server.addModule(new XmppPingModule());
+ server.addModule(new PrivateDataModule());
+ out.println("Test Vysper server started...");
+
+ // Wait forever
+ final Object lock = new Object();
+ synchronized(lock) {
+ lock.wait();
+ }
+
+ System.out.println("Stopping test Vysper server...");
+ server.stop();
+ out.println("Test Vysper server stopped.");
+ System.exit(0);
+ }
+}
diff --git a/sandbox/sebastien/cpp/apr-2/components/chat/vysper-classpath b/sandbox/sebastien/cpp/apr-2/components/chat/vysper-classpath
new file mode 100755
index 0000000000..7cf16a5ae8
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/components/chat/vysper-classpath
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Compute a classpath for running a Vysper server
+here=`readlink -f $0`; here=`dirname $here`
+
+if [ "$1" = "" ]; then
+ vysper_prefix=`cat $here/vysper.prefix`
+else
+ vysper_prefix=$1
+fi
+jars=`find $vysper_prefix/lib -name "*.jar" | awk '{ printf "%s:", $1 }'`
+echo "$vysper_prefix/config:$jars"
diff --git a/sandbox/sebastien/cpp/apr-2/components/chat/vysper-start b/sandbox/sebastien/cpp/apr-2/components/chat/vysper-start
new file mode 100755
index 0000000000..af95ecadf4
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/components/chat/vysper-start
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Start Vysper test XMPP server
+here=`readlink -f $0`; here=`dirname $here`
+
+java_prefix=`cat $here/../../modules/java/java.prefix`
+mkdir -p $here/tmp/logs
+${java_prefix}/jre/bin/java -cp `$here/vysper-classpath`$here test.TestVysperServer 2>&1 1>>$here/tmp/logs/vysper.log &
diff --git a/sandbox/sebastien/cpp/apr-2/components/chat/vysper-stop b/sandbox/sebastien/cpp/apr-2/components/chat/vysper-stop
new file mode 100755
index 0000000000..3c4be4efa9
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/components/chat/vysper-stop
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Stop Vysper test XMPP server
+here=`readlink -f $0`; here=`dirname $here`
+
+java_prefix=`cat $here/../../modules/java/java.prefix`
+kill `ps -ef | grep -v grep | grep "${java_prefix}/jre/bin/java" | grep "vysper" | awk '{ print $2 }'`
+
diff --git a/sandbox/sebastien/cpp/apr-2/components/chat/xmpp-test.cpp b/sandbox/sebastien/cpp/apr-2/components/chat/xmpp-test.cpp
new file mode 100644
index 0000000000..6b7fa3439f
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/components/chat/xmpp-test.cpp
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * Test XMPP support functions.
+ */
+
+#include <assert.h>
+#include "stream.hpp"
+#include "string.hpp"
+#include "list.hpp"
+#include "element.hpp"
+#include "monad.hpp"
+#include "value.hpp"
+#include "perf.hpp"
+#include "parallel.hpp"
+#include "xmpp.hpp"
+
+namespace tuscany {
+namespace chat {
+
+const value jid1("sca1@localhost");
+const value pass1("sca1");
+const value jid2("sca2@localhost");
+const value pass2("sca2");
+
+worker w(2);
+bool received;
+
+const failable<bool> listener(const value& from, const value& val, unused XMPPClient& xc) {
+ assert(contains(from, "sca1@localhost"));
+ assert(val == "hey");
+ received = true;
+ return false;
+}
+
+struct subscribe {
+ XMPPClient& xc;
+ subscribe(XMPPClient& xc) : xc(xc) {
+ }
+ const failable<bool> operator()() const {
+ const lambda<failable<bool>(const value&, const value&, XMPPClient&)> l(listener);
+ listen(l, xc);
+ return true;
+ }
+};
+
+bool testListen() {
+ received = false;
+ XMPPClient& xc = *(new (gc_new<XMPPClient>()) XMPPClient(jid2, pass2));
+ const failable<bool> c = connect(xc);
+ assert(hasContent(c));
+ const lambda<failable<bool>()> subs = subscribe(xc);
+ submit(w, subs);
+ return true;
+}
+
+bool testPost() {
+ XMPPClient xc(jid1, pass1);
+ const failable<bool> c = connect(xc);
+ assert(hasContent(c));
+ const failable<bool> p = post(jid2, "hey", xc);
+ assert(hasContent(p));
+ return true;
+}
+
+bool testReceived() {
+ shutdown(w);
+ assert(received == true);
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::chat::testListen();
+ tuscany::chat::testPost();
+ tuscany::chat::testReceived();
+
+ tuscany::cout << "OK" << tuscany::endl;
+ return 0;
+}
diff --git a/sandbox/sebastien/cpp/apr-2/components/chat/xmpp.hpp b/sandbox/sebastien/cpp/apr-2/components/chat/xmpp.hpp
new file mode 100644
index 0000000000..3a6aa86c5c
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/components/chat/xmpp.hpp
@@ -0,0 +1,320 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+#ifndef tuscany_queue_hpp
+#define tuscany_queue_hpp
+
+/**
+ * XMPP support functions.
+ */
+
+#include "strophe.h"
+extern "C" {
+#include "common.h"
+}
+#include "string.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "../../modules/scheme/eval.hpp"
+
+namespace tuscany {
+namespace chat {
+
+/**
+ * XMPP runtime, one per process.
+ */
+class XMPPRuntime {
+public:
+ XMPPRuntime() {
+ xmpp_initialize();
+ log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG);
+ }
+
+ ~XMPPRuntime() {
+ xmpp_shutdown();
+ }
+
+private:
+ friend class XMPPClient;
+ xmpp_log_t* log;
+
+} xmppRuntime;
+
+/**
+ * Represents an XMPP client.
+ */
+class XMPPClient {
+public:
+ XMPPClient(const string& jid, const string& pass, bool owner = true) : owner(owner), ctx(xmpp_ctx_new(NULL, xmppRuntime.log)), conn(xmpp_conn_new(ctx)), connecting(false), connected(false), disconnecting(false) {
+ xmpp_conn_set_jid(conn, c_str(jid + "/" + mkuuid()));
+ xmpp_conn_set_pass(conn, c_str(pass));
+ }
+
+ XMPPClient(const XMPPClient& xc) : owner(false), ctx(xc.ctx), conn(xc.conn), listener(xc.listener), connecting(xc.connecting), connected(xc.connected), disconnecting(xc.disconnecting) {
+ }
+
+ ~XMPPClient() {
+ extern const failable<bool> disconnect(XMPPClient& xc);
+ if (!owner)
+ return;
+ if (!disconnecting)
+ disconnect(*this);
+ xmpp_conn_release(conn);
+ xmpp_ctx_free(ctx);
+ }
+
+private:
+ friend int versionHandler(xmpp_conn_t* const conn, xmpp_stanza_t* const stanza, void* const udata);
+ friend void connHandler(xmpp_conn_t * const conn, const xmpp_conn_event_t status, const int err, xmpp_stream_error_t* const errstream, void *const udata);
+ friend int messageHandler(xmpp_conn_t* const conn, xmpp_stanza_t* const stanza, void* const udata);
+ friend const failable<bool> connect(XMPPClient& xc);
+ friend const failable<size_t> send(const char* data, const size_t len, XMPPClient& xc);
+ friend const failable<size_t> send(xmpp_stanza_t* const stanza, XMPPClient& xc);
+ friend const failable<bool> post(const value& to, const value& val, XMPPClient& xc);
+ friend const failable<bool> disconnect(XMPPClient& xc);
+ friend const failable<bool> listen(const lambda<failable<bool>(const value&, const value&, XMPPClient&)>& listener, XMPPClient& xc);
+
+ const bool owner;
+ xmpp_ctx_t* ctx;
+ xmpp_conn_t* conn;
+ lambda<failable<bool>(const value&, const value&, XMPPClient&)> listener;
+ bool connecting;
+ bool connected;
+ bool disconnecting;
+};
+
+/**
+ * Make a text stanza.
+ */
+xmpp_stanza_t* textStanza(const char* text, xmpp_ctx_t* ctx) {
+ xmpp_stanza_t* stanza = xmpp_stanza_new(ctx);
+ xmpp_stanza_set_text(stanza, text);
+ return stanza;
+}
+
+/**
+ * Make a named stanza.
+ */
+xmpp_stanza_t* namedStanza(const char* ns, const char* name, xmpp_ctx_t* ctx) {
+ xmpp_stanza_t* stanza = xmpp_stanza_new(ctx);
+ xmpp_stanza_set_name(stanza, name);
+ if (ns != NULL)
+ xmpp_stanza_set_ns(stanza, ns);
+ return stanza;
+}
+
+/**
+ * Make a named stanza using a qualified name.
+ */
+xmpp_stanza_t* namedStanza(const char* name, xmpp_ctx_t* ctx) {
+ xmpp_stanza_t* stanza = xmpp_stanza_new(ctx);
+ xmpp_stanza_set_name(stanza, name);
+ return stanza;
+}
+
+/**
+ * XMPP version handler.
+ */
+int versionHandler(xmpp_conn_t* const conn, xmpp_stanza_t* const stanza, void* const udata) {
+ XMPPClient& xc = *(XMPPClient*)udata;
+
+ // Build version reply stanza
+ xmpp_stanza_t* reply = namedStanza("iq", xc.ctx);
+ xmpp_stanza_set_type(reply, "result");
+ xmpp_stanza_set_id(reply, xmpp_stanza_get_id(stanza));
+ xmpp_stanza_set_attribute(reply, "to", xmpp_stanza_get_attribute(stanza, "from"));
+ xmpp_stanza_t* query = namedStanza(xmpp_stanza_get_ns(xmpp_stanza_get_children(stanza)), "query", xc.ctx);
+ xmpp_stanza_add_child(reply, query);
+ xmpp_stanza_t* name = namedStanza("name", xc.ctx);
+ xmpp_stanza_add_child(query, name);
+ xmpp_stanza_add_child(name, textStanza("Apache Tuscany", xc.ctx));
+ xmpp_stanza_t* version = namedStanza("version", xc.ctx);
+ xmpp_stanza_add_child(query, version);
+ xmpp_stanza_add_child(version, textStanza("1.0", xc.ctx));
+
+ // Send it
+ xmpp_send(conn, reply);
+ xmpp_stanza_release(reply);
+ return 1;
+}
+
+/**
+ * XMPP message handler
+ */
+int messageHandler(unused xmpp_conn_t* const conn, xmpp_stanza_t* const stanza, void* const udata) {
+ // Ignore noise
+ if(xmpp_stanza_get_child_by_name(stanza, "body") == NULL)
+ return 1;
+ if(!strcmp(xmpp_stanza_get_attribute(stanza, "type"), "error"))
+ return 1;
+
+ // Call the client listener function
+ XMPPClient& xc = *(XMPPClient*)udata;
+ const char* from = xmpp_stanza_get_attribute(stanza, "from");
+ const char* text = xmpp_stanza_get_text(xmpp_stanza_get_child_by_name(stanza, "body"));
+ if (isNil(xc.listener))
+ return 1;
+ const value val(scheme::readValue(text));
+ debug(from, "chat::messageHandler::from");
+ debug(val, "chat::messageHandler::body");
+ const failable<bool> r = xc.listener(value(string(from)), val, xc);
+ if (!hasContent(r) || !content(r)) {
+ // Stop listening
+ xc.listener = lambda<failable<bool>(const value&, const value&, XMPPClient&)>();
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ * XMPP connection handler.
+ */
+void connHandler(xmpp_conn_t * const conn, const xmpp_conn_event_t status, unused const int err, unused xmpp_stream_error_t* const errstream, void *const udata) {
+ XMPPClient& xc = *(XMPPClient*)udata;
+ xc.connecting = false;
+
+ if (status == XMPP_CONN_CONNECT) {
+ debug("chat::connHandler::connected");
+ xmpp_handler_add(conn, versionHandler, "jabber:iq:version", "iq", NULL, &xc);
+
+ // Send a <presence/> stanza so that we appear online to contacts
+ xmpp_stanza_t* pres = xmpp_stanza_new(xc.ctx);
+ xmpp_stanza_set_name(pres, "presence");
+ xmpp_send(conn, pres);
+ xmpp_stanza_release(pres);
+ xc.connected = true;
+ return;
+ }
+
+ debug("chat::connHandler::disconnected");
+ xc.connected = false;
+ if (xc.ctx->loop_status == XMPP_LOOP_RUNNING)
+ xc.ctx->loop_status = XMPP_LOOP_QUIT;
+}
+
+/**
+ * Connect to an XMPP server.
+ */
+const failable<bool> connect(XMPPClient& xc) {
+ xc.connecting = true;
+ xmpp_connect_client(xc.conn, NULL, 0, connHandler, &xc);
+ while(xc.connecting)
+ xmpp_run_once(xc.ctx, 20L);
+ if (!xc.connected)
+ return mkfailure<bool>("Couldn't connect to XMPP server");
+ return true;
+}
+
+/**
+ * Send a buffer on an XMPP session.
+ */
+const failable<size_t> send(const char* data, const size_t len, XMPPClient& xc) {
+ if (len == 0)
+ return 0;
+ const size_t written = xc.conn->tls? tls_write(xc.conn->tls, data, len) : sock_write(xc.conn->sock, data, len);
+ if (written == (size_t)-1) {
+ xc.conn->error = xc.conn->tls? tls_error(xc.conn->tls) : sock_error();
+ return mkfailure<size_t>("Couldn't send stanza to XMPP server");
+ }
+ return send(data + written, len - written, xc);
+}
+
+/**
+ * Send a string on an XMPP session.
+ */
+const failable<size_t> send(const string& data, XMPPClient& xc) {
+ return send(c_str(data), length(data), xc);
+}
+
+/**
+ * Send a stanza on an XMPP session.
+ */
+const failable<size_t> send(xmpp_stanza_t* const stanza, XMPPClient& xc) {
+ char *buf;
+ size_t len;
+ const int rc = xmpp_stanza_to_text(stanza, &buf, &len);
+ if (rc != 0)
+ return mkfailure<size_t>("Couldn't convert stanza to text");
+ const failable<size_t> r = send(buf, len, xc);
+ if (!hasContent(r)) {
+ xmpp_free(xc.conn->ctx, buf);
+ return r;
+ }
+ xmpp_debug(xc.conn->ctx, "conn", "SENT: %s", buf);
+ xmpp_free(xc.conn->ctx, buf);
+ return content(r);
+}
+
+/**
+ * Post a message to an XMPP jid.
+ */
+const failable<bool> post(const value& to, const value& val, XMPPClient& xc) {
+ debug(to, "chat::post::to");
+ debug(val, "chat::post::body");
+
+ // Convert the value to a string
+ const string vs(scheme::writeValue(val));
+
+ // Build message stanza
+ xmpp_stanza_t* stanza = namedStanza("message", xc.ctx);
+ xmpp_stanza_set_type(stanza, "chat");
+ xmpp_stanza_set_attribute(stanza, "to", c_str(string(to)));
+ xmpp_stanza_t* body = namedStanza("body", xc.ctx);
+ xmpp_stanza_add_child(stanza, body);
+ xmpp_stanza_add_child(body, textStanza(c_str(vs), xc.ctx));
+
+ // Send it
+ const failable<size_t> r = send(stanza, xc);
+ xmpp_stanza_release(stanza);
+ if (!hasContent(r))
+ return mkfailure<bool>(reason(r));
+ return true;
+}
+
+/**
+ * Disconnect an XMPP session.
+ */
+const failable<bool> disconnect(XMPPClient& xc) {
+ xc.disconnecting = true;
+ const failable<size_t> r = send("</stream:stream>", xc);
+ if (!hasContent(r))
+ return mkfailure<bool>(reason(r));
+ return true;
+}
+
+/**
+ * Listen to messages received by an XMPP client.
+ */
+const failable<bool> listen(const lambda<failable<bool>(const value&, const value&, XMPPClient&)>& listener, XMPPClient& xc) {
+ debug("chat::listen");
+ xc.listener = listener;
+ xmpp_handler_add(xc.conn, messageHandler, NULL, "message", NULL, &xc);
+ xc.ctx->loop_status = XMPP_LOOP_RUNNING;
+ while(xc.connected && !isNil(xc.listener) && xc.ctx->loop_status == XMPP_LOOP_RUNNING)
+ xmpp_run_once(xc.ctx, 1000L);
+ return true;
+}
+
+}
+}
+
+#endif /* tuscany_xmpp_hpp */