diff options
-rw-r--r-- | Sluift/client_test.lua | 10 | ||||
-rw-r--r-- | Sluift/multiple_client_test.lua | 26 | ||||
-rw-r--r-- | Sluift/sluift.cpp | 312 | ||||
-rw-r--r-- | Swiften/Roster/XMPPRoster.h | 6 | ||||
-rw-r--r-- | Swiften/Roster/XMPPRosterController.cpp | 9 | ||||
-rw-r--r-- | Swiften/Roster/XMPPRosterController.h | 2 |
6 files changed, 289 insertions, 76 deletions
diff --git a/Sluift/client_test.lua b/Sluift/client_test.lua index 4eebf0c..1652083 100644 --- a/Sluift/client_test.lua +++ b/Sluift/client_test.lua @@ -6,11 +6,13 @@ client2_jid = os.getenv("SWIFT_CLIENTTEST_JID") .. "/Client2" password = os.getenv("SWIFT_CLIENTTEST_PASS") print "Connecting client 1" -client1 = sluift.connect(client1_jid, password) +client1 = sluift.new_client(client1_jid, password) +client1:connect() client1:send_presence("I'm here") print "Connecting client 2" -client2 = sluift.connect(client2_jid, password) +client2 = sluift.new_client(client2_jid, password) +client2:connect() client2:send_presence("I'm here") print "Checking version of client 2 from client 1" @@ -28,5 +30,9 @@ received_message = client2:for_event(function(event) end) assert(received_message == "Hello") +print "Retrieving the roster" +roster = client1:get_roster() +table.foreach(roster, print) + client1:disconnect() client2:disconnect() diff --git a/Sluift/multiple_client_test.lua b/Sluift/multiple_client_test.lua new file mode 100644 index 0000000..d2b2cd7 --- /dev/null +++ b/Sluift/multiple_client_test.lua @@ -0,0 +1,26 @@ +require "sluift" + +-- sluift.debug = true +num_clients = 10 + +print("Connecting clients") +clients = {} +for i = 1, num_clients do + jid = os.getenv("SWIFT_CLIENTTEST_JID") .. "/Client" .. i + client = sluift.new_client(jid, os.getenv("SWIFT_CLIENTTEST_PASS")) + client:async_connect() + table.insert(clients, client) +end + +print("Waiting for clients to be connected") +for i, client in ipairs(clients) do + client:wait_connected() + client:send_presence("Hello") +end + +print("Disconnecting clients") +for i, client in ipairs(clients) do + client:disconnect() +end + +print("Done") diff --git a/Sluift/sluift.cpp b/Sluift/sluift.cpp index 84643b8..b3ca8dc 100644 --- a/Sluift/sluift.cpp +++ b/Sluift/sluift.cpp @@ -21,6 +21,7 @@ extern "C" { #include <Swiften/Base/sleep.h> #include <Swiften/Elements/SoftwareVersion.h> #include <Swiften/Queries/Requests/GetSoftwareVersionRequest.h> +#include <Swiften/Roster/XMPPRoster.h> using namespace Swift; @@ -39,13 +40,28 @@ bool debug = false; SimpleEventLoop eventLoop; BoostNetworkFactories networkFactories(&eventLoop); +class SluiftException { + public: + SluiftException(const std::string& reason) : reason(reason) { + } + + const std::string& getReason() const { + return reason; + } + + private: + std::string reason; +}; + class SluiftClient { public: SluiftClient(const JID& jid, const std::string& password) : tracer(NULL) { client = new Client(jid, password, &networkFactories); client->setAlwaysTrustCertificates(); + client->onDisconnected.connect(boost::bind(&SluiftClient::handleDisconnected, this, _1)); client->onMessageReceived.connect(boost::bind(&SluiftClient::handleIncomingEvent, this, _1)); client->onPresenceReceived.connect(boost::bind(&SluiftClient::handleIncomingEvent, this, _1)); + client->getRoster()->onInitialRosterPopulated.connect(boost::bind(&SluiftClient::handleInitialRosterPopulated, this)); if (debug) { tracer = new ClientXMLTracer(client); } @@ -57,7 +73,11 @@ class SluiftClient { } void connect() { + rosterReceived = false; client->connect(); + } + + void waitConnected() { while (client->isActive() && !client->isAvailable()) { processEvents(); } @@ -121,11 +141,23 @@ class SluiftClient { } } + std::vector<XMPPRosterItem> getRoster() { + client->requestRoster(); + while (!rosterReceived) { + processEvents(); + } + return client->getRoster()->getItems(); + } + private: void handleIncomingEvent(Stanza::ref stanza) { pendingEvents.push_back(stanza); } + void handleInitialRosterPopulated() { + rosterReceived = true; + } + void processEvents() { eventLoop.runUntilEvents(); } @@ -141,15 +173,57 @@ class SluiftClient { this->softwareVersion = SoftwareVersion("", "", ""); } } + + void handleDisconnected(const boost::optional<ClientError>& error) { + if (error) { + std::string reason("Disconnected: "); + switch(error->getType()) { + case ClientError::UnknownError: reason += "Unknown Error"; break; + case ClientError::DomainNameResolveError: reason += "Unable to find server"; break; + case ClientError::ConnectionError: reason += "Error connecting to server"; break; + case ClientError::ConnectionReadError: reason += "Error while receiving server data"; break; + case ClientError::ConnectionWriteError: reason += "Error while sending data to the server"; break; + case ClientError::XMLError: reason += "Error parsing server data"; break; + case ClientError::AuthenticationFailedError: reason += "Login/password invalid"; break; + case ClientError::CompressionFailedError: reason += "Error while compressing stream"; break; + case ClientError::ServerVerificationFailedError: reason += "Server verification failed"; break; + case ClientError::NoSupportedAuthMechanismsError: reason += "Authentication mechanisms not supported"; break; + case ClientError::UnexpectedElementError: reason += "Unexpected response"; break; + case ClientError::ResourceBindError: reason += "Error binding resource"; break; + case ClientError::SessionStartError: reason += "Error starting session"; break; + case ClientError::StreamError: reason += "Stream error"; break; + case ClientError::TLSError: reason += "Encryption error"; break; + case ClientError::ClientCertificateLoadError: reason += "Error loading certificate (Invalid password?)"; break; + case ClientError::ClientCertificateError: reason += "Certificate not authorized"; break; + case ClientError::UnknownCertificateError: reason += "Unknown certificate"; break; + case ClientError::CertificateExpiredError: reason += "Certificate has expired"; break; + case ClientError::CertificateNotYetValidError: reason += "Certificate is not yet valid"; break; + case ClientError::CertificateSelfSignedError: reason += "Certificate is self-signed"; break; + case ClientError::CertificateRejectedError: reason += "Certificate has been rejected"; break; + case ClientError::CertificateUntrustedError: reason += "Certificate is not trusted"; break; + case ClientError::InvalidCertificatePurposeError: reason += "Certificate cannot be used for encrypting your connection"; break; + case ClientError::CertificatePathLengthExceededError: reason += "Certificate path length constraint exceeded"; break; + case ClientError::InvalidCertificateSignatureError: reason += "Invalid certificate signature"; break; + case ClientError::InvalidCAError: reason += "Invalid Certificate Authority"; break; + case ClientError::InvalidServerIdentityError: reason += "Certificate does not match the host identity"; break; + } + throw SluiftException(reason); + } + } private: Client* client; ClientXMLTracer* tracer; + bool rosterReceived; boost::optional<SoftwareVersion> softwareVersion; ErrorPayload::ref error; std::deque<Stanza::ref> pendingEvents; }; +#define CHECK_CLIENT_CONNECTED(client, L) \ + if (!(*client)->isConnected()) { \ + lua_pushnil(L); \ + } /******************************************************************************* * Client functions. @@ -159,14 +233,51 @@ static inline SluiftClient* getClient(lua_State* L) { return *reinterpret_cast<SluiftClient**>(luaL_checkudata(L, 1, SLUIFT_CLIENT)); } +static int sluift_client_connect(lua_State *L) { + try { + SluiftClient* client = getClient(L); + client->connect(); + client->waitConnected(); + return 0; + } + catch (const SluiftException& e) { + return luaL_error(L, e.getReason().c_str()); + } +} + +static int sluift_client_async_connect(lua_State *L) { + try { + getClient(L)->connect(); + return 0; + } + catch (const SluiftException& e) { + return luaL_error(L, e.getReason().c_str()); + } +} + +static int sluift_client_wait_connected(lua_State *L) { + try { + getClient(L)->waitConnected(); + return 0; + } + catch (const SluiftException& e) { + return luaL_error(L, e.getReason().c_str()); + } +} + static int sluift_client_is_connected(lua_State *L) { lua_pushboolean(L, getClient(L)->isConnected()); return 1; } static int sluift_client_disconnect(lua_State *L) { - getClient(L)->disconnect(); - return 0; + try { + getClient(L)->disconnect(); + return 0; + } + catch (const SluiftException& e) { + return luaL_error(L, e.getReason().c_str()); + } } static int sluift_client_set_version(lua_State *L) { @@ -183,24 +294,80 @@ static int sluift_client_set_version(lua_State *L) { return 0; } -static int sluift_client_get_version(lua_State *L) { - SluiftClient* client = getClient(L); - JID jid(std::string(luaL_checkstring(L, 2))); +static int sluift_client_get_roster(lua_State *L) { + try { + SluiftClient* client = getClient(L); + std::vector<XMPPRosterItem> items = client->getRoster(); + + lua_createtable(L, 0, items.size()); + foreach(const XMPPRosterItem& item, items) { + lua_createtable(L, 0, 3); + + lua_pushstring(L, item.getName().c_str()); + lua_setfield(L, -2, "name"); + + std::string subscription; + switch(item.getSubscription()) { + case RosterItemPayload::None: subscription = "none"; break; + case RosterItemPayload::To: subscription = "to"; break; + case RosterItemPayload::From: subscription = "from"; break; + case RosterItemPayload::Both: subscription = "both"; break; + case RosterItemPayload::Remove: subscription = "remove"; break; + } + lua_pushstring(L, subscription.c_str()); + lua_setfield(L, -2, "subscription"); + + std::vector<std::string> groups = item.getGroups(); + lua_createtable(L, groups.size(), 0); + for (size_t i = 0; i < groups.size(); ++i) { + lua_pushstring(L, groups[i].c_str()); + lua_rawseti(L, -2, i + 1); + } + lua_setfield(L, -2, "groups"); - boost::optional<SoftwareVersion> version = client->getSoftwareVersion(jid); - if (version) { - lua_createtable(L, 0, 3); + lua_setfield(L, -2, item.getJID().toString().c_str()); + } + /*boost::optional<SoftwareVersion> version = client->getSoftwareVersion(jid); + if (version) { lua_pushstring(L, version->getName().c_str()); - lua_setfield(L, -2, "name"); - lua_pushstring(L, version->getVersion().c_str()); - lua_setfield(L, -2, "version"); - lua_pushstring(L, version->getOS().c_str()); - lua_setfield(L, -2, "os"); + lua_pushstring(L, version->getName().c_str()); + lua_setfield(L, -2, "name"); + lua_pushstring(L, version->getVersion().c_str()); + lua_setfield(L, -2, "version"); + lua_pushstring(L, version->getOS().c_str()); + lua_setfield(L, -2, "os"); + } + */ + return 1; } - else { - lua_pushnil(L); + catch (const SluiftException& e) { + return luaL_error(L, e.getReason().c_str()); + } +} + +static int sluift_client_get_version(lua_State *L) { + try { + SluiftClient* client = getClient(L); + JID jid(std::string(luaL_checkstring(L, 2))); + + boost::optional<SoftwareVersion> version = client->getSoftwareVersion(jid); + if (version) { + lua_createtable(L, 0, 3); + lua_pushstring(L, version->getName().c_str()); + lua_setfield(L, -2, "name"); + lua_pushstring(L, version->getVersion().c_str()); + lua_setfield(L, -2, "version"); + lua_pushstring(L, version->getOS().c_str()); + lua_setfield(L, -2, "os"); + } + else { + lua_pushnil(L); + } + return 1; + } + catch (const SluiftException& e) { + return luaL_error(L, e.getReason().c_str()); } - return 1; } static int sluift_client_send_message(lua_State *L) { @@ -214,60 +381,65 @@ static int sluift_client_send_presence(lua_State *L) { } static int sluift_client_for_event (lua_State *L) { - SluiftClient* client = getClient(L); - luaL_checktype(L, 2, LUA_TFUNCTION); - while (true) { - Stanza::ref event = client->getNextEvent(); - if (!event) { - // We got disconnected - lua_pushnil(L); - lua_pushliteral(L, "disconnected"); - return 2; - } - else { - // Push the function on the stack - lua_pushvalue(L, 2); - - bool emitEvent = false; - if (Message::ref message = boost::dynamic_pointer_cast<Message>(event)) { - lua_createtable(L, 0, 3); - lua_pushliteral(L, "message"); - lua_setfield(L, -2, "type"); - lua_pushstring(L, message->getFrom().toString().c_str()); - lua_setfield(L, -2, "from"); - lua_pushstring(L, message->getBody().c_str()); - lua_setfield(L, -2, "body"); - emitEvent = true; - } - else if (Presence::ref presence = boost::dynamic_pointer_cast<Presence>(event)) { - lua_createtable(L, 0, 3); - lua_pushliteral(L, "presence"); - lua_setfield(L, -2, "type"); - lua_pushstring(L, presence->getFrom().toString().c_str()); - lua_setfield(L, -2, "from"); - lua_pushstring(L, presence->getStatus().c_str()); - lua_setfield(L, -2, "status"); - emitEvent = true; + try { + SluiftClient* client = getClient(L); + luaL_checktype(L, 2, LUA_TFUNCTION); + while (true) { + Stanza::ref event = client->getNextEvent(); + if (!event) { + // We got disconnected + lua_pushnil(L); + lua_pushliteral(L, "disconnected"); + return 2; } else { - assert(false); - } - if (emitEvent) { - int oldTop = lua_gettop(L) - 2; - lua_call(L, 1, LUA_MULTRET); - int returnValues = lua_gettop(L) - oldTop; - if (returnValues > 0) { - lua_remove(L, -1 - returnValues); - return returnValues; + // Push the function on the stack + lua_pushvalue(L, 2); + + bool emitEvent = false; + if (Message::ref message = boost::dynamic_pointer_cast<Message>(event)) { + lua_createtable(L, 0, 3); + lua_pushliteral(L, "message"); + lua_setfield(L, -2, "type"); + lua_pushstring(L, message->getFrom().toString().c_str()); + lua_setfield(L, -2, "from"); + lua_pushstring(L, message->getBody().c_str()); + lua_setfield(L, -2, "body"); + emitEvent = true; + } + else if (Presence::ref presence = boost::dynamic_pointer_cast<Presence>(event)) { + lua_createtable(L, 0, 3); + lua_pushliteral(L, "presence"); + lua_setfield(L, -2, "type"); + lua_pushstring(L, presence->getFrom().toString().c_str()); + lua_setfield(L, -2, "from"); + lua_pushstring(L, presence->getStatus().c_str()); + lua_setfield(L, -2, "status"); + emitEvent = true; + } + else { + assert(false); + } + if (emitEvent) { + int oldTop = lua_gettop(L) - 2; + lua_call(L, 1, LUA_MULTRET); + int returnValues = lua_gettop(L) - oldTop; + if (returnValues > 0) { + lua_remove(L, -1 - returnValues); + return returnValues; + } + } + else { + // Remove the function from the stack again, since + // we're not calling the function + lua_pop(L, 1); } - } - else { - // Remove the function from the stack again, since - // we're not calling the function - lua_pop(L, 1); } } } + catch (const SluiftException& e) { + return luaL_error(L, e.getReason().c_str()); + } } static int sluift_client_gc (lua_State *L) { @@ -278,11 +450,15 @@ static int sluift_client_gc (lua_State *L) { static const luaL_reg sluift_client_functions[] = { + {"connect", sluift_client_connect}, + {"async_connect", sluift_client_async_connect}, + {"wait_connected", sluift_client_wait_connected}, {"is_connected", sluift_client_is_connected}, {"disconnect", sluift_client_disconnect}, {"send_message", sluift_client_send_message}, {"send_presence", sluift_client_send_presence}, {"set_version", sluift_client_set_version}, + {"get_roster", sluift_client_get_roster}, {"get_version", sluift_client_get_version}, {"for_event", sluift_client_for_event}, {"__gc", sluift_client_gc}, @@ -293,7 +469,7 @@ static const luaL_reg sluift_client_functions[] = { * Module functions ******************************************************************************/ -static int sluift_connect(lua_State *L) { +static int sluift_new_client(lua_State *L) { JID jid(std::string(luaL_checkstring(L, 1))); std::string password(luaL_checkstring(L, 2)); @@ -302,10 +478,6 @@ static int sluift_connect(lua_State *L) { lua_setmetatable(L, -2); *client = new SluiftClient(jid, password); - (*client)->connect(); - if (!(*client)->isConnected()) { - lua_pushnil(L); - } return 1; } @@ -332,7 +504,7 @@ static int sluift_newindex(lua_State *L) { } static const luaL_reg sluift_functions[] = { - {"connect", sluift_connect}, + {"new_client", sluift_new_client}, {NULL, NULL} }; diff --git a/Swiften/Roster/XMPPRoster.h b/Swiften/Roster/XMPPRoster.h index 958c1f6..be3494d 100644 --- a/Swiften/Roster/XMPPRoster.h +++ b/Swiften/Roster/XMPPRoster.h @@ -86,5 +86,11 @@ namespace Swift { * onJIDAdded and onJIDRemoved events. */ boost::signal<void ()> onRosterCleared; + + /** + * Emitted after the last contact of the initial roster request response + * was added. + */ + boost::signal<void ()> onInitialRosterPopulated; }; } diff --git a/Swiften/Roster/XMPPRosterController.cpp b/Swiften/Roster/XMPPRosterController.cpp index 3a1d11f..a294d35 100644 --- a/Swiften/Roster/XMPPRosterController.cpp +++ b/Swiften/Roster/XMPPRosterController.cpp @@ -20,7 +20,7 @@ namespace Swift { * The controller does not gain ownership of these parameters. */ XMPPRosterController::XMPPRosterController(IQRouter* iqRouter, XMPPRosterImpl* xmppRoster) : iqRouter_(iqRouter), rosterPushResponder_(iqRouter), xmppRoster_(xmppRoster) { - rosterPushResponder_.onRosterReceived.connect(boost::bind(&XMPPRosterController::handleRosterReceived, this, _1)); + rosterPushResponder_.onRosterReceived.connect(boost::bind(&XMPPRosterController::handleRosterReceived, this, _1, false)); rosterPushResponder_.start(); } @@ -31,11 +31,11 @@ XMPPRosterController::~XMPPRosterController() { void XMPPRosterController::requestRoster() { xmppRoster_->clear(); GetRosterRequest::ref rosterRequest = GetRosterRequest::create(iqRouter_); - rosterRequest->onResponse.connect(boost::bind(&XMPPRosterController::handleRosterReceived, this, _1)); + rosterRequest->onResponse.connect(boost::bind(&XMPPRosterController::handleRosterReceived, this, _1, true)); rosterRequest->send(); } -void XMPPRosterController::handleRosterReceived(boost::shared_ptr<RosterPayload> rosterPayload) { +void XMPPRosterController::handleRosterReceived(boost::shared_ptr<RosterPayload> rosterPayload, bool initial) { if (rosterPayload) { foreach(const RosterItemPayload& item, rosterPayload->getItems()) { //Don't worry about the updated case, the XMPPRoster sorts that out. @@ -46,6 +46,9 @@ void XMPPRosterController::handleRosterReceived(boost::shared_ptr<RosterPayload> } } } + if (initial) { + xmppRoster_->onInitialRosterPopulated(); + } } } diff --git a/Swiften/Roster/XMPPRosterController.h b/Swiften/Roster/XMPPRosterController.h index 28c2541..eeb84f6 100644 --- a/Swiften/Roster/XMPPRosterController.h +++ b/Swiften/Roster/XMPPRosterController.h @@ -27,7 +27,7 @@ namespace Swift { void requestRoster(); private: - void handleRosterReceived(boost::shared_ptr<RosterPayload> rosterPayload); + void handleRosterReceived(boost::shared_ptr<RosterPayload> rosterPayload, bool initial); private: IQRouter* iqRouter_; |