X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fjabber_whiteboard%2Fsession-manager.cpp;h=a04ab05f00e39fa1f424141140e37e0e72260670;hb=96d7f74ab56c7ce6cf9df0108ce925f59bcf0035;hp=a64efbdb33efe821fd8be87c0695cadbd849b310;hpb=fa1b6dbd11c790efd8f2a3cb37c7fea3be4e446b;p=inkscape.git diff --git a/src/jabber_whiteboard/session-manager.cpp b/src/jabber_whiteboard/session-manager.cpp index a64efbdb3..a04ab05f0 100644 --- a/src/jabber_whiteboard/session-manager.cpp +++ b/src/jabber_whiteboard/session-manager.cpp @@ -13,28 +13,40 @@ #include #include #include +#include #include #include +#include "xml/node.h" +#include "xml/repr.h" + +#include "util/ucompose.hpp" + #include "xml/node-observer.h" #include "pedro/pedrodom.h" +#include "ui/view/view-widget.h" + +#include "application/application.h" +#include "application/editor.h" + +#include "document-private.h" +#include "interface.h" +#include "sp-namedview.h" #include "document.h" #include "desktop.h" #include "desktop-handles.h" +#include "jabber_whiteboard/invitation-confirm-dialog.h" #include "jabber_whiteboard/message-verifier.h" #include "jabber_whiteboard/session-manager.h" #include "jabber_whiteboard/inkboard-document.h" -#include "jabber_whiteboard/new-inkboard-document.h" #include "jabber_whiteboard/defines.h" #include "jabber_whiteboard/dialog/choose-desktop.h" -#define INKBOARD_XMLNS "http://inkscape.org/inkboard" - namespace Inkscape { namespace Whiteboard { @@ -60,11 +72,11 @@ SessionManager::instance() SessionManager::SessionManager() { - sequenceNumber = 0L; getClient().addXmppEventListener(*this); - this->_check_pending_invitations = Glib::signal_timeout().connect(sigc::mem_fun(*this, &SessionManager::_checkInvitationQueue), 50); - this->_check_invitation_responses = Glib::signal_timeout().connect(sigc::mem_fun(*this, &SessionManager::_checkInvitationResponseQueue), 50); + this->CheckPendingInvitations = + Glib::signal_timeout().connect(sigc::mem_fun( + *this, &SessionManager::checkInvitationQueue), 50); } SessionManager::~SessionManager() @@ -73,13 +85,88 @@ SessionManager::~SessionManager() getClient().disconnect(); } +/** + * Initiates a shared session with a user or conference room. + * + * \param to The recipient to which this desktop will be linked, specified as a JID. + * \param type Type of the session; i.e. private message or group chat. + */ +void +SessionManager::initialiseSession(Glib::ustring const& to, State::SessionType type) +{ + + SPDocument* doc = makeInkboardDocument(g_quark_from_static_string("xml"), "svg:svg", type, to); + InkboardDocument* inkdoc = dynamic_cast< InkboardDocument* >(doc->rdoc); + if(inkdoc == NULL) return; + + if(type == State::WHITEBOARD_PEER) + { + ChooseDesktop dialog; + int result = dialog.run(); + + if(result == Gtk::RESPONSE_OK) + { + SPDesktop *desktop = dialog.getDesktop(); + + if(desktop != NULL) + { + Inkscape::XML::Document *old_doc = + sp_desktop_document(desktop)->rdoc; + inkdoc->root()->mergeFrom(old_doc->root(),"id"); + } + }else { return; } + } + + char * sessionId = createSessionId(10); + + inkdoc->setSessionId(sessionId); + + makeInkboardDesktop(doc); + addSession(WhiteboardRecord(sessionId, inkdoc)); + + inkdoc->startSessionNegotiation(); + + +} + +void +SessionManager::terminateSession(Glib::ustring const& sessionId) +{ + WhiteboardList::iterator i = whiteboards.begin(); + for(; i != whiteboards.end(); ++i) { + if ((*i).first == sessionId) + break; + } + + if (i != whiteboards.end()) { + (*i).second->terminateSession(); + whiteboards.erase(i); + } +} + +void +SessionManager::addSession(WhiteboardRecord whiteboard) +{ + whiteboards.push_back(whiteboard); +} + +InkboardDocument* +SessionManager::getInkboardSession(Glib::ustring const& sessionId) +{ + WhiteboardList::iterator i = whiteboards.begin(); + for(; i != whiteboards.end(); ++i) { + if ((*i).first == sessionId) { + return (*i).second; + } + } + return NULL; +} + void SessionManager::processXmppEvent(const Pedro::XmppEvent &event) { int type = event.getType(); - g_warning("recieved pedro"); - switch (type) { case Pedro::XmppEvent::EVENT_STATUS: { @@ -97,39 +184,20 @@ SessionManager::processXmppEvent(const Pedro::XmppEvent &event) { break; } + case Pedro::XmppEvent::EVENT_MUC_MESSAGE: case Pedro::XmppEvent::EVENT_MESSAGE: { - printf("## SM message:%s\n", event.getFrom().c_str()); Pedro::Element *root = event.getDOM(); - if (root) - { - if (root->getTagAttribute("inkboard", "xmlns") == - INKBOARD_XMLNS) - { - _processInkboardEvent(event); - } - } + if (root && root->getTagAttribute("wb", "xmlns") == Vars::INKBOARD_XMLNS) + processWhiteboardEvent(event); + break; } case Pedro::XmppEvent::EVENT_PRESENCE: { break; } - case Pedro::XmppEvent::EVENT_MUC_MESSAGE: - { - printf("## SM MUC message:%s\n", event.getFrom().c_str()); - Pedro::Element *root = event.getDOM(); - if (root) - { - if (root->getTagAttribute("inkboard", "xmlns") == - INKBOARD_XMLNS) - { - _processInkboardEvent(event); - } - } - break; - } case Pedro::XmppEvent::EVENT_MUC_JOIN: { break; @@ -150,264 +218,190 @@ SessionManager::processXmppEvent(const Pedro::XmppEvent &event) } /** - * Initiates a shared session with a user or conference room. - * - * \param to The recipient to which this desktop will be linked, specified as a JID. - * \param type Type of the session; i.e. private message or group chat. + * Handles all incoming messages from pedro within a valid namespace, CONNECT_REQUEST messages + * are handled here, as they have no InkboardDocument to be handled from, all other messages + * are passed to their appropriate Inkboard document, which is identified by the 'session' + * attribute of the 'wb' element + * */ void -SessionManager::doShare(Glib::ustring const& to, State::SessionType type) +SessionManager::processWhiteboardEvent(Pedro::XmppEvent const& event) { - InkboardDocument* doc; - SPDesktop* dt; + Pedro::Element* root = event.getDOM(); + if (root == NULL) { + g_warning("Received null DOM; ignoring message."); + return; + } - // Just create a new blank canvas for MUC sessions - if(type == State::WHITEBOARD_MUC) - { - dt = createInkboardDesktop(to, type); + Pedro::DOMString session = root->getTagAttribute("wb", "session"); + Pedro::DOMString type = root->getTagAttribute("message", "type"); + Pedro::DOMString domwrapper = root->getFirstChild()->getFirstChild()->getFirstChild()->getName(); - if (dt != NULL) - { - doc = dynamic_cast< InkboardDocument* >(sp_desktop_document(dt)->rdoc); + if (session.empty()) { + g_warning("Received incomplete Whiteboard message, missing session identifier; ignoring message."); + return; + } - if (doc != NULL) - { - doc->startSessionNegotiation(); - } - } - + if(root->exists(Message::CONNECT_REQUEST) && type == State::WHITEBOARD_PEER) + { + handleIncomingInvitation(Invitation(event.getFrom(),session)); - // Let the user pick the document which to start a peer ro peer session - // with, or a blank one, then create a blank document, copy over the contents - // and initialise session - } else if (type== State::WHITEBOARD_PEER) { + }else + { + Message::Wrapper wrapper = static_cast< Message::Wrapper >(domwrapper); + InkboardDocument* doc = getInkboardSession(session); - ChooseDesktop dialog; - int result = dialog.run(); + if(doc != NULL) + doc->recieve(wrapper, root->getFirstChild()); + } +} - if(result == Gtk::RESPONSE_OK) - { - SPDesktop *desktop = dialog.getDesktop(); - dt = createInkboardDesktop(to, type); +char* +SessionManager::createSessionId(int size) +{ + // Create a random session identifier + char * randomString = (char*) malloc (size); + for (int n=0; n(sp_desktop_document(dt)->rdoc); - - if (doc != NULL) - { - if(desktop != NULL) - { - Inkscape::XML::Document *old_doc = - sp_desktop_document(desktop)->rdoc; - doc->root()->mergeFrom(old_doc->root(),"id"); - } - - doc->startSessionNegotiation(); - } - } - } - } + return randomString; } /** - * Clone of sp_file_new and all related subroutines and functions, - * with appropriate modifications to use the Inkboard document class. + * Adds an invitation to a queue to be executed in SessionManager::_checkInvitationQueue() + * as when this method is called, we're still executing in Pedro's context, which causes + * issues when we run a dialog main loop. * - * \param to The JID to which this Inkboard document will be connected. - * \return A pointer to the created desktop, or NULL if a new desktop could not be created. */ -SPDesktop* -SessionManager::createInkboardDesktop(Glib::ustring const& to, State::SessionType type) +void +SessionManager::handleIncomingInvitation(Invitation invitation) { -// Create document (sp_repr_document_new clone) - SPDocument* doc = makeInkboardDocument(g_quark_from_static_string("xml"), "svg:svg", type, to); - g_return_val_if_fail(doc != NULL, NULL); + // don't insert duplicate invitations + if (std::find(invitations.begin(),invitations.end(),invitation) != invitations.end()) + return; - InkboardDocument* inkdoc = dynamic_cast< InkboardDocument* >(doc->rdoc); - if (inkdoc == NULL) { // this shouldn't ever happen... - return NULL; - } + invitations.push_back(invitation); -// Create desktop and attach document - SPDesktop *dt = makeInkboardDesktop(doc); - _inkboards.push_back(Inkboard_record_type(to, inkdoc)); - return dt; } -void -SessionManager::terminateInkboardSession(Glib::ustring const& to) +bool +SessionManager::checkInvitationQueue() { - std::cout << "Terminating Inkboard session to " << to << std::endl; - Inkboards_type::iterator i = _inkboards.begin(); - for(; i != _inkboards.end(); ++i) { - if ((*i).first == to) { - break; - } - } + // The user is currently busy with an action. Defer invitation processing + // until the user is free. + int x, y; + Gdk::ModifierType mt; + Gdk::Display::get_default()->get_pointer(x, y, mt); + if (mt & GDK_BUTTON1_MASK) + return true; + + if (invitations.size() > 0) + { + // There's an invitation to process; process it. + Invitation invitation = invitations.front(); + Glib::ustring from = invitation.first; + Glib::ustring sessionId = invitation.second; - if (i != _inkboards.end()) { - std::cout << "Erasing Inkboard session to " << to << std::endl; - (*i).second->terminateSession(); - _inkboards.erase(i); - } -} + Glib::ustring primary = + "" + + String::ucompose(_("%1 has invited you to a whiteboard session."), from) + + "\n\n" + + String::ucompose(_("Do you wish to accept %1's whiteboard session invitation?"), from); -InkboardDocument* -SessionManager::getInkboardSession(Glib::ustring const& to) -{ - Inkboards_type::iterator i = _inkboards.begin(); - for(; i != _inkboards.end(); ++i) { - if ((*i).first == to) { - return (*i).second; + InvitationConfirmDialog dialog(primary); + + dialog.add_button(_("Accept invitation"), Dialog::ACCEPT_INVITATION); + dialog.add_button(_("Decline invitation"), Dialog::DECLINE_INVITATION); + + Dialog::DialogReply reply = static_cast< Dialog::DialogReply >(dialog.run()); + + + SPDocument* doc = makeInkboardDocument(g_quark_from_static_string("xml"), "svg:svg", State::WHITEBOARD_PEER, from); + + InkboardDocument* inkdoc = dynamic_cast< InkboardDocument* >(doc->rdoc); + if(inkdoc == NULL) return true; + + inkdoc->handleState(State::INITIAL,State::CONNECTING); + inkdoc->setSessionId(sessionId); + addSession(WhiteboardRecord(sessionId, inkdoc)); + + switch (reply) { + + case Dialog::ACCEPT_INVITATION:{ + inkdoc->send(from, Message::PROTOCOL,Message::ACCEPT_INVITATION); + makeInkboardDesktop(doc); + break; } + + case Dialog::DECLINE_INVITATION: default: { + inkdoc->send(from, Message::PROTOCOL,Message::DECLINE_INVITATION); + terminateSession(sessionId); + break; } } + + invitations.pop_front(); + } - return NULL; + + return true; } -void -SessionManager::_processInkboardEvent(Pedro::XmppEvent const& event) +//######################################################################### +//# HELPER FUNCTIONS +//######################################################################### + +SPDocument* +makeInkboardDocument(int code, gchar const* rootname, State::SessionType type, Glib::ustring const& to) { - Pedro::Element* root = event.getDOM(); + SPDocument* doc; - if (root == NULL) { - g_warning("Received null DOM; ignoring message."); - return; - } + InkboardDocument* rdoc = new InkboardDocument(g_quark_from_static_string("xml"), type, to); + rdoc->setAttribute("version", "1.0"); + rdoc->setAttribute("standalone", "no"); + XML::Node *comment = rdoc->createComment(" Created with Inkscape (http://www.inkscape.org/) "); + rdoc->appendChild(comment); + GC::release(comment); - Pedro::DOMString type = root->getTagAttribute("inkboard", "type"); - Pedro::DOMString seq = root->getTagAttribute("inkboard", "seq"); - Pedro::DOMString protover = root->getTagAttribute("inkboard", "protocol"); + XML::Node* root = rdoc->createElement(rootname); + rdoc->appendChild(root); + GC::release(root); - if (type.empty() || seq.empty() || protover.empty()) { - g_warning("Received incomplete Inkboard message (missing type, protocol, or sequence number); ignoring message."); - return; - } + Glib::ustring name = String::ucompose( + _("Inkboard session (%1 to %2)"), SessionManager::instance().getClient().getJid(), to); - MessageType mtype = static_cast< MessageType >(atoi(type.c_str())); - - // Messages that deal with the creation and destruction of sessions should be handled - // here in the SessionManager. - // - // These events are listed below, along with rationale. - // - // - CONNECT_REQUEST_USER: when we begin to process this message, we will not have an - // Inkboard session available to send the message to. Therefore, this message needs - // to be handled by the SessionManager. - // - // - CONNECT_REQUEST_REFUSED_BY_PEER: this message means that the recipient of a - // private invitation refused the invitation. In this case, we need to destroy the - // Inkboard desktop, document, and session associated with that invitation. - // Destruction of these components seems to be more naturally done in the SessionManager - // than in the Inkboard document itself (especially since the document may be associated - // with multiple desktops). - // - // - UNSUPPORTED_PROTOCOL_VERSION: this message means that the recipient of an invitation - // does not support the version of the Inkboard protocol we are using. In this case, - // we have to destroy the Inkboard desktop, document, and session associated with that - // invitation. The rationale for doing it in the SessionManager is the same as that - // given above. - // - // - ALREADY_IN_SESSION: similar rationale to above. - // - // - DISCONNECTED_FROM_USER_SIGNAL: similar rationale to above. - // - // - // All other events can be handled inside an Inkboard session. - - // The message we are handling will have come from some Jabber ID. We need to verify - // that the Inkboard session associated with that JID is in the correct state for the - // incoming message (or, in some cases, that the session correctly exists / does not - // exist). - InkboardDocument* doc = getInkboardSession(event.getFrom()); - -// NOTE: This line refers to a class that hasn't been written yet -// MessageValidityTestResult res = MessageVerifier::verifyMessageValidity(event, mtype, doc); - - MessageValidityTestResult res = RESULT_INVALID; - /* - switch (res) { - case RESULT_VALID: - { - switch (mtype) { - case CONNECT_REQUEST: - default: - if (doc != NULL) { - unsigned int seqnum = atoi(seq.c_str()); - doc->processInkboardEvent(mtype, seqnum, event.getData()); - } - break; - } - break; - } - case RESULT_INVALID: - default: - // FIXME: better warning message - g_warning("Received message in invalid context."); - break; - } - */ -} + doc = sp_document_create(rdoc, NULL, NULL, name.c_str(), TRUE); + g_return_val_if_fail(doc != NULL, NULL); -void -SessionManager::_handleSessionEvent(Message::Wrapper mtype, Pedro::XmppEvent const& event) -{ - /* - switch (mtype) { - case CONNECT_REQUEST: - _handleIncomingInvitation(event.getFrom()); - break; - case INVITATION_DECLINE: - _handleInvitationResponse(event.getFrom(), DECLINE_INVITATION); - break; - default: - break; - } - */ + return doc; } -void -SessionManager::_handleIncomingInvitation(Glib::ustring const& from) +// TODO: When the switchover to the new GUI is complete, this function should go away +// and be replaced with a call to Inkscape::NSApplication::Editor::createDesktop. +// It currently only exists to correctly mimic the desktop creation functionality +// in file.cpp. +// +// \see sp_file_new +SPDesktop* +makeInkboardDesktop(SPDocument* doc) { - // don't insert duplicate invitations - if (std::find(_pending_invitations.begin(), _pending_invitations.end(), from) != _pending_invitations.end()) { - return; - } - - // We need to do the invitation confirm/deny dialog elsewhere -- - // when this method is called, we're still executing in Pedro's context, - // which causes issues when we run a dialog main loop. - // - // The invitation handling is done in a poller timeout that executes approximately - // every 50ms. It calls _checkInvitationQueue. - _pending_invitations.push_back(from); + SPDesktop* dt; -} + if (NSApplication::Application::getNewGui()) + dt = NSApplication::Editor::createDesktop(doc); -void -SessionManager::_handleInvitationResponse(Glib::ustring const& from, InvitationResponses resp) -{ - // only handle one response per invitation sender - // - // TODO: this could have one huge bug: say that Alice sends an invite to Bob, but - // Bob is doing something else at the moment and doesn't want to get in an Inkboard - // session. Eve intercepts Bob's "reject invitation" message and passes a - // "accept invitation" message to Alice that comes before Bob's "reject invitation" - // message. - // - // Does XMPP prevent this sort of attack? Need to investigate that. - if (std::find_if(_invitation_responses.begin(), _invitation_responses.end(), CheckInvitationSender(from)) != _invitation_responses.end()) { - return; - } - - // We need to do the invitation confirm/deny dialog elsewhere -- - // when this method is called, we're still executing in Pedro's context, - // which causes issues when we run a dialog main loop. - // - // The invitation handling is done in a poller timeout that executes approximately - // every 50ms. It calls _checkInvitationResponseQueue. - _invitation_responses.push_back(Invitation_response_type(from, resp)); + else + { + SPViewWidget *dtw = sp_desktop_widget_new(sp_document_namedview(doc, NULL)); + g_return_val_if_fail(dtw != NULL, NULL); + sp_document_unref(doc); + + sp_create_window(dtw, TRUE); + dt = static_cast(dtw->view); + sp_namedview_window_from_document(dt); + sp_namedview_update_layers_from_document(dt); + } + return dt; } } // namespace Whiteboard