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;
}
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
		}
	}
}
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 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
}
Exemplo n.º 5
0
// 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);
}
Exemplo n.º 6
0
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);
}
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 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 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;
            }
        }
    }
}
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;
}
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;
}
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;
	}
}