diff options
-rw-r--r-- | Sluift/SluiftClient.cpp | 35 | ||||
-rw-r--r-- | Sluift/SluiftClient.h | 16 | ||||
-rw-r--r-- | Sluift/client.cpp | 37 |
3 files changed, 83 insertions, 5 deletions
diff --git a/Sluift/SluiftClient.cpp b/Sluift/SluiftClient.cpp index f1c0191..4680d4b 100644 --- a/Sluift/SluiftClient.cpp +++ b/Sluift/SluiftClient.cpp @@ -1,84 +1,89 @@ /* - * Copyright (c) 2013-2016 Isode Limited. + * Copyright (c) 2013-2017 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #include <Sluift/SluiftClient.h> #include <boost/numeric/conversion/cast.hpp> #include <Swiften/Client/Client.h> +#include <Swiften/Client/ClientBlockListManager.h> #include <Swiften/Client/ClientXMLTracer.h> #include <Swiften/Elements/Message.h> #include <Swiften/Elements/Presence.h> #include <Swiften/Elements/PubSubEvent.h> #include <Swiften/Queries/RawRequest.h> #include <Swiften/Roster/XMPPRoster.h> #include <Sluift/Helpers.h> #include <Sluift/Lua/Exception.h> #include <Sluift/SluiftGlobals.h> using namespace Swift; SluiftClient::SluiftClient( const JID& jid, const std::string& password, NetworkFactories* networkFactories, SimpleEventLoop* eventLoop) : networkFactories(networkFactories), eventLoop(eventLoop), tracer(nullptr) { client = new Client(jid, password, networkFactories); client->setAlwaysTrustCertificates(); client->onDisconnected.connect(boost::bind(&SluiftClient::handleDisconnected, this, _1)); client->onMessageReceived.connect(boost::bind(&SluiftClient::handleIncomingMessage, this, _1)); client->onPresenceReceived.connect(boost::bind(&SluiftClient::handleIncomingPresence, this, _1)); client->getPubSubManager()->onEvent.connect(boost::bind(&SluiftClient::handleIncomingPubSubEvent, this, _1, _2)); client->getRoster()->onInitialRosterPopulated.connect(boost::bind(&SluiftClient::handleInitialRosterPopulated, this)); + client->getClientBlockListManager()->getBlockList()->onItemAdded.connect(boost::bind(&SluiftClient::handleIncomingBlockEvent, this, _1)); + client->getClientBlockListManager()->getBlockList()->onItemRemoved.connect(boost::bind(&SluiftClient::handleIncomingUnblockEvent, this, _1)); } SluiftClient::~SluiftClient() { delete tracer; delete client; } void SluiftClient::connect() { rosterReceived = false; + blockListReceived = false; disconnectedError = boost::optional<ClientError>(); client->connect(options); } void SluiftClient::connect(const std::string& host, int port) { rosterReceived = false; + blockListReceived = false; options.manualHostname = host; options.manualPort = port; disconnectedError = boost::optional<ClientError>(); client->connect(options); } void SluiftClient::setTraceEnabled(bool b) { if (b && !tracer) { tracer = new ClientXMLTracer(client, options.boshURL.isEmpty()? false: true); } else if (!b && tracer) { delete tracer; tracer = nullptr; } } void SluiftClient::waitConnected(int timeout) { Watchdog watchdog(timeout, networkFactories->getTimerFactory()); while (!watchdog.getTimedOut() && client->isActive() && !client->isAvailable()) { eventLoop->runUntilEvents(); } if (watchdog.getTimedOut()) { client->disconnect(); throw Lua::Exception("Timeout while connecting"); } if (disconnectedError) { throw Lua::Exception(getErrorString(*disconnectedError)); } } @@ -99,92 +104,120 @@ void SluiftClient::setSoftwareVersion(const std::string& name, const std::string boost::optional<SluiftClient::Event> SluiftClient::getNextEvent( int timeout, boost::function<bool (const Event&)> condition) { Watchdog watchdog(timeout, networkFactories->getTimerFactory()); size_t currentIndex = 0; while (true) { // Look for pending events in the queue while (currentIndex < pendingEvents.size()) { Event event = pendingEvents[currentIndex]; if (!condition || condition(event)) { pendingEvents.erase( pendingEvents.begin() + boost::numeric_cast<int>(currentIndex)); return event; } ++currentIndex; } // Wait for new events while (!watchdog.getTimedOut() && currentIndex >= pendingEvents.size() && client->isActive()) { eventLoop->runUntilEvents(); } // Finish if we're disconnected or timed out if (watchdog.getTimedOut() || !client->isActive()) { return boost::optional<Event>(); } } } +std::vector<JID> SluiftClient::getBlockList(int timeout) { + Watchdog watchdog(timeout, networkFactories->getTimerFactory()); + if (!blockListReceived) { + // If we haven't requested it yet, request it for the first time + client->getClientBlockListManager()->requestBlockList(); + + // Wait for new events + while (!watchdog.getTimedOut() && client->getClientBlockListManager()->getBlockList()->getState() != BlockList::Available) { + eventLoop->runUntilEvents(); + } + + // Throw an error if we're timed out + if (watchdog.getTimedOut()) { + throw Lua::Exception("Timeout while requesting blocklist"); + } + } + blockListReceived = true; + return client->getClientBlockListManager()->getBlockList()->getItems(); +} + std::vector<XMPPRosterItem> SluiftClient::getRoster(int timeout) { Watchdog watchdog(timeout, networkFactories->getTimerFactory()); if (!rosterReceived) { // If we haven't requested it yet, request it for the first time client->requestRoster(); // Wait for new events while (!watchdog.getTimedOut() && !rosterReceived) { eventLoop->runUntilEvents(); } // Throw an error if we're timed out if (watchdog.getTimedOut()) { throw Lua::Exception("Timeout while requesting roster"); } } return client->getRoster()->getItems(); } void SluiftClient::handleIncomingMessage(std::shared_ptr<Message> stanza) { if (stanza->getPayload<PubSubEvent>()) { // Already handled by pubsub manager return; } pendingEvents.push_back(Event(stanza)); } void SluiftClient::handleIncomingPresence(std::shared_ptr<Presence> stanza) { pendingEvents.push_back(Event(stanza)); } void SluiftClient::handleIncomingPubSubEvent(const JID& from, std::shared_ptr<PubSubEventPayload> event) { pendingEvents.push_back(Event(from, event)); } +void SluiftClient::handleIncomingBlockEvent(const JID& item) { + pendingEvents.push_back(Event(item, Event::BlockEventType)); +} + +void SluiftClient::handleIncomingUnblockEvent(const JID& item) { + pendingEvents.push_back(Event(item, Event::UnblockEventType)); +} + void SluiftClient::handleInitialRosterPopulated() { rosterReceived = true; } void SluiftClient::handleRequestResponse(std::shared_ptr<Payload> response, std::shared_ptr<ErrorPayload> error) { requestResponse = response; requestError = error; requestResponseReceived = true; } void SluiftClient::handleDisconnected(const boost::optional<ClientError>& error) { disconnectedError = error; } Sluift::Response SluiftClient::doSendRequest(std::shared_ptr<Request> request, int timeout) { requestResponse.reset(); requestError.reset(); requestResponseReceived = false; request->send(); Watchdog watchdog(timeout, networkFactories->getTimerFactory()); while (!watchdog.getTimedOut() && !requestResponseReceived) { eventLoop->runUntilEvents(); } return Sluift::Response(requestResponse, watchdog.getTimedOut() ? std::make_shared<ErrorPayload>(ErrorPayload::RemoteServerTimeout) : requestError); } diff --git a/Sluift/SluiftClient.h b/Sluift/SluiftClient.h index 2c221e6..a48c681 100644 --- a/Sluift/SluiftClient.h +++ b/Sluift/SluiftClient.h @@ -1,133 +1,143 @@ /* - * Copyright (c) 2013-2016 Isode Limited. + * Copyright (c) 2013-2017 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #pragma once #include <deque> #include <boost/bind.hpp> #include <boost/function.hpp> #include <boost/optional.hpp> #include <Swiften/Client/Client.h> +#include <Swiften/Client/ClientBlockListManager.h> #include <Swiften/Client/ClientError.h> #include <Swiften/Client/ClientOptions.h> #include <Swiften/Elements/IQ.h> #include <Swiften/Elements/Message.h> #include <Swiften/Elements/Presence.h> #include <Swiften/EventLoop/SimpleEventLoop.h> #include <Swiften/Network/NetworkFactories.h> #include <Swiften/PubSub/PubSubManager.h> #include <Swiften/Queries/GenericRequest.h> #include <Swiften/Roster/XMPPRosterItem.h> #include <Sluift/Response.h> #include <Sluift/Watchdog.h> namespace Swift { struct SluiftGlobals; class ClientXMLTracer; class Client; class Stanza; class Payload; class ErrorPayload; class JID; class SluiftClient { public: struct Event { enum Type { MessageType, PresenceType, - PubSubEventType + PubSubEventType, + BlockEventType, + UnblockEventType }; Event(std::shared_ptr<Message> stanza) : type(MessageType), stanza(stanza) {} Event(std::shared_ptr<Presence> stanza) : type(PresenceType), stanza(stanza) {} Event(const JID& from, std::shared_ptr<PubSubEventPayload> payload) : type(PubSubEventType), from(from), pubsubEvent(payload) {} - + Event(const JID& item, Type type) : type(type), item(item) {} Type type; // Message & Presence std::shared_ptr<Stanza> stanza; // PubSubEvent JID from; std::shared_ptr<PubSubEventPayload> pubsubEvent; + + // Blocklist + JID item; }; SluiftClient( const JID& jid, const std::string& password, NetworkFactories* networkFactories, SimpleEventLoop* eventLoop); ~SluiftClient(); Client* getClient() { return client; } ClientOptions& getOptions() { return options; } void connect(); void connect(const std::string& host, int port); void waitConnected(int timeout); bool isConnected() const; void setTraceEnabled(bool b); template<typename T> Sluift::Response sendPubSubRequest( IQ::Type type, const JID& jid, std::shared_ptr<T> payload, int timeout) { return sendRequest(client->getPubSubManager()->createRequest( type, jid, payload), timeout); } template<typename REQUEST_TYPE> Sluift::Response sendRequest(REQUEST_TYPE request, int timeout) { boost::signals2::scoped_connection c(request->onResponse.connect( boost::bind(&SluiftClient::handleRequestResponse, this, _1, _2))); return doSendRequest(request, timeout); } template<typename REQUEST_TYPE> Sluift::Response sendVoidRequest(REQUEST_TYPE request, int timeout) { boost::signals2::scoped_connection c(request->onResponse.connect( boost::bind(&SluiftClient::handleRequestResponse, this, std::shared_ptr<Payload>(), _1))); return doSendRequest(request, timeout); } void disconnect(); void setSoftwareVersion(const std::string& name, const std::string& version, const std::string& os); boost::optional<SluiftClient::Event> getNextEvent(int timeout, boost::function<bool (const Event&)> condition = 0); std::vector<XMPPRosterItem> getRoster(int timeout); + std::vector<JID> getBlockList(int timeout); private: Sluift::Response doSendRequest(std::shared_ptr<Request> request, int timeout); void handleIncomingMessage(std::shared_ptr<Message> stanza); void handleIncomingPresence(std::shared_ptr<Presence> stanza); void handleIncomingPubSubEvent(const JID& from, std::shared_ptr<PubSubEventPayload> event); + void handleIncomingBlockEvent(const JID& item); + void handleIncomingUnblockEvent(const JID& item); void handleInitialRosterPopulated(); void handleRequestResponse(std::shared_ptr<Payload> response, std::shared_ptr<ErrorPayload> error); void handleDisconnected(const boost::optional<ClientError>& error); private: NetworkFactories* networkFactories; SimpleEventLoop* eventLoop; Client* client; ClientOptions options; ClientXMLTracer* tracer; bool rosterReceived = false; + bool blockListReceived = false; std::deque<Event> pendingEvents; boost::optional<ClientError> disconnectedError; bool requestResponseReceived = false; std::shared_ptr<Payload> requestResponse; std::shared_ptr<ErrorPayload> requestError; }; } diff --git a/Sluift/client.cpp b/Sluift/client.cpp index 186effc..53e253f 100644 --- a/Sluift/client.cpp +++ b/Sluift/client.cpp @@ -1,32 +1,32 @@ /* - * Copyright (c) 2013-2016 Isode Limited. + * Copyright (c) 2013-2017 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #include <boost/assign/list_of.hpp> #include <boost/lambda/bind.hpp> #include <boost/lambda/lambda.hpp> #include <Swiften/Base/IDGenerator.h> #include <Swiften/Disco/ClientDiscoManager.h> #include <Swiften/Elements/DiscoInfo.h> #include <Swiften/Elements/Message.h> #include <Swiften/Elements/Presence.h> #include <Swiften/Elements/RawXMLPayload.h> #include <Swiften/Elements/RosterItemPayload.h> #include <Swiften/Elements/RosterPayload.h> #include <Swiften/Elements/SoftwareVersion.h> #include <Swiften/JID/JID.h> #include <Swiften/Presence/PresenceSender.h> #include <Swiften/Presence/SubscriptionManager.h> #include <Swiften/Queries/GenericRequest.h> #include <Swiften/Queries/IQRouter.h> #include <Swiften/Queries/Requests/GetSoftwareVersionRequest.h> #include <Swiften/Roster/SetRosterRequest.h> #include <Swiften/Roster/XMPPRoster.h> #include <Swiften/Roster/XMPPRosterItem.h> #include <Swiften/TLS/PKCS12Certificate.h> #include <Sluift/ElementConvertors/IQConvertor.h> #include <Sluift/ElementConvertors/MessageConvertor.h> @@ -179,60 +179,78 @@ SLUIFT_LUA_FUNCTION_WITH_HELP( "Returns a table of all the contacts in the contact list.", "self\n", "" ) { Sluift::globals.eventLoop.runOnce(); SluiftClient* client = getClient(L); Lua::Table contactsTable; for (const auto& item : client->getRoster(getGlobalTimeout(L))) { 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::Table itemTable = boost::assign::map_list_of ("jid", std::make_shared<Lua::Value>(item.getJID().toString())) ("name", std::make_shared<Lua::Value>(item.getName())) ("subscription", std::make_shared<Lua::Value>(subscription)) ("groups", std::make_shared<Lua::Value>(std::vector<Lua::Value>(item.getGroups().begin(), item.getGroups().end()))); contactsTable[item.getJID().toString()] = std::make_shared<Lua::Value>(itemTable); } pushValue(L, contactsTable); Lua::registerTableToString(L, -1); return 1; } SLUIFT_LUA_FUNCTION_WITH_HELP( + Client, get_block_list, + "Returns a table of all the items in the blocking list.", + "self\n", + "" +) { + Sluift::globals.eventLoop.runOnce(); + SluiftClient* client = getClient(L); + lua_newtable(L); + int i = 0; + for (const auto& item : client->getBlockList(getGlobalTimeout(L))) { + lua_pushstring(L, item.toString().c_str()); + lua_rawseti(L, -2, boost::numeric_cast<int>(++i)); + } + Lua::registerTableToString(L, -1); + return 1; +} + +SLUIFT_LUA_FUNCTION_WITH_HELP( Client, send_message, "Send a message.", "self\n" "to the JID to send the message to\n" "body the body of the message. Can alternatively be specified using the `body` option\n", "to the JID to send the message to\n" "body the body of the message\n" "subject the subject of the MUC room to set\n" "type the type of message to send (`normal`, `chat`, `error`, `groupchat`, `headline`)\n" "payloads payloads to add to the message\n" ) { Sluift::globals.eventLoop.runOnce(); JID to; boost::optional<std::string> body; boost::optional<std::string> subject; std::vector<std::shared_ptr<Payload> > payloads; int index = 2; Message::Type type = Message::Chat; if (lua_isstring(L, index)) { to = std::string(lua_tostring(L, index)); ++index; if (lua_isstring(L, index)) { body = lua_tostring(L, index); ++index; } } if (lua_istable(L, index)) { if (boost::optional<std::string> value = Lua::getStringField(L, index, "to")) { to = *value; @@ -491,60 +509,77 @@ static void pushEvent(lua_State* L, const SluiftClient::Event& event) { Lua::pushValue(L, result); addPayloadsToTable(L, message->getPayloads()); Lua::registerTableToString(L, -1); break; } case SluiftClient::Event::PresenceType: { Presence::ref presence = std::dynamic_pointer_cast<Presence>(event.stanza); Lua::Table result = boost::assign::map_list_of ("type", std::make_shared<Lua::Value>(std::string("presence"))) ("from", std::make_shared<Lua::Value>(presence->getFrom().toString())) ("to", std::make_shared<Lua::Value>(presence->getTo().toString())) ("status", std::make_shared<Lua::Value>(presence->getStatus())) ("presence_type", std::make_shared<Lua::Value>(PresenceConvertor::convertPresenceTypeToString(presence->getType()))); Lua::pushValue(L, result); addPayloadsToTable(L, presence->getPayloads()); Lua::registerTableToString(L, -1); break; } case SluiftClient::Event::PubSubEventType: { Sluift::globals.elementConvertor.convertToLua(L, event.pubsubEvent); lua_pushstring(L, "pubsub"); lua_setfield(L, -2, "type"); lua_pushstring(L, event.from.toString().c_str()); lua_setfield(L, -2, "from"); lua_rawgeti(L, LUA_REGISTRYINDEX, Sluift::globals.coreLibIndex); lua_getfield(L, -1, "process_pubsub_event"); lua_pushvalue(L, -3); lua_call(L, 1, 0); lua_pop(L, 1); + break; + } + case SluiftClient::Event::BlockEventType: { + Lua::Table result = boost::assign::map_list_of + ("type", std::make_shared<Lua::Value>(std::string("block"))) + ("jid", std::make_shared<Lua::Value>(event.item.toString())); + Lua::pushValue(L, result); + Lua::registerTableToString(L, -1); + break; + } + case SluiftClient::Event::UnblockEventType: { + Lua::Table result = boost::assign::map_list_of + ("type", std::make_shared<Lua::Value>(std::string("unblock"))) + ("jid", std::make_shared<Lua::Value>(event.item.toString())); + Lua::pushValue(L, result); + Lua::registerTableToString(L, -1); + break; } } } struct CallUnaryLuaPredicateOnEvent { CallUnaryLuaPredicateOnEvent(lua_State* L, int index) : L(L), index(index) { } bool operator()(const SluiftClient::Event& event) { lua_pushvalue(L, index); pushEvent(L, event); if (lua_pcall(L, 1, 1, 0) != 0) { throw Lua::Exception(lua_tostring(L, -1)); } bool result = lua_toboolean(L, -1); lua_pop(L, 1); return result; } lua_State* L; int index; }; SLUIFT_LUA_FUNCTION(Client, get_next_event) { Sluift::globals.eventLoop.runOnce(); SluiftClient* client = getClient(L); int timeout = getGlobalTimeout(L); boost::optional<SluiftClient::Event::Type> type; |