index daab6bcbe72c20146be99fdc1597d4c378de829f..a04ab05f00e39fa1f424141140e37e0e72260670 100644 (file)
#include <functional>
#include <algorithm>
#include <iostream>
#include <functional>
#include <algorithm>
#include <iostream>
+#include <time.h>
#include <gtkmm.h>
#include <glibmm/i18n.h>
#include <gtkmm.h>
#include <glibmm/i18n.h>
+#include "xml/node.h"
+#include "xml/repr.h"
+
+#include "util/ucompose.hpp"
+
#include "xml/node-observer.h"
#include "pedro/pedrodom.h"
#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 "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/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"
#include "jabber_whiteboard/defines.h"
#include "jabber_whiteboard/dialog/choose-desktop.h"
-#define INKBOARD_XMLNS "http://inkscape.org/inkboard"
-
namespace Inkscape {
namespace Whiteboard {
namespace Inkscape {
namespace Whiteboard {
SessionManager::SessionManager()
{
SessionManager::SessionManager()
{
- sequenceNumber = 0L;
getClient().addXmppEventListener(*this);
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()
}
SessionManager::~SessionManager()
getClient().disconnect();
}
getClient().disconnect();
}
-unsigned long SessionManager::getSequenceNumber()
+/**
+ * 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)
{
{
- return sequenceNumber++;
+
+ 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();
+
+
}
}
-bool
-SessionManager::send(const Glib::ustring &destJid,
- const Message::Wrapper type,
- const Glib::ustring &data)
+void
+SessionManager::terminateSession(Glib::ustring const& sessionId)
{
{
- Pedro::DOMString xmlData = Pedro::Parser::encode(data);
- char *fmt=
- "<message type='chat' from='%s' to='%s' id='ink_%d'>"
- "<w xmlns='%s' "
- "protocol='%d' type='%d' seq='%d'><x:inkboard-data>%s</x:inkboard-data></inkboard>"
- "<body></body>"
- "</message>";
- if (!getClient().write(fmt,
- getClient().getJid().c_str(),
- destJid.c_str(),
- getClient().getMsgId(),
- INKBOARD_XMLNS,
- 2,
- (MessageType)type,
- getSequenceNumber(),
- xmlData.c_str()
- ))
- {
- return false;
- }
-
- return true;
+ 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);
+ }
}
}
-bool
-SessionManager::sendGroup(const Glib::ustring &groupJid,
- const Message::Wrapper type,
- const Glib::ustring &data)
+void
+SessionManager::addSession(WhiteboardRecord whiteboard)
{
{
- Pedro::DOMString xmlData = Pedro::Parser::encode(data);
- char *fmt=
- "<message type='groupchat' from='%s' to='%s' id='ink_%d'>"
- "<inkboard xmlns='%s' "
- "protocol='%d' type='%d' seq='%d'><x:inkboard-data>%s</x:inkboard-data></inkboard>"
- "<body></body>"
- "</message>";
- if (!getClient().write(fmt,
- getClient().getJid().c_str(),
- groupJid.c_str(),
- getClient().getMsgId(),
- INKBOARD_XMLNS,
- 2,
- type,
- getSequenceNumber(),
- xmlData.c_str()
- ))
- {
- return false;
+ 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 true;
+ }
+ return NULL;
}
void
}
void
{
break;
}
{
break;
}
+ case Pedro::XmppEvent::EVENT_MUC_MESSAGE:
case Pedro::XmppEvent::EVENT_MESSAGE:
{
case Pedro::XmppEvent::EVENT_MESSAGE:
{
- printf("## SM message:%s\n", event.getFrom().c_str());
Pedro::Element *root = event.getDOM();
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;
}
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;
case Pedro::XmppEvent::EVENT_MUC_JOIN:
{
break;
}
/**
}
/**
- * 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
*/
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<size; n++)
+ randomString[n]=rand()%26+'a';
+ randomString[size+1]='\0';
- if (dt != NULL)
- {
- doc = dynamic_cast< InkboardDocument* >(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 =
+ "<span weight=\"bold\" size=\"larger\">" +
+ String::ucompose(_("<b>%1</b> has invited you to a whiteboard session."), from) +
+ "</span>\n\n" +
+ String::ucompose(_("Do you wish to accept <b>%1</b>'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<SPDesktop*>(dtw->view);
+ sp_namedview_window_from_document(dt);
+ sp_namedview_update_layers_from_document(dt);
+ }
+ return dt;
}
} // namespace Whiteboard
}
} // namespace Whiteboard