// should we move this to AbiCollab.cpp ? void AbiCollabSessionManager::updateAcl(AbiCollab* pSession, AccountHandler* pAccount, const std::vector<std::string> vAcl) { UT_return_if_fail(pSession); UT_return_if_fail(pAccount); // check if all current collaborators are still allowed to collaborate; if not, // then remove them from the session const std::map<BuddyPtr, std::string> collaborators = pSession->getCollaborators(); for (std::map<BuddyPtr, std::string>::const_iterator cit = collaborators.begin(); cit != collaborators.end(); cit++) { BuddyPtr pBuddy = (*cit).first; UT_continue_if_fail(pBuddy); AccountHandler* pBuddyAccount = pBuddy->getHandler(); UT_continue_if_fail(pBuddyAccount); UT_continue_if_fail(pBuddyAccount == pAccount); if (!pBuddyAccount->hasAccess(vAcl, pBuddy)) { // this current collaborator has been banned from the session, so // disconnect him UT_ASSERT_HARMLESS(UT_NOT_IMPLEMENTED); } } // set the new ACL on the account handler pAccount->setAcl(pSession, vAcl); // set the new access control list on the session pSession->setAcl(vAcl); }
void AbiCollab::_restartAsMaster() { UT_DEBUGMSG(("AbiCollab::_restartAsMaster()\n")); m_Import.masterInit(); m_Export.masterInit(); // the session controller will never have to revert individual changerecords, // so we can re-enable changerecord coalescing // FIXME: enable this //pDoc->setCoalescingMask(true); // inform everyone that we can restart this session SessionReconnectAckPacket srap(m_sId, m_pDoc->getDocUUIDString(), m_pDoc->getCRNumber()); for (std::map<BuddyPtr, std::string>::iterator it = m_vCollaborators.begin(); it != m_vCollaborators.end(); it++) { BuddyPtr pBuddy = (*it).first; UT_continue_if_fail(pBuddy); AccountHandler* pHandler = pBuddy->getHandler(); UT_continue_if_fail(pHandler); pHandler->send(&srap, pBuddy); } // we're the master now! m_eTakeoveState = STS_NONE; _pushOutgoingQueue(); }
void AccountHandler::_reportProtocolError(UT_sint32 remoteVersion, UT_sint32 errorEnum, BuddyPtr pBuddy) { #ifndef DEBUG UT_UNUSED(remoteVersion); UT_UNUSED(errorEnum); #endif UT_DEBUGMSG(("_reportProtocolError: remoteVersion=%d errorEnum=%d\n", remoteVersion, errorEnum)); UT_return_if_fail(pBuddy); static std::set<std::string> reportedBuddies; if (reportedBuddies.insert( pBuddy->getDescriptor(false).utf8_str() ).second) { UT_UTF8String msg; switch (errorEnum) { case PE_Invalid_Version: msg = UT_UTF8String_sprintf("Your buddy %s is using version %d of AbiCollab, while you are using version %d.\n" "Please make sure you are using the same AbiWord version.", pBuddy->getDescription().utf8_str(), remoteVersion, ABICOLLAB_PROTOCOL_VERSION); break; default: msg = UT_UTF8String_sprintf("An unknown error code %d was reported by buddy %s.", errorEnum, pBuddy->getDescription().utf8_str()); break; } XAP_App::getApp()->getLastFocussedFrame()->showMessageBox( msg.utf8_str(), XAP_Dialog_MessageBox::b_O, XAP_Dialog_MessageBox::a_OK); } }
bool AccountHandler::hasSession(const UT_UTF8String& sSessionId) { for (std::vector<BuddyPtr>::iterator it = m_vBuddies.begin(); it != m_vBuddies.end(); it++) { BuddyPtr pBuddy = *it; UT_continue_if_fail(pBuddy); if (pBuddy->getDocHandle(sSessionId)) return true; } return false; }
void AbiCollabSessionManager::joinSessionInitiate(BuddyPtr pBuddy, DocHandle* pDocHandle) { UT_return_if_fail(pBuddy); UT_return_if_fail(pDocHandle); UT_DEBUGMSG(("Initiating join on buddy |%s|, document |%s|\n", pBuddy->getDescription().utf8_str(), pDocHandle->getSessionId().utf8_str())); AccountHandler* pHandler = pBuddy->getHandler(); UT_return_if_fail(pHandler); pHandler->joinSessionAsync(pBuddy, *pDocHandle); // TODO: do some accounting here, so we know we send an auth request in ::joinSession() }
/*! * Send this packet. Note, the specified packet does still belong to the calling class. * So if we want to store it (for masking), we HAVE to clone it first */ void AbiCollab::push(SessionPacket* pPacket) { UT_DEBUGMSG(("AbiCollab::push()\n")); UT_return_if_fail(pPacket); if (m_bIsReverting) { UT_DEBUGMSG(("This packet was generated by a local revert triggerd in the import; dropping on the floor!\n")); return; } if (m_bExportMasked) { m_vecMaskedPackets.push_back(static_cast<SessionPacket*>(pPacket->clone())); // TODO: make this a shared ptr, so we don't need to clone the packet return; } if (!isLocallyControlled() && m_eTakeoveState != STS_NONE) { // TODO: revert ack packets should still go to old master // (or be dropped on the floor, as he probably is not even around anymore) UT_DEBUGMSG(("We're in the middle of a session takeover; holding on to the packet until the new master is ready")); m_vOutgoingQueue.push_back(static_cast<SessionPacket*>(pPacket->clone())); // TODO: make this a shared ptr, so we don't need to clone the packet return; } // record if (m_pRecorder) m_pRecorder->storeOutgoing( const_cast<const SessionPacket*>( pPacket ) ); // TODO: this could go in the session manager UT_DEBUGMSG(("Pusing packet to %d collaborators\n", m_vCollaborators.size())); for (std::map<BuddyPtr, std::string>::iterator it = m_vCollaborators.begin(); it != m_vCollaborators.end(); it++) { BuddyPtr pCollaborator = (*it).first; UT_continue_if_fail(pCollaborator); UT_DEBUGMSG(("Pushing packet to collaborator with descriptor: %s\n", pCollaborator->getDescriptor(true).utf8_str())); AccountHandler* pHandler = pCollaborator->getHandler(); UT_continue_if_fail(pHandler); // overwrite remote revision for this collaborator _fillRemoteRev(pPacket, pCollaborator); // send! bool res = pHandler->send(pPacket, pCollaborator); if (!res) { UT_DEBUGMSG(("Error sending a packet!\n")); } } }
void AP_UnixDialog_CollaborationShare::_populateBuddyModel(bool refresh) { UT_DEBUGMSG(("AP_UnixDialog_CollaborationShare::_populateBuddyModel()\n")); UT_return_if_fail(m_pBuddyModel); AbiCollabSessionManager* pManager = AbiCollabSessionManager::getManager(); UT_return_if_fail(pManager); AccountHandler* pHandler = _getActiveAccountHandler(); UT_return_if_fail(pHandler); if (refresh) { // signal the account to refresh its buddy list ... pHandler->getBuddiesAsync(); // this function is really sync() atm; we need to rework this dialog to make it proper async // fetch the current ACL m_vAcl = _getSessionACL(); } // clear out the old contents, if any _freeBuddyList(); GtkTreeIter iter; for (UT_uint32 i = 0; i < pHandler->getBuddies().size(); i++) { BuddyPtr pBuddy = pHandler->getBuddies()[i]; UT_continue_if_fail(pBuddy); if (!pBuddy->getHandler()->canShare(pBuddy)) { UT_DEBUGMSG(("Not allowed to share with buddy: %s\n", pBuddy->getDescription().utf8_str())); continue; } // crap, we can't store shared pointers in the list store; use a // hack to do it (which kinda defies the whole shared pointer thingy, // but alas...) BuddyPtrWrapper* pWrapper = new BuddyPtrWrapper(pBuddy); gtk_list_store_append (m_pBuddyModel, &iter); gtk_list_store_set (m_pBuddyModel, &iter, SHARE_COLUMN, _populateShareState(pBuddy), DESC_COLUMN, pBuddy->getDescription().utf8_str(), BUDDY_COLUMN, pWrapper, -1); } gtk_widget_show_all(m_wBuddyTree); }
void TCPAccountHandler::addBuddy(BuddyPtr pBuddy) { UT_DEBUGMSG(("TCPAccountHandler::addBuddy()\n")); UT_return_if_fail(pBuddy); AbiCollabSessionManager* pManager = AbiCollabSessionManager::getManager(); UT_return_if_fail(pManager); if (getProperty("allow-all") == "true") { const UT_GenericVector<AbiCollab *> pSessions = pManager->getSessions(); for (UT_sint32 i = 0; i < pSessions.size(); i++) { AbiCollab* pSession = pSessions.getNthItem(i); UT_continue_if_fail(pSession); if (pSession->getAclAccount() != this) continue; pSession->appendAcl(pBuddy->getDescriptor(false).utf8_str()); } } AccountHandler::addBuddy(pBuddy); }
void AbiCollab::addCollaborator(BuddyPtr pCollaborator) { UT_DEBUGMSG(("AbiCollab::addCollaborator()\n")); UT_return_if_fail(pCollaborator) // check if this buddy is in the access control list if we are hosting // this session if (isLocallyControlled()) { AccountHandler* pAccount = pCollaborator->getHandler(); UT_return_if_fail(pAccount); if (!pAccount->hasAccess(m_vAcl, pCollaborator)) { UT_ASSERT(UT_NOT_IMPLEMENTED); return; } } // check for duplicates (as long as we assume a collaborator can only be part of a collaboration session once) std::map<BuddyPtr, std::string>::iterator it = m_vCollaborators.find(pCollaborator); if (it != m_vCollaborators.end()) { UT_DEBUGMSG(("Attempting to add buddy '%s' twice to a collaboration session!", pCollaborator->getDescription().utf8_str())); UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN); return; } m_vCollaborators[pCollaborator] = ""; // will fill the remote document UUID later once we receive a packet from this buddy }
void AccountHandler::signal(const Event& event, BuddyPtr pSource) { UT_DEBUGMSG(("AccountHandler::signal()\n")); // we will not forward an event over this account that came from another // account: if you do that, then you very easily get packets running around // forever. if (pSource && pSource->getHandler() != this) return; // broadcast this event over our network (if applicable for each message type) const std::vector<BuddyPtr> vRecipients = (event.isBroadcast() ? getBuddies() : event.getRecipients()); for (std::vector<BuddyPtr>::const_iterator cit = vRecipients.begin(); cit != vRecipients.end(); cit++) { BuddyPtr pRecipient = *cit; UT_continue_if_fail(pRecipient); if (!pSource || (pSource != pRecipient)) { send(&event, pRecipient); } else { // the event originated from this buddy, so make sure not to send it // back to him, as it would result in a broadcast storm and // kill the network really fast } } }
bool AccountHandler::hasAccess(const std::vector<std::string>& vAcl, BuddyPtr pBuddy) { UT_return_val_if_fail(pBuddy, false); for (UT_uint32 i = 0; i < vAcl.size(); i++) if (vAcl[i] == pBuddy->getDescriptor(false)) return true; return false; }
bool AP_Dialog_CollaborationShare::_inAcl(const std::vector<std::string>& vAcl, BuddyPtr pBuddy) { UT_return_val_if_fail(pBuddy, false); for (UT_uint32 i = 0; i < vAcl.size(); i++) { if (vAcl[i] == pBuddy->getDescriptor(false).utf8_str()) return true; } return false; }
void AbiCollab::_checkRevokeAccess(BuddyPtr pCollaborator) { UT_DEBUGMSG(("AbiCollab::_checkRevokeAccess()\n")); UT_return_if_fail(pCollaborator); UT_return_if_fail(isLocallyControlled()); UT_return_if_fail(m_pAclAccount); // remove this buddy from the access control list if his access rights // are not persistent if (!pCollaborator->getHandler()->hasPersistentAccessControl()) { for (std::vector<std::string>::iterator it = m_vAcl.begin(); it != m_vAcl.end(); it++) { if (pCollaborator->getDescriptor(false) == (*it)) { UT_DEBUGMSG(("Dropping %s from the ACL\n", (*it).c_str())); m_vAcl.erase(it); break; } } } }
void AbiCollab::_shutdownAsMaster() { UT_DEBUGMSG(("AbiCollab::_shutdownAsMaster()\n")); UT_return_if_fail(m_pController == BuddyPtr()); UT_return_if_fail(!m_bProposedController); AbiCollabSessionManager* pManager = AbiCollabSessionManager::getManager(); UT_return_if_fail(pManager); // the session takeover is complete; inform everyone that all session data // is flushed, and that everyone should talk to the new master from now on SessionFlushedPacket sfp(m_sId, m_pDoc->getDocUUIDString()); for (std::map<BuddyPtr, std::string>::iterator it = m_vCollaborators.begin(); it != m_vCollaborators.end(); it++) { BuddyPtr pBuddy = (*it).first; UT_continue_if_fail(pBuddy); pBuddy->getHandler()->send(&sfp, pBuddy); } // session takeover is done as far as the leaving session contoller is concerned pManager->endAsyncOperation(this); }
void AP_UnixDialog_CollaborationShare::_getSelectedBuddies(std::vector<std::string>& vACL) { UT_DEBUGMSG(("AP_UnixDialog_CollaborationShare::_getSelectedBuddies()\n")); vACL.clear(); GtkTreeIter iter; if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL (m_pBuddyModel), &iter)) return; do { gboolean bshare; gpointer buddy_wrapper = NULL; gtk_tree_model_get (GTK_TREE_MODEL (m_pBuddyModel), &iter, SHARE_COLUMN, &bshare, -1); gtk_tree_model_get (GTK_TREE_MODEL (m_pBuddyModel), &iter, BUDDY_COLUMN, &buddy_wrapper, -1); if (bshare && buddy_wrapper) { BuddyPtr pBuddy = reinterpret_cast<BuddyPtrWrapper*>(buddy_wrapper)->getBuddy(); vACL.push_back(pBuddy->getDescriptor(false).utf8_str()); } } while (gtk_tree_model_iter_next(GTK_TREE_MODEL (m_pBuddyModel), &iter)); }
bool AbiCollab::push(SessionPacket* pPacket, BuddyPtr collaborator) { UT_return_val_if_fail(pPacket, false); UT_return_val_if_fail(collaborator, false); AccountHandler* pHandler = collaborator->getHandler(); UT_return_val_if_fail(pHandler, false); // record if (m_pRecorder) m_pRecorder->storeOutgoing(const_cast<const SessionPacket*>( pPacket ), collaborator); // overwrite remote revision for this collaborator _fillRemoteRev(pPacket, collaborator); // send! bool res = pHandler->send(pPacket, collaborator); if (!res) { UT_DEBUGMSG(("Error sending a packet to collaborator %s!\n", collaborator->getDescription().utf8_str())); } return res; }
void AbiCollab::initiateSessionTakeover(BuddyPtr pNewMaster) { UT_return_if_fail(pNewMaster); UT_DEBUGMSG(("AbiCollab::initiateSessionTakeover() - pNewMaster: %s\n", pNewMaster->getDescriptor(true).utf8_str())); AbiCollabSessionManager* pManager = AbiCollabSessionManager::getManager(); UT_return_if_fail(pManager); // this could lead to us never exiting; add a timeout or something somewhere :) pManager->beginAsyncOperation(this); // NOTE: we only allow slaves in the session takeover process // that are on the same account as the proposed master is. The // others are dropped from the session. At least for now. // TODO: implement me // reset any old session takeover state m_bProposedController = false; m_pProposedController = pNewMaster; m_vApprovedReconnectBuddies.clear(); m_mAckedSessionTakeoverBuddies.clear(); m_bSessionFlushed = false; if (m_vOutgoingQueue.size() > 0) UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN); m_vOutgoingQueue.clear(); // send a SessionTakeoverRequest packet to the new master std::vector<std::string> buddyIdentifiers; for (std::map<BuddyPtr, std::string>::iterator it = m_vCollaborators.begin(); it != m_vCollaborators.end(); it++) { BuddyPtr pBuddy = (*it).first; UT_continue_if_fail(pBuddy); if (pNewMaster != pBuddy) buddyIdentifiers.push_back(pBuddy->getDescriptor(true).utf8_str()); } SessionTakeoverRequestPacket strp_promote(m_sId, m_pDoc->getDocUUIDString(), true, buddyIdentifiers); pNewMaster->getHandler()->send(&strp_promote, pNewMaster); // send a SessionTakeoverRequest packet to the other slaves (if any) buddyIdentifiers.clear(); buddyIdentifiers.push_back(pNewMaster->getDescriptor(true).utf8_str()); SessionTakeoverRequestPacket strp_normal(m_sId, m_pDoc->getDocUUIDString(), false, buddyIdentifiers); for (std::map<BuddyPtr, std::string>::iterator it = m_vCollaborators.begin(); it != m_vCollaborators.end(); it++) { BuddyPtr pBuddy = (*it).first; UT_continue_if_fail(pBuddy); if (pNewMaster != pBuddy) pBuddy->getHandler()->send(&strp_normal, pBuddy); } m_eTakeoveState = STS_SENT_TAKEOVER_REQUEST; }
bool AP_Dialog_CollaborationShare::_populateShareState(BuddyPtr pBuddy) { AbiCollabSessionManager* pManager = AbiCollabSessionManager::getManager(); UT_return_val_if_fail(pManager, false); PD_Document *pDoc = static_cast<PD_Document*>(XAP_App::getApp()->getLastFocussedFrame()->getCurrentDoc()); UT_return_val_if_fail(pDoc, false); if (!pManager->isInSession(pDoc)) { AccountHandler* pHandler = pBuddy->getHandler(); UT_return_val_if_fail(pHandler, false); return pHandler->defaultShareState(pBuddy); } return _inAcl(m_vAcl, pBuddy); }
void AbiCollabSessionManager::removeBuddy(BuddyPtr pBuddy, bool graceful) { UT_return_if_fail(pBuddy); UT_DEBUGMSG(("Dropping buddy '%s' from all sessions\n", pBuddy->getDescription().utf8_str())); // TODO: should we send out events for every buddy we drop, or session // we delete? for (UT_sint32 i = m_vecSessions.getItemCount() - 1; i >= 0; i--) { AbiCollab* pSession = m_vecSessions.getNthItem(i); UT_continue_if_fail(pSession); if (pSession->isLocallyControlled()) { pSession->removeCollaborator(pBuddy); } else { // we don't control this session, meaning we can drop it completely // if this buddy controlled it // TODO: when we allow more than 1 buddy in a non-locally controlled, // then remove it from that list here if (pSession->isController(pBuddy)) { UT_DEBUGMSG(("This buddy controlled a session, destroying the session...\n")); std::string docName = pSession->getDocument()->getFilename(); if (docName == "") docName = "Untitled"; // TODO: fetch the title from the frame somehow (which frame?) - MARCM destroySession(pSession); if (!graceful) { XAP_Frame *pFrame = XAP_App::getApp()->getLastFocussedFrame(); UT_continue_if_fail(pFrame); // TODO: make this localizable UT_UTF8String msg; UT_UTF8String_sprintf(msg, "You've been disconnected from buddy %s. The collaboration session for document %s has been stopped.", pBuddy->getDescription().utf8_str(), docName.c_str()); pFrame->showMessageBox(msg.utf8_str(), XAP_Dialog_MessageBox::b_O, XAP_Dialog_MessageBox::a_OK); } } } } }
bool ABI_Collab_Import::_shouldIgnore(BuddyPtr pCollaborator) { UT_return_val_if_fail(pCollaborator, false); if (m_pAbiCollab->isLocallyControlled()) { UT_DEBUGMSG(("This session is locally controlled, check if we are waiting for a revert ack from buddy: %s\n", pCollaborator->getDescription().utf8_str())); // see if we are waiting for a revert ack packet from this collaborator; // if we do, then just drop all packets on the floor until we see it for (std::vector<std::pair<BuddyPtr, UT_sint32> >::iterator it = m_revertSet.begin(); it != m_revertSet.end(); it++) { if ((*it).first == pCollaborator) { UT_DEBUGMSG(("Found collaborator %s on our revert ack list for rev %d; changerecords should be ignored!\n", (*it).first->getDescription().utf8_str(), (*it).second)); return true; } } } UT_DEBUGMSG(("%s is not on our revert ack list, don't ignore this packet...\n", pCollaborator->getDescription().utf8_str())); return false; }
void TelepathyAccountHandler::signal(const Event& event, BuddyPtr pSource) { UT_DEBUGMSG(("TelepathyAccountHandler::signal\n")); // NOTE: do NOT let AccountHandler::signal() send broadcast packets! // It will send them to all buddies, including the ones we created // to list the available documents: TelepathyBuddies. They are just fake // buddies however, and can't receive real packets. Only DTubeBuddy's // can be sent packets // Note: there is no real need to pass the PCT_CloseSessionEvent and // PCT_DisjoinSessionEvent signals to the AccountHandler::signal() // function: that one will send all buddies the 'session is closed' // signal. However, on this backend, Telepathy will handle that for us AbiCollabSessionManager* pManager = AbiCollabSessionManager::getManager(); UT_return_if_fail(pManager); switch (event.getClassType()) { case PCT_CloseSessionEvent: { UT_DEBUGMSG(("Got a PCT_CloseSessionEvent\n")); const CloseSessionEvent cse = static_cast<const CloseSessionEvent&>(event); // check if this event came from this account in the first place if (pSource && pSource->getHandler() != this) { // nope, a session was closed on some other account; ignore this... return; } UT_return_if_fail(!pSource); // we shouldn't receive these events over the wire on this backend UT_DEBUGMSG(("Disconnecting the tube for room with session id %s\n", cse.getSessionId().utf8_str())); TelepathyChatroomPtr pChatroom = _getChatroom(cse.getSessionId()); UT_return_if_fail(pChatroom); pChatroom->stop(); } break; case PCT_DisjoinSessionEvent: { UT_DEBUGMSG(("Got a PCT_DisjoinSessionEvent\n")); const DisjoinSessionEvent dse = static_cast<const DisjoinSessionEvent&>(event); // check if this event came from this account in the first place if (pSource && pSource->getHandler() != this) { // nope, a session was closed on some other account; ignore this... return; } UT_return_if_fail(!pSource); // we shouldn't receive these events over the wire on this backend UT_DEBUGMSG(("Disconnecting the tube for room with session id %s\n", dse.getSessionId().utf8_str())); TelepathyChatroomPtr pChatroom = _getChatroom(dse.getSessionId()); UT_return_if_fail(pChatroom); pChatroom->stop(); } break; default: // I think we can ignore all other signals on this backend, at // least for now break; } }
void AccountHandler::_handlePacket(Packet* packet, BuddyPtr buddy) { // packet and buddy must always be set UT_return_if_fail(packet); UT_return_if_fail(buddy); // as must the session manager AbiCollabSessionManager* pManager = AbiCollabSessionManager::getManager(); UT_return_if_fail(pManager); // manager didn't handle it, see what we can do switch (packet->getClassType()) { case PCT_JoinSessionRequestEvent: { JoinSessionRequestEvent* jse = static_cast<JoinSessionRequestEvent*>(packet); // lookup session AbiCollab* pSession = pManager->getSessionFromSessionId(jse->getSessionId()); UT_return_if_fail(pSession); // check if this buddy is allowed to access this document // TODO: this should be done for every session packet, not just join session packets if (!hasAccess(pSession->getAcl(), buddy)) { // we should only reach this point if someone is brute forcing trying // out session IDs while not being on the ACL. Ban this uses. UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN); return; } // lookup exporter ABI_Collab_Export* pExport = pSession->getExport(); UT_return_if_fail(pExport); // lookup adjusts const UT_GenericVector<ChangeAdjust *>* pExpAdjusts = pExport->getAdjusts(); UT_return_if_fail(pExpAdjusts); PD_Document* pDoc = pSession->getDocument(); // add this author to the document if we don't recognize him UT_sint32 iAuthorId = -1; UT_UTF8String buddyDescriptor = buddy->getDescriptor(); UT_GenericVector<pp_Author*> authors = pDoc->getAuthors(); UT_DEBUGMSG(("Scanning %d authors to see if we recognize this buddy\n", authors.getItemCount())); for (UT_sint32 i = 0; i < authors.getItemCount(); i++) { pp_Author* pAuthor = authors.getNthItem(i); UT_continue_if_fail(pAuthor); const gchar* szDescriptor = NULL; pAuthor->getProperty("abicollab-descriptor", szDescriptor); if (!szDescriptor) continue; if (buddyDescriptor != szDescriptor) continue; // yay, we know this author! iAuthorId = pAuthor->getAuthorInt(); UT_DEBUGMSG(("Found known author with descriptior %s, id %d!\n", buddyDescriptor.utf8_str(), iAuthorId)); break; } if (iAuthorId == -1) { // we don't know this author yet, create a new author object for him iAuthorId = pDoc->findFirstFreeAuthorInt(); pp_Author * pA = pDoc->addAuthor(iAuthorId); PP_AttrProp * pPA = pA->getAttrProp(); pPA->setProperty("abicollab-descriptor", buddyDescriptor.utf8_str()); pDoc->sendAddAuthorCR(pA); UT_DEBUGMSG(("Added a new author to the documument with descriptor %s, id %d\n", buddyDescriptor.utf8_str(), iAuthorId)); } // serialize entire document into string JoinSessionRequestResponseEvent jsre(jse->getSessionId(), iAuthorId); if (AbiCollabSessionManager::serializeDocument(pDoc, jsre.m_sZABW, false /* no base64 */) == UT_OK) { // set more document properties jsre.m_iRev = pDoc->getCRNumber(); jsre.m_sDocumentId = pDoc->getDocUUIDString(); if (pDoc->getFilename()) jsre.m_sDocumentName = UT_go_basename_from_uri(pDoc->getFilename()); // send to buddy! send(&jsre, buddy); // add this buddy to the collaboration session pSession->addCollaborator(buddy); } break; } case PCT_JoinSessionRequestResponseEvent: { JoinSessionRequestResponseEvent* jsre = static_cast<JoinSessionRequestResponseEvent*>( packet ); PD_Document* pDoc = 0; if (AbiCollabSessionManager::deserializeDocument(&pDoc, jsre->m_sZABW, false) == UT_OK) { if (pDoc) { // NOTE: we could adopt the same document name here, but i'd // rather not at the moment - MARCM pDoc->forceDirty(); if (jsre->m_sDocumentName.size() > 0) { gchar* fname = g_strdup(jsre->m_sDocumentName.utf8_str()); pDoc->setFilename(fname); } // The default ownership when joining is FALSE, as that seems // to make sense for the generic case. The person sharing the // document by default owns the document (and is thus allowed // to modify the ACL). pManager->joinSession(jsre->getSessionId(), pDoc, jsre->m_sDocumentId, jsre->m_iRev, jsre->getAuthorId(), buddy, this, false, NULL); } else { UT_DEBUGMSG(("AccountHandler::_handlePacket() - deserializing document failed!\n")); } } break; } case PCT_GetSessionsEvent: { GetSessionsResponseEvent gsre; const UT_GenericVector<AbiCollab *> sessions = pManager->getSessions(); for (UT_sint32 i = 0; i < sessions.getItemCount(); i++) { AbiCollab* pSession = sessions.getNthItem(i); if (pSession && pSession->isLocallyControlled()) { // check if the buddy has access to this session if (!hasAccess(pSession->getAcl(), buddy)) { UT_DEBUGMSG(("Buddy %s denied access to session %s by ALC\n", buddy->getDescriptor(true).utf8_str(), pSession->getSessionId().utf8_str())); continue; } const PD_Document * pDoc = pSession->getDocument(); UT_continue_if_fail(pDoc); // determine name UT_UTF8String documentBaseName; if (pDoc->getFilename()) documentBaseName = UT_go_basename_from_uri(pDoc->getFilename()); // set session info gsre.m_Sessions[ pSession->getSessionId() ] = documentBaseName; } } send(&gsre, buddy); break; } case PCT_GetSessionsResponseEvent: { GetSessionsResponseEvent* gsre = static_cast<GetSessionsResponseEvent*>( packet ); UT_GenericVector<DocHandle*> vDocHandles; for (std::map<UT_UTF8String,UT_UTF8String>::iterator it=gsre->m_Sessions.begin(); it!=gsre->m_Sessions.end(); ++it) { DocHandle* pDocHandle = new DocHandle((*it).first, (*it).second); vDocHandles.addItem(pDocHandle); } pManager->setDocumentHandles(buddy, vDocHandles); break; } default: { UT_DEBUGMSG(("Unhandled packet class: 0x%x\n", packet->getClassType())); UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN); break; } } }
void AbiCollabSessionManager::setDocumentHandles(BuddyPtr pBuddy, const UT_GenericVector<DocHandle*>& vDocHandles) { UT_DEBUGMSG(("Setting document handles for buddy %s\n", pBuddy->getDescriptor().utf8_str())); UT_return_if_fail(pBuddy); // create a copy of the current document handles, which // we'll use to determine which document handles do not exist anymore std::vector<DocHandle*> oldDocHandles(pBuddy->getDocHandles()); for (UT_sint32 i = 0; i < vDocHandles.size(); i++) { DocHandle* pDocHandle = vDocHandles.getNthItem(i); UT_continue_if_fail(pDocHandle); // sanity checking UT_UTF8String sId = pDocHandle->getSessionId(); UT_continue_if_fail(sId.size() > 0); // construct a nice document name UT_UTF8String sDocumentName = pDocHandle->getName(); if (sDocumentName.size() == 0) { // this document has no name yet; give it an untitled name const XAP_StringSet * pSS = XAP_App::getApp()->getStringSet(); std::string sUntitled; pSS->getValueUTF8(XAP_STRING_ID_UntitledDocument, sUntitled); UT_UTF8String_sprintf(sDocumentName, sUntitled.c_str(), 0); // TODO: as should append a number here, but at the moment // XAP_Frame::m_iUntitled is not accessible from here } // check to see if we already have a document handle with this ID DocHandle* pCurDocHandle = pBuddy->getDocHandle(sId); if (!pCurDocHandle) { // Ok, all set. Get the buddy from the AccountHandler, and assign // the document handle to the buddy DocHandle * pNewDocHandle = new DocHandle(sId, sDocumentName); pBuddy->addDocHandle(pNewDocHandle); UT_DEBUGMSG(("Added DocHandle (%s) to buddy (%s)\n", sId.utf8_str(), pBuddy->getDescription().utf8_str())); // signal that a buddy has a new session AccountBuddyAddDocumentEvent event(pNewDocHandle); signal(event, pBuddy); } else { UT_DEBUGMSG(("Found an existing DocHandle (%s) for buddy (%s)\n", sId.utf8_str(), pBuddy->getDescription().utf8_str())); // we already have a handle for this document, remove it from the old document handles copy for (std::vector<DocHandle*>::iterator it = oldDocHandles.begin(); it != oldDocHandles.end(); it++) { DocHandle* pOldDocHandle = *it; if (pCurDocHandle == pOldDocHandle) { oldDocHandles.erase(it); break; } } } } // every document that is still in the old document handles list does not // exist anymore, so let's delete it std::vector<DocHandle*>::iterator it = oldDocHandles.begin(); while (it != oldDocHandles.end()) { DocHandle* pDocHandle = *it; UT_continue_if_fail(pDocHandle); // TODO: when we are a part of this session, then handle that properly UT_DEBUGMSG(("Purging existing DocHandle (%s) for buddy (%s)\n", pDocHandle->getSessionId().utf8_str(), pBuddy->getDescription().utf8_str())); UT_UTF8String pDestroyedSessionId = pDocHandle->getSessionId(); pBuddy->destroyDocHandle(pDestroyedSessionId); CloseSessionEvent event(pDestroyedSessionId); signal(event, pBuddy); it = oldDocHandles.erase(it); } }
bool AbiCollabSessionManager::processPacket(AccountHandler& /*handler*/, Packet* packet, BuddyPtr buddy) { UT_DEBUGMSG(("AbiCollabSessionManager::processPacket()\n")); UT_return_val_if_fail(packet, false); UT_return_val_if_fail(buddy, false); // check if this is a simple import-meh-now-packet PClassType pct = packet->getClassType(); if (pct >= _PCT_FirstSessionPacket && pct <= _PCT_LastSessionPacket) { // lookup session SessionPacket* dsp = static_cast<SessionPacket*>( packet ); const UT_UTF8String& sessionId = dsp->getSessionId(); AbiCollab* pAbiCollab = getSessionFromSessionId(sessionId); if (!pAbiCollab) { UT_DEBUGMSG(("Unknown session id: '%s'", sessionId.utf8_str())); UT_return_val_if_fail(pAbiCollab, true); } // handle packet! pAbiCollab->import(dsp, buddy); return true; } // handle packet switch (pct) { case PCT_StartSessionEvent: { StartSessionEvent event; event.setBroadcast(true); signal(event, buddy); return true; } case PCT_JoinSessionEvent: { JoinSessionEvent* jse = static_cast<JoinSessionEvent*>(packet); const UT_UTF8String& joinedSessionId = jse->getSessionId(); // someone who joined this session disconnected, remove him from the collaboration session AbiCollab* pSession = getSessionFromSessionId(joinedSessionId); if (pSession) { if (isLocallyControlled( pSession->getDocument() )) { // we should already know this buddy, as we sent should have already added this // buddy when responding to his JoinSessionRequest // TODO: check this } // signal all JoinSessionEvent event(joinedSessionId); signal( event, buddy ); } else { // we don't know this session, don't forward the packet UT_ASSERT_HARMLESS(UT_NOT_REACHED); } return true; } case PCT_DisjoinSessionEvent: { DisjoinSessionEvent* dse = static_cast<DisjoinSessionEvent*>(packet); const UT_UTF8String& disjoinedSessionId = dse->getSessionId(); // someone who joined this session disconnected, remove him from the collaboration session AbiCollab* pSession = getSessionFromSessionId(disjoinedSessionId); if (pSession) { pSession->removeCollaborator(buddy); // signal all DisjoinSessionEvent event(disjoinedSessionId); signal(event, buddy); } else { // we don't know this session, don't forward the packet UT_ASSERT_HARMLESS(UT_NOT_REACHED); } return true; } case PCT_CloseSessionEvent: { CloseSessionEvent* cse = static_cast<CloseSessionEvent*>(packet); const UT_UTF8String& destroyedSessionId = cse->getSessionId(); buddy->destroyDocHandle( destroyedSessionId ); // handle the event outselves AbiCollab* pSession = getSessionFromSessionId(destroyedSessionId); if (pSession) { if (!isLocallyControlled(pSession->getDocument())) { std::string docName = pSession->getDocument()->getFilename(); if (docName == "") docName = "Untitled"; // TODO: fetch the title from the frame somehow (which frame?) - MARCM // the server hosting this session is gone, so let's disconnect as well if (!destroySession(pSession)) { UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN); } // signal all CloseSessionEvent event( destroyedSessionId ); signal( event, buddy ); // inform the user of the disconnect XAP_Frame *pFrame = XAP_App::getApp()->getLastFocussedFrame(); UT_return_val_if_fail(pFrame, true); UT_UTF8String msg; // TODO: make this localizable UT_UTF8String_sprintf(msg, "Document %s is not being shared anymore by buddy %s. You are disconnected from the collaboration session.", docName.c_str(), buddy->getDescription().utf8_str()); pFrame->showMessageBox(msg.utf8_str(), XAP_Dialog_MessageBox::b_O, XAP_Dialog_MessageBox::a_OK); } else { // someone who is not controlling this session sends out messages he closed it! // we will not forward this packet UT_ASSERT_HARMLESS(UT_NOT_REACHED); } } else { UT_DEBUGMSG(("Ignoring a CloseSession event for unknown session (%s)\n", destroyedSessionId.utf8_str())); } return true; } case PCT_AccountAddBuddyRequestEvent: { // look at this packet; I have a feeling we need to deprecate it - MARCM UT_ASSERT_HARMLESS(UT_NOT_IMPLEMENTED); return true; } default: break; } return false; }
void AbiCollabSessionManager::storeProfile() { UT_DEBUGMSG(("AbiCollabSessionManager::storeProfile()\n")); xmlBufferPtr doc = xmlBufferCreate(); if (doc) { xmlTextWriterPtr writer = xmlNewTextWriterMemory(doc, 0); if (writer) { int rc = xmlTextWriterStartDocument(writer, NULL, "UTF-8", NULL); if (rc >= 0) { // TODO: one could check every return value here, but I'm lazy right now xmlTextWriterStartElement(writer, reinterpret_cast<const xmlChar*>("AbiCollabProfile")); for (UT_uint32 i = 0; i < m_vecAccounts.size(); i++) { AccountHandler* pHandler = m_vecAccounts[i]; UT_continue_if_fail(pHandler); xmlTextWriterStartElement(writer, reinterpret_cast<const xmlChar*>("AccountHandler")); // write out the account handler type xmlTextWriterWriteAttribute(writer, reinterpret_cast<const xmlChar*>("type"), BAD_CAST pHandler->getStorageType().utf8_str()); // write out the account handler properties for (PropertyMap::const_iterator cit = pHandler->getProperties().begin(); cit != pHandler->getProperties().end(); cit++) { xmlTextWriterWriteElement(writer, BAD_CAST cit->first.c_str(), BAD_CAST BAD_CAST cit->second.c_str()); } // write out the account handler buddies xmlTextWriterStartElement(writer, reinterpret_cast<const xmlChar*>("buddies")); for (UT_uint32 j = 0; j < pHandler->getBuddies().size(); j++) { BuddyPtr pBuddy = pHandler->getBuddies()[j]; UT_continue_if_fail(pBuddy); if (!pBuddy->isVolatile()) { // we need to be able to store buddy properties UT_ASSERT_HARMLESS(UT_NOT_IMPLEMENTED); /*xmlTextWriterStartElement(writer, reinterpret_cast<const xmlChar*>("buddy")); // write out the buddy properties // TODO: for now, the only useful property a buddy has is its "name"; // However in the future we really should write out a generic property list xmlTextWriterWriteElement( writer, reinterpret_cast<const xmlChar*>("name"), reinterpret_cast<const xmlChar*>(pBuddy->getName().utf8_str()) ); xmlTextWriterEndElement(writer);*/ /* end buddy */ } } xmlTextWriterEndElement(writer); /* end buddies */ xmlTextWriterEndElement(writer); /* end AccountHandler */ } xmlTextWriterEndElement(writer); /* end AbiCollabProfile */ } xmlTextWriterEndDocument(writer); xmlFreeTextWriter(writer); gchar * s = g_build_filename(XAP_App::getApp()->getUserPrivateDirectory(), "AbiCollab.Profile",NULL); UT_UTF8String profile(s); FREEP(s); char *uri = UT_go_filename_to_uri(profile.utf8_str()); GError* error = 0; GsfOutput* out = UT_go_file_create (uri, &error); if (out) { gsf_output_write(out, strlen(reinterpret_cast<const char*>(const_cast<const xmlChar*>(doc->content))), reinterpret_cast<const guint8*>(const_cast<const xmlChar*>(doc->content)) ); gsf_output_close(out); g_object_unref(G_OBJECT(out)); } else { UT_DEBUGMSG(("Error creating AbiCollab Profile %s: %s!\n", profile.utf8_str(), error ? error->message : "unknown error")); } FREEP(uri); } else { UT_DEBUGMSG(("Error creating XML output writer\n")); } xmlBufferFree(doc); } else { UT_DEBUGMSG(("Error creating XML output buffer\n")); } }
void AbiCollab::import(SessionPacket* pPacket, BuddyPtr collaborator) { UT_DEBUGMSG(("AbiCollab::import()\n")); UT_return_if_fail(pPacket); if (m_bDoingMouseDrag) { // we block incoming packets while dragging the mouse; this prevents // scary race conditions from occuring, like importing a 'delete image' packet // when you are just dragging said image around UT_DEBUGMSG(("We are currently dragging something around; deferring packet import until after the release!\n")); m_vIncomingQueue.push_back( std::make_pair(static_cast<SessionPacket*>(pPacket->clone()), collaborator)); return; } // record the incoming packet if (m_pRecorder) m_pRecorder->storeIncoming(pPacket, collaborator); // execute an alternative packet handling path when this session is being // taken over by another collaborator if (AbstractSessionTakeoverPacket::isInstanceOf(*pPacket)) { AbstractSessionTakeoverPacket* astp = static_cast<AbstractSessionTakeoverPacket*>(pPacket); bool res = _handleSessionTakeover(astp, collaborator); if (!res) { // TODO: implement/handle an offending collaborator UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN); } return; } /* Session packets are only allowed to come in from a collaborator when: 1. no session takeover is in progress, or 2a. if this session is a slave: always 2b. if this session is a master: until the collaborator has responded to a SessionTakeoverRequest from us with a SessionTakeoverAck packet */ // TODO: implement/handle an offending collaborator UT_return_if_fail( (m_eTakeoveState == STS_NONE) || (!isLocallyControlled()) || (isLocallyControlled() && m_eTakeoveState == STS_SENT_TAKEOVER_REQUEST && !_hasAckedSessionTakeover(collaborator)) ); // import the packet; note that it might be denied due to collisions maskExport(); if (AbstractChangeRecordSessionPacket::isInstanceOf(*pPacket)) m_pActivePacket = static_cast<const AbstractChangeRecordSessionPacket*>(pPacket); m_vCollaborators[collaborator] = pPacket->getDocUUID().utf8_str(); // FIXME: this is redunant after we set this the first time m_Import.import(*pPacket, collaborator); m_pActivePacket = NULL; const std::vector<SessionPacket*>& maskedPackets = unmaskExport(); if (isLocallyControlled() && maskedPackets.size() > 0) { UT_DEBUGMSG(("Forwarding message (%u packets) from %s\n", maskedPackets.size(), collaborator->getDescription().utf8_str())); // It seems we are in the center of a collaboration session. // It's our duty to reroute the packets to the other collaborators for (std::map<BuddyPtr, std::string>::iterator it = m_vCollaborators.begin(); it != m_vCollaborators.end(); it++) { // send all masked packets during import to everyone, except to the // person who initialy sent us the packet BuddyPtr pBuddy = (*it).first; UT_continue_if_fail(pBuddy); if (pBuddy != collaborator) { UT_DEBUGMSG(("Forwarding message from %s to %s\n", collaborator->getDescription().utf8_str(), pBuddy->getDescription().utf8_str())); for (std::vector<SessionPacket*>::const_iterator cit = maskedPackets.begin(); cit != maskedPackets.end(); cit++) { SessionPacket* maskedPacket = (*cit); push(maskedPacket, pBuddy); } } } } }
// returns true if the import can continue, false otherwise bool ABI_Collab_Import::_handleCollision(UT_sint32 iIncomingRev, UT_sint32 iLocalRev, BuddyPtr pCollaborator) { UT_DEBUGMSG(("_handleCollision() - incoming rev %d collides against local rev %d!!!\n", iIncomingRev, iLocalRev)); UT_return_val_if_fail(pCollaborator, false); if (m_pAbiCollab->isLocallyControlled()) { UT_DEBUGMSG(("We're controlling this session, refusing this changerecord from %s!\n", pCollaborator->getDescription().utf8_str())); // add this collaborator to our revert ack list, so we can ignore his packets // until we get an acknoledgement that he has reverted his local, colliding changes m_revertSet.push_back(std::make_pair(pCollaborator, iIncomingRev)); // send the revert command to the collaborator RevertSessionPacket rsp(m_pAbiCollab->getSessionId(), m_pDoc->getOrigDocUUIDString(), iIncomingRev); m_pAbiCollab->push(&rsp, pCollaborator); return false; } else { UT_DEBUGMSG(("We're NOT controlling this session, reverting local changes and accepting changerecord!\n")); ABI_Collab_Export* pExport = m_pAbiCollab->getExport(); UT_return_val_if_fail(pExport, false); UT_GenericVector<ChangeAdjust *>* pAdjusts = pExport->getAdjusts(); UT_return_val_if_fail(pAdjusts, false); m_pAbiCollab->setIsReverting(true); // mask all changes in the exporter // undo our cool local changes, and nuke our exported packet list as well up to (and including) iLocalRev for (UT_sint32 i = pAdjusts->getItemCount() - 1; i >= 0; i--) { ChangeAdjust* pChange = pAdjusts->getNthItem(i); if (pChange) { if (pChange->getLocalRev() >= iLocalRev) { if (strcmp(m_pDoc->getOrigDocUUIDString(), pChange->getRemoteDocUUID().utf8_str()) == 0) { UT_DEBUGMSG(("UNDO-ING AND NUKING LOCAL CHANGE: EXPORT POSITION %d, pChange->m_iCRNumber: %d!\n", i, pChange->getLocalRev())); // undo the change locally m_pDoc->undoCmd(1); // fix up the positions on the change stack for (UT_sint32 j = i+1; j < pAdjusts->getItemCount(); j++) { ChangeAdjust* pC = pAdjusts->getNthItem(j); if (pC) { UT_DEBUGMSG(("Looking at fixing up the position of change pos %d\n", j)); if (pChange->getLocalPos() < pC->getLocalPos()) { UT_DEBUGMSG(("Adjusting change pos %d from m_iDocPos: %d to m_iDocPos: %d\n", j, pC->getLocalPos(), pC->getLocalPos() - pChange->getLocalAdjust())); pC->setLocalPos(pC->getLocalPos() - pChange->getLocalAdjust()); } else { UT_DEBUGMSG(("No need to adjust change pos %d\n", j)); } } else { UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN); } } // kill off the item pAdjusts->deleteNthItem(i); delete pChange; } else { UT_DEBUGMSG(("Skipping undo of remote change\n")); } } else break; } else { UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN); } } m_pAbiCollab->setIsReverting(false); // unmask all changes in the exporter UT_DEBUGMSG(("Pre-Acknowledging revert of revision %d\n", iLocalRev)); // send the revert acknowledgement command to the session owner RevertAckSessionPacket rasp(m_pAbiCollab->getSessionId(), m_pDoc->getOrigDocUUIDString(), iLocalRev); m_pAbiCollab->push(&rasp, pCollaborator); m_iAlreadyRevertedRevs.push_back(iLocalRev); return true; } }
bool AbiCollab::_handleSessionTakeover(AbstractSessionTakeoverPacket* pPacket, BuddyPtr collaborator) { UT_DEBUGMSG(("AbiCollab::_handleSessionTakeover()\n")); UT_return_val_if_fail(pPacket, false); UT_return_val_if_fail(collaborator, false); AbiCollabSessionManager* pManager = AbiCollabSessionManager::getManager(); UT_return_val_if_fail(pManager, false); switch (m_eTakeoveState) { case STS_NONE: { // we only accept a SessionTakeoverRequest or MasterChangeRequest packet UT_return_val_if_fail(pPacket->getClassType() == PCT_SessionTakeoverRequestPacket, false); // we can only allow such a packet from the controller UT_return_val_if_fail(m_pController == collaborator, false); // handle the SessionTakeoverRequestPacket packet m_pProposedController = BuddyPtr(); m_vApprovedReconnectBuddies.clear(); SessionTakeoverRequestPacket* strp = static_cast<SessionTakeoverRequestPacket*>(pPacket); m_bProposedController = strp->promote(); if (m_bProposedController) { for (std::vector<std::string>::const_iterator cit = strp->getBuddyIdentifiers().begin(); cit != strp->getBuddyIdentifiers().end(); cit++) m_vApprovedReconnectBuddies[*cit] = false; } else { UT_return_val_if_fail(strp->getBuddyIdentifiers().size() == 1, false); BuddyPtr pBuddy = pManager->constructBuddy(strp->getBuddyIdentifiers()[0], collaborator); UT_return_val_if_fail(pBuddy, false); m_pProposedController = pBuddy; } // inform the master that we received the takeover request SessionTakeoverAckPacket stap(m_sId, m_pDoc->getDocUUIDString()); collaborator->getHandler()->send(&stap, collaborator); m_eTakeoveState = STS_SENT_TAKEOVER_ACK; return true; } return false; case STS_SENT_TAKEOVER_REQUEST: { // we only accept SessionTakeoverAck packets UT_return_val_if_fail(pPacket->getClassType() == PCT_SessionTakeoverAckPacket, false); // we can only receive SessionTakeoverAck packets when we are the master UT_return_val_if_fail(!m_pController, false); // we should have a proposed master UT_return_val_if_fail(m_pProposedController, false); // a slave should only ack once UT_return_val_if_fail(!_hasAckedSessionTakeover(collaborator), false); // handle the SessionTakeoverAck packet m_mAckedSessionTakeoverBuddies[collaborator] = true; // check if every slave has acknowledged the session takeover // TODO: handle dropouts if (m_vCollaborators.size() == 1 || m_mAckedSessionTakeoverBuddies.size() == m_vCollaborators.size()) { // ... our tour of duty is done _shutdownAsMaster(); m_eTakeoveState = STS_NONE; return true; } } return true; case STS_SENT_TAKEOVER_ACK: // we only accept a SessionFlushed or SessionReconnectRequest packet UT_return_val_if_fail( pPacket->getClassType() == PCT_SessionFlushedPacket || pPacket->getClassType() == PCT_SessionReconnectRequestPacket, false ); if (pPacket->getClassType() == PCT_SessionReconnectRequestPacket) { // we only accept a SessionReconnectRequest when we are the proposed master UT_return_val_if_fail(m_bProposedController, false); // we only allow an incoming SessionReconnectRequest packet from a buddy // that is in the buddy list we received from the master, and we didn't receive // such a packet from him before bool allow = false; for (std::map<std::string, bool>::iterator it = m_vApprovedReconnectBuddies.begin(); it != m_vApprovedReconnectBuddies.end(); it++) { // TODO: is it a good idea to compare descriptors with full session information? if ((*it).first == collaborator->getDescriptor(true) && (*it).second == false) { (*it).second = true; allow = true; break; } } UT_return_val_if_fail(allow, false); // handle the SessionReconnectRequest packet addCollaborator(collaborator); _checkRestartAsMaster(); return true; } else if (pPacket->getClassType() == PCT_SessionFlushedPacket) { // we can only allow a SessionFlushed packet from the controller UT_return_val_if_fail(m_pController == collaborator, false); // handle the SessionFlushed packet m_bSessionFlushed = true; if (m_bProposedController) { // as far we we're concerned now, the old master is dead _becomeMaster(); _checkRestartAsMaster(); return true; } else { // as far we we're concerned now, the old master is dead _switchMaster(); // inform the new master that we want to rejoin the session SessionReconnectRequestPacket srrp(m_sId, m_pDoc->getDocUUIDString()); m_pProposedController->getHandler()->send(&srrp, m_pProposedController); m_eTakeoveState = STS_SENT_SESSION_RECONNECT_REQUEST; } return true; } return false; case STS_SENT_SESSION_RECONNECT_REQUEST: { // we only accept a SessionReconnectAck packet UT_return_val_if_fail(pPacket->getClassType() == PCT_SessionReconnectAckPacket, false); // we only accept said packet when we are a slave UT_return_val_if_fail(m_pController, false); // we only accept said packet when we are not the proposed master UT_return_val_if_fail(!m_bProposedController, false); // we only accept said packet from the proposed master UT_return_val_if_fail(m_pProposedController == collaborator, false); // handle the SessionReconnectAck packet SessionReconnectAckPacket* srap = static_cast<SessionReconnectAckPacket*>(pPacket); // Nuke the current collaboration state, and restart with the // given revision from the proposed master UT_return_val_if_fail(_restartAsSlave(srap->getDocUUID(), srap->getRev()), false); } return true; default: UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN); break; } return false; }