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);
	}
}
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 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()
}
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;
}
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;
}
// 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;
	}
}
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);
                }
            }
        }
    }
}
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
}
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::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);
	}
}