diff --git a/backends/libpurple/main.cpp b/backends/libpurple/main.cpp index 70ba33332ef5f0b9892680b9ab70905e95c3cc1e..13b0725518e1db3d92b7da865e10c36f2566e96d 100644 --- a/backends/libpurple/main.cpp +++ b/backends/libpurple/main.cpp @@ -45,6 +45,11 @@ DEFINE_LOGGER(logger_libpurple, "libpurple"); DEFINE_LOGGER(logger, "backend"); +/* Additional PURPLE_MESSAGE_* flags as a hack to track the origin of the message. */ +typedef enum { + PURPLE_MESSAGE_SPECTRUM2_ORIGINATED = 0x80000000, +} PurpleMessageSpectrum2Flags; + int main_socket; static int writeInput; bool firstPing = true; @@ -542,19 +547,19 @@ class SpectrumNetworkPlugin : public NetworkPlugin { if (xhtml.empty()) { gchar *_markup = purple_markup_escape_text_wrapped(message.c_str(), -1); if (purple_conversation_get_type_wrapped(conv) == PURPLE_CONV_TYPE_IM) { - purple_conv_im_send_wrapped(PURPLE_CONV_IM_WRAPPED(conv), _markup); + purple_conv_im_send_with_flags_wrapped(PURPLE_CONV_IM_WRAPPED(conv), _markup, static_cast(PURPLE_MESSAGE_SPECTRUM2_ORIGINATED)); } else if (purple_conversation_get_type_wrapped(conv) == PURPLE_CONV_TYPE_CHAT) { - purple_conv_chat_send_wrapped(PURPLE_CONV_CHAT_WRAPPED(conv), _markup); + purple_conv_chat_send_with_flags_wrapped(PURPLE_CONV_CHAT_WRAPPED(conv), _markup, static_cast(PURPLE_MESSAGE_SPECTRUM2_ORIGINATED)); } g_free(_markup); } else { if (purple_conversation_get_type_wrapped(conv) == PURPLE_CONV_TYPE_IM) { - purple_conv_im_send_wrapped(PURPLE_CONV_IM_WRAPPED(conv), xhtml.c_str()); + purple_conv_im_send_with_flags_wrapped(PURPLE_CONV_IM_WRAPPED(conv), xhtml.c_str(), static_cast(PURPLE_MESSAGE_SPECTRUM2_ORIGINATED)); } else if (purple_conversation_get_type_wrapped(conv) == PURPLE_CONV_TYPE_CHAT) { - purple_conv_chat_send_wrapped(PURPLE_CONV_CHAT_WRAPPED(conv), xhtml.c_str()); + purple_conv_chat_send_with_flags_wrapped(PURPLE_CONV_CHAT_WRAPPED(conv), xhtml.c_str(), static_cast(PURPLE_MESSAGE_SPECTRUM2_ORIGINATED)); } } } @@ -1138,7 +1143,11 @@ static PurpleBlistUiOps blistUiOps = NULL }; +static void conv_write_im(PurpleConversation *conv, const char *who, const char *msg, PurpleMessageFlags flags, time_t mtime); + static void conv_write(PurpleConversation *conv, const char *who, const char *alias, const char *msg, PurpleMessageFlags flags, time_t mtime) { + LOG4CXX_INFO(logger, "conv_write()"); + if (flags & PURPLE_MESSAGE_SYSTEM && CONFIG_STRING(config, "service.protocol") == "prpl-telegram") { PurpleAccount *account = purple_conversation_get_account_wrapped(conv); @@ -1194,6 +1203,10 @@ static void conv_write(PurpleConversation *conv, const char *who, const char *al np->handleMessage(np->m_accounts[account], np->NameToLegacyName(account, conversationName), message_, who, xhtml_, timestamp); } } + else { + //Handle all non-special cases by just passing them to conv_write_im + conv_write_im(conv, who, msg, flags, mtime); + } } static char *calculate_data_hash(guchar *data, size_t len, @@ -1223,9 +1236,26 @@ static char *calculate_data_hash(guchar *data, size_t len, } static void conv_write_im(PurpleConversation *conv, const char *who, const char *msg, PurpleMessageFlags flags, time_t mtime) { - // Don't forwards our own messages. - if (purple_conversation_get_type_wrapped(conv) == PURPLE_CONV_TYPE_IM && (flags & PURPLE_MESSAGE_SEND || flags & PURPLE_MESSAGE_SYSTEM)) { - return; + LOG4CXX_INFO(logger, "conv_write_im()"); + bool isCarbon = false; + + if (purple_conversation_get_type_wrapped(conv) == PURPLE_CONV_TYPE_IM) { + //Don't forwards our own messages, but do forward messages "from=us" which originated elsewhere + //(such as carbons of our messages from other legacy network clients) + if (flags & PURPLE_MESSAGE_SPECTRUM2_ORIGINATED) { + LOG4CXX_INFO(logger, "conv_write_im(): ignoring a message generated by us"); + return; + } + + //If this is a carbon of a message from us, mark it as such + if(flags & PURPLE_MESSAGE_SEND) + isCarbon = true; + + //Originally the transport had this filter too, I'm leaving it in for now: + if (flags & PURPLE_MESSAGE_SYSTEM) { + LOG4CXX_INFO(logger, "conv_write_im(): ignoring a system message"); + return; + } } PurpleAccount *account = purple_conversation_get_account_wrapped(conv); @@ -1336,12 +1366,12 @@ static void conv_write_im(PurpleConversation *conv, const char *who, const char w.erase((int) pos, w.length() - (int) pos); } LOG4CXX_INFO(logger, "Received message body='" << message_ << "' xhtml='" << xhtml_ << "' name='" << w << "'"); - np->handleMessage(np->m_accounts[account], w, message_, n, xhtml_, timestamp); + np->handleMessage(np->m_accounts[account], w, message_, n, xhtml_, timestamp, false, false, isCarbon); } else { std::string conversationName = purple_conversation_get_name_wrapped(conv); LOG4CXX_INFO(logger, "Received message body='" << message_ << "' xhtml='" << xhtml_ << "' name='" << conversationName << "' " << who); - np->handleMessage(np->m_accounts[account], np->NameToLegacyName(account, conversationName), message_, who, xhtml_, timestamp); + np->handleMessage(np->m_accounts[account], np->NameToLegacyName(account, conversationName), message_, who, xhtml_, timestamp, false, false, isCarbon); } } diff --git a/backends/libpurple/purple_defs.cpp b/backends/libpurple/purple_defs.cpp index be01c7a2ab716769660d34d3c81e73f69ca50822..626201c6a52b7274f75c126e88dbbebd662304e1 100644 --- a/backends/libpurple/purple_defs.cpp +++ b/backends/libpurple/purple_defs.cpp @@ -74,7 +74,9 @@ purple_find_conversation_with_account_wrapped_fnc purple_find_conversation_with_ purple_conversation_new_wrapped_fnc purple_conversation_new_wrapped = NULL; purple_conversation_get_type_wrapped_fnc purple_conversation_get_type_wrapped = NULL; purple_conv_im_send_wrapped_fnc purple_conv_im_send_wrapped = NULL; +purple_conv_im_send_with_flags_wrapped_fnc purple_conv_im_send_with_flags_wrapped = NULL; purple_conv_chat_send_wrapped_fnc purple_conv_chat_send_wrapped = NULL; +purple_conv_chat_send_with_flags_wrapped_fnc purple_conv_chat_send_with_flags_wrapped = NULL; purple_conversation_destroy_wrapped_fnc purple_conversation_destroy_wrapped = NULL; purple_conversation_get_account_wrapped_fnc purple_conversation_get_account_wrapped = NULL; purple_conversation_get_name_wrapped_fnc purple_conversation_get_name_wrapped = NULL; @@ -441,10 +443,18 @@ bool resolvePurpleFunctions() { if (!purple_conv_im_send_wrapped) return false; + purple_conv_im_send_with_flags_wrapped = (purple_conv_im_send_with_flags_wrapped_fnc)GetProcAddress(f_hPurple, "purple_conv_im_send_with_flags"); + if (!purple_conv_im_send_with_flags_wrapped) + return false; + purple_conv_chat_send_wrapped = (purple_conv_chat_send_wrapped_fnc)GetProcAddress(f_hPurple, "purple_conv_chat_send"); if (!purple_conv_chat_send_wrapped) return false; + purple_conv_chat_send_with_flags_wrapped = (purple_conv_chat_send_with_flags_wrapped_fnc)GetProcAddress(f_hPurple, "purple_conv_chat_send_with_flags"); + if (!purple_conv_chat_send_with_flags_wrapped) + return false; + purple_conversation_destroy_wrapped = (purple_conversation_destroy_wrapped_fnc)GetProcAddress(f_hPurple, "purple_conversation_destroy"); if (!purple_conversation_destroy_wrapped) return false; diff --git a/backends/libpurple/purple_defs.h b/backends/libpurple/purple_defs.h index d11953c638dcd10ebcc6a2350133a7358c9a683d..0f6051acbe076aba9b6d00010b952e61752906a3 100644 --- a/backends/libpurple/purple_defs.h +++ b/backends/libpurple/purple_defs.h @@ -236,9 +236,15 @@ extern purple_conversation_update_wrapped_func purple_conversation_update_wrappe typedef void (_cdecl * purple_conv_im_send_wrapped_fnc)(PurpleConvIm *im, const char *message); extern purple_conv_im_send_wrapped_fnc purple_conv_im_send_wrapped; +typedef void (_cdecl * purple_conv_im_send_with_flags_wrapped_fnc)(PurpleConvIm *im, const char *message, PurpleMessageFlags flags); +extern purple_conv_im_send_with_flags_wrapped_fnc purple_conv_im_send_with_flags_wrapped; + typedef void (_cdecl * purple_conv_chat_send_wrapped_fnc)(PurpleConvChat *chat, const char *message); extern purple_conv_chat_send_wrapped_fnc purple_conv_chat_send_wrapped; +typedef void (_cdecl * purple_conv_chat_send_with_flags_wrapped_fnc)(PurpleConvChat *chat, const char *message, PurpleMessageFlags flags); +extern purple_conv_chat_send_with_flags_wrapped_fnc purple_conv_chat_send_with_flags_wrapped; + typedef void (_cdecl * purple_conversation_destroy_wrapped_fnc)(PurpleConversation *conv); extern purple_conversation_destroy_wrapped_fnc purple_conversation_destroy_wrapped; @@ -543,7 +549,9 @@ extern wpurple_g_io_channel_win32_new_socket_wrapped_fnc wpurple_g_io_channel_wi #define purple_conversation_set_data_wrapped purple_conversation_set_data #define purple_conversation_update_wrapped purple_conversation_update #define purple_conv_im_send_wrapped purple_conv_im_send +#define purple_conv_im_send_with_flags_wrapped purple_conv_im_send_with_flags #define purple_conv_chat_send_wrapped purple_conv_chat_send +#define purple_conv_chat_send_with_flags_wrapped purple_conv_chat_send_with_flags #define purple_conversation_destroy_wrapped purple_conversation_destroy #define purple_conversation_get_account_wrapped purple_conversation_get_account #define purple_conversation_get_name_wrapped purple_conversation_get_name diff --git a/docs/guide/developer_lowlevel.textile b/docs/guide/developer_lowlevel.textile index 76691a4fe355c8599e88a6100668778428f1b1ba..4ce3a0f9c05b9015e205c82f1d7d9fa82ade14cf 100644 --- a/docs/guide/developer_lowlevel.textile +++ b/docs/guide/developer_lowlevel.textile @@ -181,6 +181,7 @@ Backend sends this payload when it receives new message from legacy network whic |message|Plain text message| |xhtml|Message formatted using XHTML-IM XEP if available| |nickname| If the conversation is room, this is the nickname of user who sent the original message| +|carbon| If set, the message is a carbon copy of our own message sent in a different legacy network client. It should be treated as a message FROM us, not TO us| h3. Type: TYPE_ATTENTION, Payload: ConversationMessage diff --git a/include/Swiften/Elements/HintPayload.cpp b/include/Swiften/Elements/HintPayload.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a164cfe6dc4cbb73691550d85958c82d041f751a --- /dev/null +++ b/include/Swiften/Elements/HintPayload.cpp @@ -0,0 +1,15 @@ +/* + * Implements XEP-0334: Message Processing Hints + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include + +namespace Swift { + +HintPayload::HintPayload(Type type) +: type_(type) { +} + +} diff --git a/include/Swiften/Elements/HintPayload.h b/include/Swiften/Elements/HintPayload.h new file mode 100644 index 0000000000000000000000000000000000000000..add9d6fd61e02d786cc31dc027f1b2ff347077a3 --- /dev/null +++ b/include/Swiften/Elements/HintPayload.h @@ -0,0 +1,33 @@ +/* + * Implements XEP-0334: Message Processing Hints + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include +#include +#include + +#include + +#include "Swiften/SwiftenCompat.h" + +namespace Swift { + class HintPayload : public Payload { + public: + typedef SWIFTEN_SHRPTR_NAMESPACE::shared_ptr ref; + + enum Type { NoPermanentStore, NoStore, NoCopy, Store }; + + public: + HintPayload(Type type = NoCopy); + + void setType(Type type) { type_ = type; } + const Type getType() { return type_; } + + private: + Type type_; + }; +} diff --git a/include/Swiften/Elements/Privilege.cpp b/include/Swiften/Elements/Privilege.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7156e731362e690ea0ae927ffa4436490a5af2dd --- /dev/null +++ b/include/Swiften/Elements/Privilege.cpp @@ -0,0 +1,14 @@ +/* + * Implements Privilege tag for XEP-0356: Privileged Entity + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include + +namespace Swift { + +Privilege::Privilege() { +} + +} diff --git a/include/Swiften/Elements/Privilege.h b/include/Swiften/Elements/Privilege.h new file mode 100644 index 0000000000000000000000000000000000000000..760eef09ff39aafdb96cbe82f6fadc86a42fb965 --- /dev/null +++ b/include/Swiften/Elements/Privilege.h @@ -0,0 +1,45 @@ +/* + * Implements Privilege tag for XEP-0356: Privileged Entity + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include +#include +#include + +#include +#include + +#include +#if (SWIFTEN_VERSION >= 0x030000) +#define SWIFTEN_SUPPORTS_FORWARDED +#include +#endif + +#include "Swiften/SwiftenCompat.h" + +namespace Swift { + class Stanza; + + class Privilege : public Payload { + public: + typedef SWIFTEN_SHRPTR_NAMESPACE::shared_ptr ref; +#ifdef SWIFTEN_SUPPORTS_FORWARDED + typedef Swift::Forwarded Forwarded; +#else + typedef Payload Forwarded; +#endif + + public: + Privilege(); + + void setForwarded(SWIFTEN_SHRPTR_NAMESPACE::shared_ptr forwarded) { forwarded_ = forwarded; } + const SWIFTEN_SHRPTR_NAMESPACE::shared_ptr& getForwarded() const { return forwarded_; } + + private: + SWIFTEN_SHRPTR_NAMESPACE::shared_ptr forwarded_; + }; +} diff --git a/include/Swiften/Parser/PayloadParsers/HintPayloadParser.cpp b/include/Swiften/Parser/PayloadParsers/HintPayloadParser.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bdecf6eae57966fc4c9bffa68843671d18b7d36c --- /dev/null +++ b/include/Swiften/Parser/PayloadParsers/HintPayloadParser.cpp @@ -0,0 +1,38 @@ +/* + * Implements XEP-0334: Message Processing Hints + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include + +namespace Swift { + +HintPayloadParser::HintPayloadParser() : level_(0) { +} + +void HintPayloadParser::handleStartElement(const std::string& element, const std::string& /*ns*/, const AttributeMap& /*attributes*/) { + if (level_ == 0) { + HintPayload::Type type = HintPayload::NoCopy; + if (element == "no-permanent-store") { + type = HintPayload::NoPermanentStore; + } else if (element == "no-store") { + type = HintPayload::NoStore; + } else if (element == "no-copy") { + type = HintPayload::NoCopy; + } else if (element == "store") { + type = HintPayload::Store; + } + getPayloadInternal()->setType(type); + } + ++level_; +} + +void HintPayloadParser::handleEndElement(const std::string&, const std::string&) { + --level_; +} + +void HintPayloadParser::handleCharacterData(const std::string&) { +} + +} diff --git a/include/Swiften/Parser/PayloadParsers/HintPayloadParser.h b/include/Swiften/Parser/PayloadParsers/HintPayloadParser.h new file mode 100644 index 0000000000000000000000000000000000000000..6dd9f0be5224a1f9b859255fec58bbe6f76b43a1 --- /dev/null +++ b/include/Swiften/Parser/PayloadParsers/HintPayloadParser.h @@ -0,0 +1,24 @@ +/* + * Implements XEP-0334: Message Processing Hints + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include +#include + +namespace Swift { + class HintPayloadParser : public GenericPayloadParser { + public: + HintPayloadParser(); + + virtual void handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes); + virtual void handleEndElement(const std::string& element, const std::string&); + virtual void handleCharacterData(const std::string& data); + + private: + int level_; + }; +} diff --git a/include/Swiften/Parser/PayloadParsers/PrivilegeParser.cpp b/include/Swiften/Parser/PayloadParsers/PrivilegeParser.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7a6ed12afcff62d93db471556fc6eb47eee8dd1d --- /dev/null +++ b/include/Swiften/Parser/PayloadParsers/PrivilegeParser.cpp @@ -0,0 +1,60 @@ +/* + * Implements Privilege tag for XEP-0356: Privileged Entity + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include +#ifdef SWIFTEN_SUPPORTS_FORWARDED +#include +#else +#include +#include +#include +#endif + +namespace Swift { + +PrivilegeParser::PrivilegeParser(PayloadParserFactoryCollection* factories) : factories_(factories), level_(TopLevel) { +} + +void PrivilegeParser::handleStartElement(const std::string& element, const std::string& ns, const AttributeMap& attributes) { + if (level_ == PayloadLevel) { + if (element == "forwarded" && ns == "urn:xmpp:forward:0") { +#ifdef SWIFTEN_SUPPORTS_FORWARDED + childParser_ = SWIFTEN_SHRPTR_NAMESPACE::dynamic_pointer_cast(SWIFTEN_SHRPTR_NAMESPACE::make_shared(factories_)); +#else + PayloadParserFactory* parserFactory = factories_->getPayloadParserFactory(element, ns, attributes); + if (parserFactory) { + childParser_.reset(parserFactory->createPayloadParser()); + } + else { + childParser_.reset(new UnknownPayloadParser()); + } +#endif + }; + } + if (childParser_) { + childParser_->handleStartElement(element, ns, attributes); + } + ++level_; +} + +void PrivilegeParser::handleEndElement(const std::string& element, const std::string& ns) { + --level_; + if (childParser_ && level_ >= PayloadLevel) { + childParser_->handleEndElement(element, ns); + } + if (childParser_ && level_ == PayloadLevel) { + getPayloadInternal()->setForwarded(SWIFTEN_SHRPTR_NAMESPACE::dynamic_pointer_cast(childParser_->getPayload())); + childParser_.reset(); + } +} + +void PrivilegeParser::handleCharacterData(const std::string& data) { + if (childParser_) { + childParser_->handleCharacterData(data); + } +} + +} diff --git a/include/Swiften/Parser/PayloadParsers/PrivilegeParser.h b/include/Swiften/Parser/PayloadParsers/PrivilegeParser.h new file mode 100644 index 0000000000000000000000000000000000000000..0bffe973328e74c288fb4ae55db7e942c8eae966 --- /dev/null +++ b/include/Swiften/Parser/PayloadParsers/PrivilegeParser.h @@ -0,0 +1,35 @@ +/* + * Implements Privilege tag for XEP-0356: Privileged Entity + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include +#include +#include + +namespace Swift { + class PayloadParserFactoryCollection; + class PayloadParser; + + class PrivilegeParser : public GenericPayloadParser { + public: + PrivilegeParser(PayloadParserFactoryCollection* factories); + + virtual void handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes); + virtual void handleEndElement(const std::string& element, const std::string&); + virtual void handleCharacterData(const std::string& data); + + enum Level { + TopLevel = 0, + PayloadLevel = 1 + }; + + private: + PayloadParserFactoryCollection* factories_; + SWIFTEN_SHRPTR_NAMESPACE::shared_ptr childParser_; + int level_; + }; +} diff --git a/include/Swiften/Serializer/PayloadSerializers/HintPayloadSerializer.cpp b/include/Swiften/Serializer/PayloadSerializers/HintPayloadSerializer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..31b61c7a796e74b3934010eba98e458f966b560a --- /dev/null +++ b/include/Swiften/Serializer/PayloadSerializers/HintPayloadSerializer.cpp @@ -0,0 +1,34 @@ +/* + * Implements XEP-0334: Message Processing Hints + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include + +#include + +#include + +#include +#include +#include + +namespace Swift { + +HintPayloadSerializer::HintPayloadSerializer() : GenericPayloadSerializer() { +} + +std::string HintPayloadSerializer::serializePayload(SWIFTEN_SHRPTR_NAMESPACE::shared_ptr hint) const { + std::string tagname = ""; + switch(hint->getType()) { + case HintPayload::NoPermanentStore: tagname = "no-permanent-store"; break; + case HintPayload::NoStore: tagname = "no-store"; break; + case HintPayload::NoCopy: tagname = "no-copy"; break; + case HintPayload::Store: tagname = "store"; break; + } + + return XMLElement(tagname, "urn:xmpp:hints").serialize(); +} + +} diff --git a/include/Swiften/Serializer/PayloadSerializers/HintPayloadSerializer.h b/include/Swiften/Serializer/PayloadSerializers/HintPayloadSerializer.h new file mode 100644 index 0000000000000000000000000000000000000000..83a3fea094f4cefd679e3ad82738cc3eb84a842e --- /dev/null +++ b/include/Swiften/Serializer/PayloadSerializers/HintPayloadSerializer.h @@ -0,0 +1,21 @@ +/* + * Implements XEP-0334: Message Processing Hints + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include +#include + +#include "Swiften/SwiftenCompat.h" + +namespace Swift { + class HintPayloadSerializer : public GenericPayloadSerializer { + public: + HintPayloadSerializer(); + + virtual std::string serializePayload(SWIFTEN_SHRPTR_NAMESPACE::shared_ptr) const; + }; +} diff --git a/include/Swiften/Serializer/PayloadSerializers/PrivilegeSerializer.cpp b/include/Swiften/Serializer/PayloadSerializers/PrivilegeSerializer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1d28267e38bc60b5024c37a291c45fa2e42ca7e7 --- /dev/null +++ b/include/Swiften/Serializer/PayloadSerializers/PrivilegeSerializer.cpp @@ -0,0 +1,54 @@ +/* + * Implements Privilege tag for XEP-0356: Privileged Entity + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include + +#include + +#include +#include +#include + +#ifdef SWIFTEN_SUPPORTS_FORWARDED +#include +#include +#else +#include +#endif + +namespace Swift { + +PrivilegeSerializer::PrivilegeSerializer(PayloadSerializerCollection* serializers) : GenericPayloadSerializer(), serializers_(serializers) { +} + +PrivilegeSerializer::~PrivilegeSerializer() { +} + +std::string PrivilegeSerializer::serializePayload(SWIFTEN_SHRPTR_NAMESPACE::shared_ptr payload) const { + if (!payload) { + return ""; + } + + XMLElement element("privilege", "urn:xmpp:privilege:1"); + + SWIFTEN_SHRPTR_NAMESPACE::shared_ptr forwarded(payload->getForwarded()); + if (forwarded) { + std::string forwardedStr = ""; +#ifdef SWIFTEN_SUPPORTS_FORWARDED + forwardedStr = ForwardedSerializer(serializers_).serialize(forwarded); +#else + PayloadSerializer* serializer = serializers_->getPayloadSerializer(payload); + if(serializer) { + forwardedStr = serializer->serialize(payload); + } +#endif + element.addNode(SWIFTEN_SHRPTR_NAMESPACE::make_shared(forwardedStr)); + } + + return element.serialize(); +} + +} diff --git a/include/Swiften/Serializer/PayloadSerializers/PrivilegeSerializer.h b/include/Swiften/Serializer/PayloadSerializers/PrivilegeSerializer.h new file mode 100644 index 0000000000000000000000000000000000000000..f9915f02bee76e989ae9f9d6d73cf2cc4ad01b94 --- /dev/null +++ b/include/Swiften/Serializer/PayloadSerializers/PrivilegeSerializer.h @@ -0,0 +1,27 @@ +/* + * Implements Privilege tag for XEP-0356: Privileged Entity + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include +#include + +#include "Swiften/SwiftenCompat.h" + +namespace Swift { + class PayloadSerializerCollection; + + class PrivilegeSerializer : public GenericPayloadSerializer { + public: + PrivilegeSerializer(PayloadSerializerCollection* serializers); + virtual ~PrivilegeSerializer(); + + virtual std::string serializePayload(SWIFTEN_SHRPTR_NAMESPACE::shared_ptr) const; + + private: + PayloadSerializerCollection* serializers_; + }; +} diff --git a/include/Swiften/SwiftenCompat.h b/include/Swiften/SwiftenCompat.h index 8584e20b8e57611db0b5eed3687242f237f95525..62d87b26842fa40167b1ad2e1a04a6a4881f0cc4 100644 --- a/include/Swiften/SwiftenCompat.h +++ b/include/Swiften/SwiftenCompat.h @@ -51,3 +51,12 @@ #define SWIFTEN_SIGNAL_CONNECTION_NAMESPACE boost::signals #define SWIFT_HOSTADDRESS(x) Swift::HostAddress(x) #endif + +#if (SWIFTEN_VERSION >= 0x030000) +//Swiften supports carbon Sent and Received tags as well as Forwarded tags inside those +#define SWIFTEN_SUPPORTS_CARBONS +//Swiften supports Forwarded tag +#define SWIFTEN_SUPPORTS_FORWARDED +//Privilege tag is implemented locally, but it makes little sense without forwarded tag +#define SWIFTEN_SUPPORTS_PRIVILEGE +#endif \ No newline at end of file diff --git a/include/transport/Conversation.h b/include/transport/Conversation.h index ad2680081dbbeab4637d4839e06d4c0a78c6165e..705606ec82cceb4aaf414d4e255113ac85234a2c 100644 --- a/include/transport/Conversation.h +++ b/include/transport/Conversation.h @@ -64,7 +64,17 @@ class Conversation { /// \param message Message received from legacy network. /// \param nickname For MUC conversation this is nickname of room participant who sent this message. - void handleMessage(SWIFTEN_SHRPTR_NAMESPACE::shared_ptr &message, const std::string &nickname = ""); + void handleMessage(SWIFTEN_SHRPTR_NAMESPACE::shared_ptr &message, const std::string &nickname = "", const bool carbon = false); + + //Generates a carbon wrapper around the given payload and delivers it + void forwardAsCarbonSent( + const SWIFTEN_SHRPTR_NAMESPACE::shared_ptr &payload, + const Swift::JID& to); + + //Generates a impersonation request arount the given payload and delivers it + void forwardImpersonated( + SWIFTEN_SHRPTR_NAMESPACE::shared_ptr payload, + const Swift::JID& server); void handleRawMessage(SWIFTEN_SHRPTR_NAMESPACE::shared_ptr &message); void handleRawPresence(Swift::Presence::ref presence); diff --git a/include/transport/NetworkPlugin.h b/include/transport/NetworkPlugin.h index ece57e7992395293610bdf61ddec9099906d5bc6..cb38acfcc4f2fc68a7f7ac58b690a932ac90b03f 100644 --- a/include/transport/NetworkPlugin.h +++ b/include/transport/NetworkPlugin.h @@ -122,7 +122,8 @@ class NetworkPlugin { /// \param message Plain text message. /// \param nickname Nickname of buddy in room. Empty if it's normal chat message. /// \param xhtml XHTML message. - void handleMessage(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &nickname = "", const std::string &xhtml = "", const std::string ×tamp = "", bool headline = false, bool pm = false); + /// \param carbon If set, the message is a carbon copy of our own message, sent in a different legacy network client. The message should be treated as sent FROM us, not TO us. + void handleMessage(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &nickname = "", const std::string &xhtml = "", const std::string ×tamp = "", bool headline = false, bool pm = false, bool carbon = false); void handleMessageAck(const std::string &user, const std::string &legacyName, const std::string &id); diff --git a/include/transport/protocol.proto b/include/transport/protocol.proto index 8f8a790b99ff69b6a9651b9d9c7e5ca52968b3ae..744426003ef0987f32896aae76f311699770f6be 100644 --- a/include/transport/protocol.proto +++ b/include/transport/protocol.proto @@ -77,6 +77,7 @@ message ConversationMessage { optional bool headline = 7; optional string id = 8; optional bool pm = 9; + optional bool carbon = 10; } message Room { diff --git a/libtransport/Conversation.cpp b/libtransport/Conversation.cpp index cd96bb170046b465a4d8800f40810ca1a78758c1..a42515c1e6814284061522ad9a5654b5b9900af6 100644 --- a/libtransport/Conversation.cpp +++ b/libtransport/Conversation.cpp @@ -24,6 +24,7 @@ #include "transport/User.h" #include "transport/Transport.h" #include "transport/Buddy.h" +#include "transport/PresenceOracle.h" #include "transport/RosterManager.h" #include "transport/Frontend.h" #include "transport/Config.h" @@ -34,8 +35,20 @@ #include "Swiften/Elements/MUCUserPayload.h" #include "Swiften/Elements/Delay.h" #include "Swiften/Elements/MUCPayload.h" +#include "Swiften/Elements/Presence.h" #include "Swiften/Elements/VCardUpdate.h" +#include "Swiften/SwiftenCompat.h" +#ifdef SWIFTEN_SUPPORTS_CARBONS +#include "Swiften/Elements/CarbonsSent.h" +#include "Swiften/Elements/Forwarded.h" +#include "Swiften/Elements/HintPayload.h" +#endif + +#ifdef SWIFTEN_SUPPORTS_PRIVILEGE +#include "Swiften/Elements/Privilege.h" +#endif + namespace Transport { DEFINE_LOGGER(logger, "Conversation"); @@ -145,7 +158,7 @@ void Conversation::handleRawMessage(SWIFTEN_SHRPTR_NAMESPACE::shared_ptr &message, const std::string &nickname) { +void Conversation::handleMessage(SWIFTEN_SHRPTR_NAMESPACE::shared_ptr &message, const std::string &nickname, const bool carbon) { if (m_muc) { message->setType(Swift::Message::Groupchat); } @@ -223,7 +236,102 @@ void Conversation::handleMessage(SWIFTEN_SHRPTR_NAMESPACE::shared_ptrgetFrom().toString()); } + + if (carbon) { +#ifdef SWIFTEN_SUPPORTS_CARBONS + LOG4CXX_INFO(logger, "CARBON MSG"); + //Swap from and to + Swift::JID from = message->getFrom(); + message->setFrom(message->getTo()); + message->setTo(from); + + //Carbons should be sent to every resource directly. + //Even if we tried to send to bare jid, the server would at best route it + //as it would normal message (usually to the highest priority resource), + //but won't produce carbons to other resources as it does with normal messages. + Component* transport = this->getConversationManager()->getComponent(); + std::vector presences = transport->getPresenceOracle()->getAllPresence(this->m_jid.toBare()); + if (presences.empty()) { + LOG4CXX_INFO(logger, "No presences for JID " << this->m_jid.toString() + << ", will send to bare JID for archival."); + this->forwardAsCarbonSent(message, this->m_jid.toBare()); + } else + BOOST_FOREACH(const Swift::Presence::ref &it, presences) { + this->forwardAsCarbonSent(message, it->getFrom()); + } +#else //!SWIFTEN_SUPPORTS_CARBONS + //Ignore the message. +#endif + } else { + handleRawMessage(message); + } +} + +void Conversation::forwardAsCarbonSent( + const SWIFTEN_SHRPTR_NAMESPACE::shared_ptr &payload, + const Swift::JID& to) +{ +#ifdef SWIFTEN_SUPPORTS_CARBONS + LOG4CXX_INFO(logger, "Carbon to -> " << to.toString()); + + //Message envelope + SWIFTEN_SHRPTR_NAMESPACE::shared_ptr message(new Swift::Message()); + + //Type MUST be equal to the original message type + message->setType(payload->getType()); + + //XEP-0280 docs say "from" MUST be the carbon subscriber's JID, + //so a transport will need to wrap this in another wrapper. + message->setFrom(m_jid.toBare()); + message->setTo(to); + + //Wrap the payload in a + SWIFTEN_SHRPTR_NAMESPACE::shared_ptr forwarded(new Swift::Forwarded()); + forwarded->setStanza(payload); + SWIFTEN_SHRPTR_NAMESPACE::shared_ptr sent(new Swift::CarbonsSent()); + sent->setForwarded(forwarded); + message->addPayload(sent); + + //Add no-copy to prevent some servers from creating carbons of carbons + Swift::HintPayload::ref noCopy(new Swift::HintPayload(Swift::HintPayload::NoCopy)); + message->addPayload(noCopy); + + this->forwardImpersonated(message, Swift::JID("", message->getFrom().getDomain())); +#else + //We cannot send the carbon. +#endif +} + +//Generates a XEP-0356 privilege wrapper asking to impersonate a user from a given domain +void Conversation::forwardImpersonated( + SWIFTEN_SHRPTR_NAMESPACE::shared_ptr payload, + const Swift::JID& server) +{ +#ifdef SWIFTEN_SUPPORTS_PRIVILEGE + LOG4CXX_INFO(logger, "Impersonate to -> " << server.toString()); + Component* transport = this->getConversationManager()->getComponent(); + + //Message envelope + SWIFTEN_SHRPTR_NAMESPACE::shared_ptr message(new Swift::Message()); + // "from" MUST be our bare jid + message->setFrom(transport->getJID()); + // "to" MUST be the bare jid of the server we're asking to impersonate + message->setTo(server); + message->setType(Swift::Message::Normal); + + SWIFTEN_SHRPTR_NAMESPACE::shared_ptr forwarded(new Swift::Forwarded()); + forwarded->setStanza(payload); + + Swift::Privilege::ref privilege(new Swift::Privilege()); + privilege->setForwarded(forwarded); + + message->addPayload(privilege); + LOG4CXX_INFO(logger, "Impersonate: sending message"); handleRawMessage(message); +#else + //Try to send the message as is -- some servers can be configured to accept this + handleRawMessage(payload); +#endif } std::string Conversation::getParticipants() { diff --git a/libtransport/NetworkPluginServer.cpp b/libtransport/NetworkPluginServer.cpp index f8651420f4dbd1cfd49a92e8b56e6cb1344ab452..19a331a0f0712169f697ff60dee7249a056d48c7 100644 --- a/libtransport/NetworkPluginServer.cpp +++ b/libtransport/NetworkPluginServer.cpp @@ -734,9 +734,8 @@ void NetworkPluginServer::handleConvMessagePayload(const std::string &data, bool user->getConversationManager()->addConversation(conv); conv->onMessageToSend.connect(boost::bind(&NetworkPluginServer::handleMessageReceived, this, _1, _2)); } - // Forward it - conv->handleMessage(msg, payload.nickname()); + conv->handleMessage(msg, payload.nickname(), payload.carbon()); m_userManager->messageToXMPPSent(); } diff --git a/plugin/cpp/networkplugin.cpp b/plugin/cpp/networkplugin.cpp index f7e57db01d5342f188d7e7eeba2dd2a8f8bbaccb..1f681c52415092081b2428627e47de3191d5c318 100644 --- a/plugin/cpp/networkplugin.cpp +++ b/plugin/cpp/networkplugin.cpp @@ -94,7 +94,7 @@ void NetworkPlugin::sendRawXML(std::string &xml) { send(xml); } -void NetworkPlugin::handleMessage(const std::string &user, const std::string &legacyName, const std::string &msg, const std::string &nickname, const std::string &xhtml, const std::string ×tamp, bool headline, bool pm) { +void NetworkPlugin::handleMessage(const std::string &user, const std::string &legacyName, const std::string &msg, const std::string &nickname, const std::string &xhtml, const std::string ×tamp, bool headline, bool pm, bool carbon) { pbnetwork::ConversationMessage m; m.set_username(user); m.set_buddyname(legacyName); @@ -104,6 +104,7 @@ void NetworkPlugin::handleMessage(const std::string &user, const std::string &le m.set_timestamp(timestamp); m.set_headline(headline); m.set_pm(pm); + m.set_carbon(carbon); std::string message; m.SerializeToString(&message); diff --git a/spectrum/src/frontends/xmpp/XMPPFrontend.cpp b/spectrum/src/frontends/xmpp/XMPPFrontend.cpp index 3639925a4bc0e6b6e5be7ab9499249fd92a19faa..649c6c03b9f8336bd38796493afdb96351988843 100644 --- a/spectrum/src/frontends/xmpp/XMPPFrontend.cpp +++ b/spectrum/src/frontends/xmpp/XMPPFrontend.cpp @@ -32,6 +32,7 @@ #include "transport/Config.h" #include "transport/Transport.h" #include "storageparser.h" +#include "Swiften/SwiftenCompat.h" #ifdef _WIN32 #include #include "Swiften/TLS/Schannel/SchannelServerContext.h" @@ -59,7 +60,16 @@ #include "BlockSerializer.h" #include "Swiften/Parser/PayloadParsers/InvisibleParser.h" #include "Swiften/Serializer/PayloadSerializers/InvisibleSerializer.h" +#include "Swiften/Parser/PayloadParsers/HintPayloadParser.h" +#include "Swiften/Serializer/PayloadSerializers/HintPayloadSerializer.h" +#ifdef SWIFTEN_SUPPORTS_PRIVILEGE +#include "Swiften/Parser/PayloadParsers/PrivilegeParser.h" +#include "Swiften/Serializer/PayloadSerializers/PrivilegeSerializer.h" +#endif #include "Swiften/Parser/GenericPayloadParserFactory.h" +#if SWIFTEN_VERSION >= 0x030000 +#include "Swiften/Parser/GenericPayloadParserFactory2.h" +#endif #include "Swiften/Queries/IQRouter.h" #include "Swiften/Elements/RosterPayload.h" #include "discoitemsresponder.h" @@ -74,6 +84,20 @@ DEFINE_LOGGER(logger, "XMPPFrontend"); XMPPFrontend::XMPPFrontend() { } +class SwiftServerExposed: public Swift::Server +{ +public: + PayloadParserFactoryCollection* getPayloadParserFactories() { return Swift::Server::getPayloadParserFactories(); } + PayloadSerializerCollection* getPayloadSerializers() { return Swift::Server::getPayloadSerializers(); } +}; + +class SwiftComponentExposed: public Swift::Component +{ +public: + PayloadParserFactoryCollection* getPayloadParserFactories() { return Swift::Component::getPayloadParserFactories(); } + PayloadSerializerCollection* getPayloadSerializers() { return Swift::Component::getPayloadSerializers(); } +}; + void XMPPFrontend::init(Component *transport, Swift::EventLoop *loop, Swift::NetworkFactories *factories, Config *config, Transport::UserRegistry *userRegistry) { m_transport = transport; m_component = NULL; @@ -93,6 +117,10 @@ void XMPPFrontend::init(Component *transport, Swift::EventLoop *loop, Swift::Net m_parserFactories.push_back(new Swift::GenericPayloadParserFactory("query", "http://jabber.org/protocol/stats")); m_parserFactories.push_back(new Swift::GenericPayloadParserFactory("query", "jabber:iq:gateway")); m_parserFactories.push_back(new Swift::GenericPayloadParserFactory("x", "http://jabber.org/protocol/muc")); + m_parserFactories.push_back(new Swift::GenericPayloadParserFactory("no-permanent-store", "urn:xmpp:hints")); + m_parserFactories.push_back(new Swift::GenericPayloadParserFactory("no-store", "urn:xmpp:hints")); + m_parserFactories.push_back(new Swift::GenericPayloadParserFactory("no-copy", "urn:xmpp:hints")); + m_parserFactories.push_back(new Swift::GenericPayloadParserFactory("store", "urn:xmpp:hints")); m_payloadSerializers.push_back(new Swift::AttentionSerializer()); m_payloadSerializers.push_back(new Swift::XHTMLIMSerializer()); @@ -101,6 +129,7 @@ void XMPPFrontend::init(Component *transport, Swift::EventLoop *loop, Swift::Net m_payloadSerializers.push_back(new Swift::StatsSerializer()); m_payloadSerializers.push_back(new Swift::SpectrumErrorSerializer()); m_payloadSerializers.push_back(new Swift::GatewayPayloadSerializer()); + m_payloadSerializers.push_back(new Swift::HintPayloadSerializer()); if (CONFIG_BOOL(m_config, "service.server_mode")) { LOG4CXX_INFO(logger, "Creating component in server mode on port " << CONFIG_INT(m_config, "service.port")); @@ -125,6 +154,12 @@ void XMPPFrontend::init(Component *transport, Swift::EventLoop *loop, Swift::Net m_stanzaChannel = m_server->getStanzaChannel(); m_iqRouter = m_server->getIQRouter(); + SwiftServerExposed* entity(reinterpret_cast(m_server)); +#ifdef SWIFTEN_SUPPORTS_PRIVILEGE + m_parserFactories.push_back(new Swift::GenericPayloadParserFactory2("privilege", "urn:xmpp:privilege:1", entity->getPayloadParserFactories())); + m_payloadSerializers.push_back(new Swift::PrivilegeSerializer(entity->getPayloadSerializers())); +#endif + BOOST_FOREACH(Swift::PayloadParserFactory *factory, m_parserFactories) { m_server->addPayloadParserFactory(factory); } @@ -149,6 +184,12 @@ void XMPPFrontend::init(Component *transport, Swift::EventLoop *loop, Swift::Net m_component->onDataRead.connect(boost::bind(&XMPPFrontend::handleDataRead, this, _1)); m_component->onDataWritten.connect(boost::bind(&XMPPFrontend::handleDataWritten, this, _1)); + SwiftComponentExposed* entity(reinterpret_cast(m_component)); +#ifdef SWIFTEN_SUPPORTS_PRIVILEGE + m_parserFactories.push_back(new Swift::GenericPayloadParserFactory2("privilege", "urn:xmpp:privilege:1", entity->getPayloadParserFactories())); + m_payloadSerializers.push_back(new Swift::PrivilegeSerializer(entity->getPayloadSerializers())); +#endif + BOOST_FOREACH(Swift::PayloadParserFactory *factory, m_parserFactories) { m_component->addPayloadParserFactory(factory); } @@ -161,6 +202,7 @@ void XMPPFrontend::init(Component *transport, Swift::EventLoop *loop, Swift::Net m_iqRouter = m_component->getIQRouter(); } + m_capsMemoryStorage = new CapsMemoryStorage(); #if HAVE_SWIFTEN_3 m_capsManager = new CapsManager(m_capsMemoryStorage, m_stanzaChannel, m_iqRouter, factories->getCryptoProvider()); diff --git a/tests/libtransport/basictest.cpp b/tests/libtransport/basictest.cpp index 094b61ca563ad7746f20309a64d5fcd79f99853b..51b5f87967b4e3a55e28091382541bb803b2014c 100644 --- a/tests/libtransport/basictest.cpp +++ b/tests/libtransport/basictest.cpp @@ -4,6 +4,7 @@ #include "XMPPUserManager.h" #include #include +#include #include #include #include @@ -14,6 +15,10 @@ #include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h" #include "Swiften/Serializer/GenericPayloadSerializer.h" +#include "Swiften/Parser/GenericPayloadParserFactory.h" +#if SWIFTEN_VERSION >= 0x030000 +#include "Swiften/Parser/GenericPayloadParserFactory2.h" +#endif #include "storageparser.h" #include "Swiften/Parser/PayloadParsers/AttentionParser.h" @@ -30,6 +35,12 @@ #include "BlockSerializer.h" #include "Swiften/Parser/PayloadParsers/InvisibleParser.h" #include "Swiften/Serializer/PayloadSerializers/InvisibleSerializer.h" +#include "Swiften/Parser/PayloadParsers/HintPayloadParser.h" +#include "Swiften/Serializer/PayloadSerializers/HintPayloadSerializer.h" +#ifdef SWIFTEN_SUPPORTS_PRIVILEGE +#include "Swiften/Parser/PayloadParsers/PrivilegeParser.h" +#include "Swiften/Serializer/PayloadSerializers/PrivilegeSerializer.h" +#endif using namespace Transport; @@ -68,6 +79,13 @@ void BasicTest::setMeUp (void) { parserFactories.push_back(new Swift::GenericPayloadParserFactory("query", "http://jabber.org/protocol/stats")); parserFactories.push_back(new Swift::GenericPayloadParserFactory("query", "jabber:iq:gateway")); parserFactories.push_back(new Swift::GenericPayloadParserFactory("x", "http://jabber.org/protocol/muc")); + parserFactories.push_back(new Swift::GenericPayloadParserFactory("no-permanent-store", "urn:xmpp:hints")); + parserFactories.push_back(new Swift::GenericPayloadParserFactory("no-store", "urn:xmpp:hints")); + parserFactories.push_back(new Swift::GenericPayloadParserFactory("no-copy", "urn:xmpp:hints")); + parserFactories.push_back(new Swift::GenericPayloadParserFactory("store", "urn:xmpp:hints")); +#ifdef SWIFTEN_SUPPORTS_PRIVILEGE + parserFactories.push_back(new Swift::GenericPayloadParserFactory2("privilege", "urn:xmpp:privilege:1", payloadParserFactories)); +#endif BOOST_FOREACH(Swift::PayloadParserFactory *factory, parserFactories) { payloadParserFactories->addFactory(factory); @@ -80,6 +98,10 @@ void BasicTest::setMeUp (void) { _payloadSerializers.push_back(new Swift::StatsSerializer()); _payloadSerializers.push_back(new Swift::SpectrumErrorSerializer()); _payloadSerializers.push_back(new Swift::GatewayPayloadSerializer()); + _payloadSerializers.push_back(new Swift::HintPayloadSerializer()); +#ifdef SWIFTEN_SUPPORTS_PRIVILEGE + _payloadSerializers.push_back(new Swift::PrivilegeSerializer(payloadSerializers)); +#endif BOOST_FOREACH(Swift::PayloadSerializer *serializer, _payloadSerializers) { payloadSerializers->addSerializer(serializer);