/* * Copyright (c) 2014 Kevin Smith and Remko Tronçon * Licensed under the GNU General Public License. * See the COPYING file for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Swift; namespace lambda = boost::lambda; static inline SluiftComponent* getComponent(lua_State* L) { return *Lua::checkUserData(L, 1); } static inline int getGlobalTimeout(lua_State* L) { lua_rawgeti(L, LUA_REGISTRYINDEX, Sluift::globals.moduleLibIndex); lua_getfield(L, -1, "timeout"); int result = boost::numeric_cast(lua_tointeger(L, -1)); lua_pop(L, 2); return result; } static void addPayloadsToTable(lua_State* L, const std::vector >& payloads) { if (!payloads.empty()) { lua_createtable(L, boost::numeric_cast(payloads.size()), 0); for (size_t i = 0; i < payloads.size(); ++i) { Sluift::globals.elementConvertor.convertToLua(L, payloads[i]); lua_rawseti(L, -2, boost::numeric_cast(i+1)); } Lua::registerGetByTypeIndex(L, -1); lua_setfield(L, -2, "payloads"); } } static boost::shared_ptr getPayload(lua_State* L, int index) { if (lua_type(L, index) == LUA_TTABLE) { return boost::dynamic_pointer_cast(Sluift::globals.elementConvertor.convertFromLua(L, index)); } else if (lua_type(L, index) == LUA_TSTRING) { return boost::make_shared(Lua::checkString(L, index)); } else { return boost::shared_ptr(); } } static std::vector< boost::shared_ptr > getPayloadsFromTable(lua_State* L, int index) { index = Lua::absoluteOffset(L, index); std::vector< boost::shared_ptr > result; lua_getfield(L, index, "payloads"); if (lua_istable(L, -1)) { for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { boost::shared_ptr payload = getPayload(L, -1); if (payload) { result.push_back(payload); } } } lua_pop(L, 1); return result; } SLUIFT_LUA_FUNCTION(Component, async_connect) { SluiftComponent* component = getComponent(L); std::string host; int port = 0; if (lua_istable(L, 2)) { if (boost::optional hostString = Lua::getStringField(L, 2, "host")) { host = *hostString; } if (boost::optional portInt = Lua::getIntField(L, 2, "port")) { port = *portInt; } } component->connect(host, port); return 0; } SLUIFT_LUA_FUNCTION_WITH_HELP( Component, set_trace_enabled, "Enable/disable tracing of the data sent/received.\n\n.", "self\n" "enable a boolean specifying whether to enable/disable tracing", "" ) { getComponent(L)->setTraceEnabled(lua_toboolean(L, 1)); return 0; } SLUIFT_LUA_FUNCTION_WITH_HELP( Component, wait_connected, "Block until the component is connected.\n\nThis is useful after an `async_connect`.", "self", "" ) { getComponent(L)->waitConnected(getGlobalTimeout(L)); return 0; } SLUIFT_LUA_FUNCTION_WITH_HELP( Component, is_connected, "Checks whether this component is still connected.\n\nReturns a boolean.", "self\n", "" ) { lua_pushboolean(L, getComponent(L)->isConnected()); return 1; } SLUIFT_LUA_FUNCTION_WITH_HELP( Component, disconnect, "Disconnect from the server", "self\n", "" ) { Sluift::globals.eventLoop.runOnce(); getComponent(L)->disconnect(); return 0; } SLUIFT_LUA_FUNCTION_WITH_HELP( Component, set_version, "Sets the published version of this component.", "self", "name the name of the component software\n" "version the version identifier of this component\n" "os the OS this component is running on\n" ) { Sluift::globals.eventLoop.runOnce(); SluiftComponent* component = getComponent(L); if (boost::shared_ptr version = boost::dynamic_pointer_cast(Sluift::globals.elementConvertor.convertFromLuaUntyped(L, 2, "software_version"))) { component->setSoftwareVersion(version->getName(), version->getVersion(), version->getOS()); } return 0; } SLUIFT_LUA_FUNCTION_WITH_HELP( Component, 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 from; boost::optional body; boost::optional subject; std::vector > 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 value = Lua::getStringField(L, index, "to")) { to = *value; } if (boost::optional value = Lua::getStringField(L, index, "from")) { from = value; } if (boost::optional value = Lua::getStringField(L, index, "body")) { body = value; } if (boost::optional value = Lua::getStringField(L, index, "type")) { type = MessageConvertor::convertMessageTypeFromString(*value); } if (boost::optional value = Lua::getStringField(L, index, "subject")) { subject = value; } payloads = getPayloadsFromTable(L, index); } if (!to.isValid()) { throw Lua::Exception("Missing 'to'"); } if ((!body || body->empty()) && !subject && payloads.empty()) { throw Lua::Exception("Missing any of 'body', 'subject' or 'payloads'"); } Message::ref message = boost::make_shared(); message->setTo(to); if (from && !from->empty()) { message->setFrom(*from); } if (body && !body->empty()) { message->setBody(*body); } if (subject) { message->setSubject(*subject); } message->addPayloads(payloads.begin(), payloads.end()); message->setType(type); getComponent(L)->getComponent()->sendMessage(message); return 0; } SLUIFT_LUA_FUNCTION_WITH_HELP( Component, send_presence, "Send presence.", "self\n" "body the text of the presence. Can alternatively be specified using the `status` option\n", "to the JID to send the message to\n" "from the JID to send the message from\n" "status the text of the presence\n" "priority the priority of the presence\n" "type the type of message to send (`available`, `error`, `probe`, `subscribe`, `subscribed`, `unavailable`, `unsubscribe`, `unsubscribed`)\n" "payloads payloads to add to the presence\n" ) { Sluift::globals.eventLoop.runOnce(); boost::shared_ptr presence = boost::make_shared(); int index = 2; if (lua_isstring(L, index)) { presence->setStatus(lua_tostring(L, index)); ++index; } if (lua_istable(L, index)) { if (boost::optional value = Lua::getStringField(L, index, "to")) { presence->setTo(*value); } if (boost::optional value = Lua::getStringField(L, index, "from")) { presence->setFrom(*value); } if (boost::optional value = Lua::getStringField(L, index, "status")) { presence->setStatus(*value); } if (boost::optional value = Lua::getIntField(L, index, "priority")) { presence->setPriority(*value); } if (boost::optional value = Lua::getStringField(L, index, "type")) { presence->setType(PresenceConvertor::convertPresenceTypeFromString(*value)); } std::vector< boost::shared_ptr > payloads = getPayloadsFromTable(L, index); presence->addPayloads(payloads.begin(), payloads.end()); } getComponent(L)->getComponent()->sendPresence(presence); lua_pushvalue(L, 1); return 0; } static int sendQuery(lua_State* L, IQ::Type type) { SluiftComponent* component = getComponent(L); JID to; if (boost::optional toString = Lua::getStringField(L, 2, "to")) { to = JID(*toString); } JID from; if (boost::optional fromString = Lua::getStringField(L, 2, "from")) { from = JID(*fromString); } int timeout = getGlobalTimeout(L); if (boost::optional timeoutInt = Lua::getIntField(L, 2, "timeout")) { timeout = *timeoutInt; } boost::shared_ptr payload; lua_getfield(L, 2, "query"); payload = getPayload(L, -1); lua_pop(L, 1); return component->sendRequest( boost::make_shared< GenericRequest >(type, from, to, payload, component->getComponent()->getIQRouter()), timeout).convertToLuaResult(L); } #define DISPATCH_PUBSUB_PAYLOAD(payloadType, container, response) \ else if (boost::shared_ptr p = boost::dynamic_pointer_cast(payload)) { \ return component->sendPubSubRequest(type, to, p, timeout).convertToLuaResult(L); \ } SLUIFT_LUA_FUNCTION(Component, get) { return sendQuery(L, IQ::Get); } SLUIFT_LUA_FUNCTION(Component, set) { return sendQuery(L, IQ::Set); } SLUIFT_LUA_FUNCTION_WITH_HELP( Component, send, "Sends a raw string", "self\n" "data the string to send\n", "" ) { Sluift::globals.eventLoop.runOnce(); getComponent(L)->getComponent()->sendData(std::string(Lua::checkString(L, 2))); lua_pushvalue(L, 1); return 0; } static void pushEvent(lua_State* L, const SluiftComponent::Event& event) { switch (event.type) { case SluiftComponent::Event::MessageType: { Message::ref message = boost::dynamic_pointer_cast(event.stanza); Lua::Table result = boost::assign::map_list_of ("type", boost::make_shared(std::string("message"))) ("from", boost::make_shared(message->getFrom().toString())) ("to", boost::make_shared(message->getTo().toString())) ("body", boost::make_shared(message->getBody())) ("message_type", boost::make_shared(MessageConvertor::convertMessageTypeToString(message->getType()))); Lua::pushValue(L, result); addPayloadsToTable(L, message->getPayloads()); Lua::registerTableToString(L, -1); break; } case SluiftComponent::Event::PresenceType: { Presence::ref presence = boost::dynamic_pointer_cast(event.stanza); Lua::Table result = boost::assign::map_list_of ("type", boost::make_shared(std::string("presence"))) ("from", boost::make_shared(presence->getFrom().toString())) ("to", boost::make_shared(presence->getTo().toString())) ("status", boost::make_shared(presence->getStatus())) ("presence_type", boost::make_shared(PresenceConvertor::convertPresenceTypeToString(presence->getType()))); Lua::pushValue(L, result); addPayloadsToTable(L, presence->getPayloads()); Lua::registerTableToString(L, -1); break; } } } struct CallUnaryLuaPredicateOnEvent { CallUnaryLuaPredicateOnEvent(lua_State* L, int index) : L(L), index(index) { } bool operator()(const SluiftComponent::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(Component, get_next_event) { Sluift::globals.eventLoop.runOnce(); SluiftComponent* component = getComponent(L); int timeout = getGlobalTimeout(L); boost::optional type; int condition = 0; if (lua_istable(L, 2)) { if (boost::optional typeString = Lua::getStringField(L, 2, "type")) { if (*typeString == "message") { type = SluiftComponent::Event::MessageType; } else if (*typeString == "presence") { type = SluiftComponent::Event::PresenceType; } } if (boost::optional timeoutInt = Lua::getIntField(L, 2, "timeout")) { timeout = *timeoutInt; } lua_getfield(L, 2, "if"); if (lua_isfunction(L, -1)) { condition = Lua::absoluteOffset(L, -1); } } boost::optional event; if (condition) { event = component->getNextEvent(timeout, CallUnaryLuaPredicateOnEvent(L, condition)); } else if (type) { event = component->getNextEvent( timeout, lambda::bind(&SluiftComponent::Event::type, lambda::_1) == *type); } else { event = component->getNextEvent(timeout); } if (event) { pushEvent(L, *event); } else { lua_pushnil(L); } return 1; } SLUIFT_LUA_FUNCTION_WITH_HELP( Component, jid, "Returns the JID of this component", "self\n", "" ) { SluiftComponent* component = getComponent(L); lua_pushstring(L, component->getComponent()->getJID().toString().c_str()); return 1; } SLUIFT_LUA_FUNCTION(Component, __gc) { SluiftComponent* component = getComponent(L); delete component; return 0; }