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::_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);
	}
}
예제 #3
0
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);
}
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;
}
/*!
 *	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::_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));
}
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 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;
		}
	}
}
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;
}
예제 #11
0
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);
	}
}