diff options
Diffstat (limited to 'src/jabber_whiteboard/message-handler.cpp')
| -rw-r--r-- | src/jabber_whiteboard/message-handler.cpp | 456 |
1 files changed, 456 insertions, 0 deletions
diff --git a/src/jabber_whiteboard/message-handler.cpp b/src/jabber_whiteboard/message-handler.cpp new file mode 100644 index 000000000..cdcef74b8 --- /dev/null +++ b/src/jabber_whiteboard/message-handler.cpp @@ -0,0 +1,456 @@ +/** + * Whiteboard session manager + * Jabber received message handling + * + * Authors: + * David Yip <yipdw@rose-hulman.edu> + * Steven Montgomery, Jonas Collaros (original C version) + * + * Copyright (c) 2004-2005 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +extern "C" { +#include <loudmouth/loudmouth.h> +} + +#include <glibmm.h> +#include <glibmm/i18n.h> +#include <glib.h> +#include <map> + +#include "jabber_whiteboard/defines.h" +#include "jabber_whiteboard/typedefs.h" +#include "jabber_whiteboard/message-processors.h" +#include "jabber_whiteboard/message-handler.h" +#include "jabber_whiteboard/session-manager.h" +#include "jabber_whiteboard/chat-handler.h" +#include "jabber_whiteboard/buddy-list-manager.h" + +namespace Inkscape { + +namespace Whiteboard { + +bool message_contexts_initialized = false; +MessageContextMap _received_message_contexts; + +MessageHandler::MessageHandler(SessionManager* sm) : _sm(sm) +{ + if (message_contexts_initialized == false) { +// this->_initializeContexts(); + MessageHandler::_initializeContexts(); + } + this->_initializeProcessors(); +} + +MessageHandler::~MessageHandler() +{ + this->_destructProcessors(); +} + +LmHandlerResult +MessageHandler::handle(LmMessage* message, HandlerMode mode) +{ + if (this->_isValidMessage(message)) { + switch(mode) { + case DEFAULT: + return this->_default(message); + case PRESENCE: + return this->_presence(message); + case ERROR: + return this->_error(message); + default: + g_warning("Jabber message handler was asked to process a message of an unhandled type; discarding message."); + return LM_HANDLER_RESULT_REMOVE_MESSAGE; + } + } else { + return LM_HANDLER_RESULT_REMOVE_MESSAGE; + } +} + +bool +MessageHandler::_hasValidReceiveContext(LmMessage* message) +{ + MessageType type = this->_getType(message); + std::bitset< NUM_FLAGS >& status = this->_sm->session_data->status; + + std::string s1 = status.to_string< char, std::char_traits< char >, std::allocator< char > >(); + + + if (type == UNKNOWN) { + // unknown types never have a valid receive context + return false; + } else { + std::bitset< NUM_FLAGS >& recvcontext = _received_message_contexts[type]; + + // TODO: remove this debug block + if ((status & recvcontext).to_ulong() < status.to_ulong()) { + g_warning("Received message in incorrect context (current is not a subset of required); discarding message."); + + std::string s2 = recvcontext.to_string< char, std::char_traits< char >, std::allocator< char > >(); + + + g_warning("current context=%s required context=%s (msgtype %s)", s1.c_str(), s2.c_str(), MessageHandler::ink_type_to_string(type)); + } + + return ((status & recvcontext).to_ulong() >= status.to_ulong()); + } +} + +bool +MessageHandler::_isValidMessage(LmMessage* message) +{ + // Global sanity checks + LmMessageNode* root; + LmMessageNode* protocolver; + LmMessageNode* offline; + LmMessageType mtype; + LmMessageSubType msubtype; + gchar const* tmp; + + Glib::ustring sender; + + + // 0. The message must have a root node. + root = lm_message_get_node(message); + if (root == NULL) { + g_warning("Check 0 failed (message has no root node)"); + return false; + } + + + // 1. The message must be of LM_MESSAGE_TYPE_MESSAGE to continue the sanity checks. + // If it is not, check to see if it is either + // a presence message or an error message. If it is either of these, then automatically + // consider it sane. + // + // FIXME: + // (That is probably a dangerous assumption. We should probably at least validate + // the source for error messages.) + // + // We do not handle IQ stanzas or STREAM messages (yet), and we certainly don't + // handle unknowns. + + mtype = lm_message_get_type(message); + switch(mtype) { + case LM_MESSAGE_TYPE_PRESENCE: + case LM_MESSAGE_TYPE_STREAM_ERROR: + return true; + case LM_MESSAGE_TYPE_IQ: + case LM_MESSAGE_TYPE_STREAM: + case LM_MESSAGE_TYPE_UNKNOWN: + g_warning("Check 1 failed (Loudmouth reported type IQ, STREAM, or UNKNOWN)"); + return false; + case LM_MESSAGE_TYPE_MESSAGE: + break; + } + + // 2. The message must contain the JID of the sender. + tmp = lm_message_node_get_attribute(root, MESSAGE_FROM); + + if (tmp == NULL) { + g_warning("Check 2 failed (no sender attribute present)"); + return false; + } else { + sender = tmp; + } + + // 3. We do not yet handle messages from offline storage, so ensure that this is not + // such a message. + offline = lm_message_node_get_child(root, "x"); + if (offline != NULL) { + if (strcmp(lm_message_node_get_value(offline), "Offline Storage") == 0) { + return false; + } + } + + // 4. If this is a regular chat message... + msubtype = lm_message_get_sub_type(message); + + if (msubtype == LM_MESSAGE_SUB_TYPE_CHAT) { + // 4a. A protocol version node must be present. + protocolver = lm_message_node_get_child(root, MESSAGE_PROTOCOL_VER); + if (protocolver == NULL) { + g_warning("Check 4a failed (no protocol attribute in chat message)"); + return false; + } else { + tmp = lm_message_node_get_value(protocolver); + if (tmp == NULL) { + g_warning("Check 4a failed (no protocol attribute in chat message)"); + return false; + } + } + + // 5a. The protocol version must be supported. + if (atoi(tmp) > HIGHEST_SUPPORTED) { + g_warning("Check 5a failed (received a message with protocol version %s, but version %s is not supported)", tmp, tmp); + return false; + } + + // ...otherwise, if this is a groupchat message, we may not have a protocol version + // (since it may be communication from the Jabber server). In this case, we have a + // different set of sanity checks. + } else if (msubtype == LM_MESSAGE_SUB_TYPE_GROUPCHAT) { + // 4b. + // In a chatroom situation, we need to ensure that we don't process messages that + // originated from us. + int cutoff = sender.find_last_of('/') + 1; + if (sender.substr(cutoff, sender.length()) == this->_sm->session_data->chat_handle) { + return false; + } + // TODO: 6b. If the message is NOT from the Jabber server, then check the protocol version. + } + + // If all tests pass, then the message is at least valid. + // Correct context has not yet been established, however; that is the job of the default handler + // and hasValidReceiveContext. + + return true; +} + +MessageType +MessageHandler::_getType(LmMessage* message) +{ + LmMessageNode* root; + LmMessageNode* typenode; + + root = lm_message_get_node(message); + if (root != NULL) { + typenode = lm_message_node_get_child(root, MESSAGE_TYPE); + if (typenode != NULL) { + return static_cast< MessageType >(atoi(lm_message_node_get_value(typenode))); + } + } + return UNKNOWN; +} + +JabberMessage +MessageHandler::_extractData(LmMessage* message) +{ + + JabberMessage jm(message); + LmMessageNode* root; + LmMessageNode* sequence; + LmMessageNode* body; + gchar const* tmp; + + root = lm_message_get_node(message); + + if (root != NULL) { + sequence = lm_message_node_get_child(root, MESSAGE_SEQNUM); + body = lm_message_node_get_child(root, MESSAGE_BODY); + + jm.sender = lm_message_node_get_attribute(root, MESSAGE_FROM); + + if (sequence) { + tmp = lm_message_node_get_value(sequence); + if (tmp != NULL) { + jm.sequence = atoi(tmp); + } + } + + if (body) { + tmp = lm_message_node_get_value(body); + if (tmp != NULL) { + jm.body = tmp; + } + } + + } else { + jm.sequence = 0; + jm.sender = ""; + jm.body = ""; + } + + return jm; +} + +LmHandlerResult +MessageHandler::_default(LmMessage* message) +{ + std::bitset< NUM_FLAGS >& status = this->_sm->session_data->status; + + // Pass groupchat messages with no Inkboard type off to the chat message handler + if (this->_getType(message) == UNKNOWN) { + if (lm_message_get_sub_type(message) == LM_MESSAGE_SUB_TYPE_GROUPCHAT) { + if (status[IN_CHATROOM] || status[CONNECTING_TO_CHAT]) { + return this->_sm->chat_handler()->parse(message); + } + } else { + return LM_HANDLER_RESULT_REMOVE_MESSAGE; + } + } + + if (this->_hasValidReceiveContext(message)) { + // Extract message data + JabberMessage msg = this->_extractData(message); + MessageType type = this->_getType(message); + + // Call message handler and return instruction value to Loudmouth + + return (*this->_received_message_processors[type])(type, msg); + } else { + g_warning("Default message handler received message in invalid receive context; discarding message."); + return LM_HANDLER_RESULT_REMOVE_MESSAGE; + } +} + +LmHandlerResult +MessageHandler::_presence(LmMessage* message) +{ + LmMessageNode* root; + LmMessageSubType msubtype; + gchar const* tmp; + std::string sender; + + SessionData* sd = this->_sm->session_data; + std::bitset< NUM_FLAGS >& status = this->_sm->session_data->status; + + root = lm_message_get_node(message); + if (root == NULL) { + return LM_HANDLER_RESULT_REMOVE_MESSAGE; + } + + tmp = lm_message_node_get_attribute(root, MESSAGE_FROM); + if (tmp == NULL) { + return LM_HANDLER_RESULT_REMOVE_MESSAGE; + } else { + sender = tmp; + } + + msubtype = lm_message_get_sub_type(message); + if (status[CONNECTING_TO_CHAT] || status[IN_CHATROOM]) { + this->_sm->chat_handler()->parse(message); + } else { + switch(msubtype) { + case LM_MESSAGE_SUB_TYPE_UNAVAILABLE: + // remove buddy from online roster + sd->buddyList.erase(sender); + + // if this buddy is in a 1-1 session with us, we need to exit + // the whiteboard + if (status[IN_WHITEBOARD] && !(status[IN_CHATROOM]) && strcasecmp(sender.c_str(), sd->recipient) == 0) { + status.set(IN_WHITEBOARD, 0); + this->_sm->userDisconnectedFromWhiteboard(sender); + this->_sm->closeSession(); + } + return LM_HANDLER_RESULT_REMOVE_MESSAGE; + + case LM_MESSAGE_SUB_TYPE_AVAILABLE: + // we don't want to insert an entry into a buddy list + // if it's our own presence + if (sender != lm_connection_get_jid(this->_sm->session_data->connection)) { + sd->buddyList.insert(sender); + } + return LM_HANDLER_RESULT_REMOVE_MESSAGE; + default: + break; + } + } + return LM_HANDLER_RESULT_REMOVE_MESSAGE; +} + +LmHandlerResult +MessageHandler::_error(LmMessage* message) +{ + LmMessageNode* root; + LmMessageSubType msubtype; + + root = lm_message_get_node(message); + if (root != NULL) { + msubtype = lm_message_get_sub_type(message); + if (msubtype == LM_MESSAGE_SUB_TYPE_ERROR) { + gchar* error = g_strdup(lm_message_node_get_value(root)); + g_warning(error); + + // TODO: more robust error handling code + this->_sm->disconnectFromDocument(); + this->_sm->disconnectFromServer(); + this->_sm->connectionError(error); + } + } + + return LM_HANDLER_RESULT_REMOVE_MESSAGE; +} + +void +MessageHandler::_initializeContexts() +{ + initialize_received_message_contexts(_received_message_contexts); + message_contexts_initialized = true; +} + +void +MessageHandler::_initializeProcessors() +{ + initialize_received_message_processors(this->_sm, this->_received_message_processors); +} + +void +MessageHandler::_destructProcessors() +{ + destroy_received_message_processors(this->_received_message_processors); +} + + +char const* +MessageHandler::ink_type_to_string(gint ink_type) { + switch(ink_type) { + case Inkscape::Whiteboard::CHANGE_NOT_REPEATABLE: + return "CHANGE_NOT_REPEATABLE"; + case Inkscape::Whiteboard::CHANGE_REPEATABLE: + return "CHANGE_REPEATABLE"; + case Inkscape::Whiteboard::DUMMY_CHANGE: + return "DUMMY_CHANGE"; + case Inkscape::Whiteboard::CHANGE_COMMIT: + return "CHANGE_COMMIT"; + case Inkscape::Whiteboard::CONNECT_REQUEST_USER: + return "CONNECT_REQUEST_USER"; + case Inkscape::Whiteboard::CONNECT_REQUEST_RESPONSE_USER: + return "CONNECT_REQUEST_RESPONSE_USER"; + case Inkscape::Whiteboard::CONNECT_REQUEST_RESPONSE_CHAT: + return "CONNECT_REQUEST_RESPONSE_CHAT"; + case Inkscape::Whiteboard::DOCUMENT_SENDER_REQUEST: + return "DOCUMENT_SENDER_REQUEST"; + case Inkscape::Whiteboard::DOCUMENT_SENDER_REQUEST_RESPONSE: + return "DOCUMENT_SENDER_REQUEST_RESPONSE"; + case Inkscape::Whiteboard::DOCUMENT_REQUEST: + return "DOCUMENT_REQUEST"; + case Inkscape::Whiteboard::DOCUMENT_BEGIN: + return "DOCUMENT_BEGIN"; + case Inkscape::Whiteboard::DOCUMENT_END: + return "DOCUMENT_END"; + case Inkscape::Whiteboard::CONNECTED_SIGNAL: + return "CONNECTED_SIGNAL"; + case Inkscape::Whiteboard::DISCONNECTED_FROM_USER_SIGNAL: + return "DISCONNECTED_FROM_USER_SIGNAL"; + case Inkscape::Whiteboard::CONNECT_REQUEST_REFUSED_BY_PEER: + return "CONNECT_REQUEST_REFUSED_BY_PEER"; + case Inkscape::Whiteboard::UNSUPPORTED_PROTOCOL_VERSION: + return "UNSUPPORTED_PROTOCOL_VERSION"; + case Inkscape::Whiteboard::CHATROOM_SYNCHRONIZE_REQUEST: + return "CHATROOM_SYNCHRONIZE_REQUEST"; + case Inkscape::Whiteboard::CHATROOM_SYNCHRONIZE_RESPONSE: + return "CHATROOM_SYNCHRONIZE_RESPONSE"; + case Inkscape::Whiteboard::ALREADY_IN_SESSION: + return "ALREADY_IN_SESSION"; + default: + return "UNKNOWN"; + } +} + +} + +} + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : |
