/***************************************************************************
                          cdownloadmanager.cpp  -  description
                             -------------------
    begin                : Don Mai 16 2002
    copyright            : (C) 2002-2005 by Mathias Küster
    email                : mathen@users.berlios.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "cdownloadmanager.h"

#include <stdio.h>
#include <math.h>

#include "dcobject.h"
#include "cconfig.h"
#include "core/cdir.h"
#include "core/cfile.h"
#include "cconnectionmanager.h"
#include "cdownloadqueue.h"
#include "core/clogfile.h"
#include "core/cmanager.h"
#include "cfilemanager.h"
#include "dclib.h"
#include "core/platform.h"
#include "csearchmanager.h"
#include "cutils.h"
#include "core/ccallback.h"
#include "core/cnetaddr.h"

extern "C" {
#include "gnulib/md5.h"
}

#ifdef WIN32
#include <winsock2.h>
#else
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#endif

#define TEST_CHUNK_SIZE	(10*1024)

/* search results by TTH are now returned in a set */
#include <set>
#include <list>

/** */
CDownloadManager::CDownloadManager()
{
	m_nID            = 0;
	m_eShutdownState = essNONE;

	m_tDownloadQueueTimeout  = time(0);
	m_tUpdateTransferTimeout = time(0);
	m_tHubSearchTimeout	 = time(0);
	m_tSearchTimeout	 = 0;
	m_tLastRateExtra	 = time(0);
	m_tWaitListCleaned       = time(0);

	m_pTransfersMutex    = new CMutex();
	m_pTransferList      = new CStringList<CTransferObject>();
	m_pBanListMutex      = new CMutex();
	m_pTransferBanList   = new CStringList<DCTransferBanObject>();
	m_pExtraSlotsMutex   = new CMutex();
	m_pExtraUserSlotList = new CList<CExtraUserSlot>();
	m_pWaitListMutex     = new CMutex();
	m_pTransferWaitList  = new CList<DCTransferWait>();
	m_pSearchList	     = new CList<CMessageSearchResult>();
	m_pSearchQueryList   = new CList<CDCMessage>();

	m_pDownloadQueue = new CDownloadQueue();

	m_pCallback = 0;
}

/** */
CDownloadManager::~CDownloadManager()
{
	SetInstance(0);
	CManager::Instance()->Remove( m_pCallback );
	delete m_pCallback;
	m_pCallback = 0;

	delete m_pExtraUserSlotList;
	m_pExtraUserSlotList = 0;

	delete m_pExtraSlotsMutex;
	m_pExtraSlotsMutex = 0;

	delete m_pTransferList;
	m_pTransferList = 0;

	delete m_pTransfersMutex;
	m_pTransfersMutex = 0;

	delete m_pTransferWaitList;
	m_pTransferWaitList = 0;

	delete m_pWaitListMutex;
	m_pWaitListMutex = 0;

	delete m_pTransferBanList;
	m_pTransferBanList = 0;

	delete m_pBanListMutex;
	m_pBanListMutex = 0;

	delete m_pSearchList;
	m_pSearchList = 0;

	delete m_pSearchQueryList;
	m_pSearchQueryList = 0;
	
	delete m_pDownloadQueue;
	m_pDownloadQueue = 0;
}

/** load the queue */
int CDownloadManager::DLM_LoadQueue()
{
	int err = -1;
	CStringList<DCTransferQueueObject> * StringList = 0;

	m_pDownloadQueue->pQueueMutex->Lock();
	m_pDownloadQueue->pQueue->Clear();
	m_pDownloadQueue->pChunksMutex->Lock();
	m_pDownloadQueue->pChunkList->Clear();

	if ( CConfig::Instance() )
		err = CConfig::Instance()->LoadDCTra( m_pDownloadQueue->pQueue, m_pDownloadQueue->pChunkList );

	// send all files over the callback function
	if ( err == 0 )
	{
		while( m_pDownloadQueue->pQueue->Next( &StringList) )
		{
			DCTransferQueueObject * TransferObject = 0;

			while( StringList->Next( &TransferObject) )
			{
				DCTransferFileObject * TransferFileObject = 0;

				while( TransferObject->pTransferFileList.Next( &TransferFileObject) )
				{
					SendFileInfo( TransferObject, TransferFileObject );
				}
			}
		}
	}

	m_pCallback = new CCallback0<CDownloadManager>( this, &CDownloadManager::Callback );
	
	if ( CManager::Instance() )
		CManager::Instance()->Add( m_pCallback );

	m_pDownloadQueue->pChunksMutex->UnLock();
	m_pDownloadQueue->pQueueMutex->UnLock();

	return err;
}

/** save the queue  */
int CDownloadManager::DLM_SaveQueue()
{
	int err = -1;

	m_pDownloadQueue->pQueueMutex->Lock();
	m_pDownloadQueue->pChunksMutex->Lock();

	if ( CConfig::Instance() )
		err = CConfig::Instance()->SaveDCTra( m_pDownloadQueue->pQueue, m_pDownloadQueue->pChunkList );

	m_pDownloadQueue->pChunksMutex->UnLock();
	m_pDownloadQueue->pQueueMutex->UnLock();

	return err;
}

/** disconnect all transfers for a clean shutdown */
void CDownloadManager::DLM_Shutdown()
{
	CTransferObject * TransferObject = 0;

	// set shutdown state
	m_eShutdownState = essSHUTDOWN;

	SendLogInfo("Shutdown download manager ...\n");

	m_pTransfersMutex->Lock();

	DPRINTF("Running Transfers: %ld\n",m_pTransferList->Count());

	while( m_pTransferList->Next( &TransferObject ) )
	{
		TransferObject->m_pTransfer->Disconnect(true);
	}

	m_pTransfersMutex->UnLock();
}

/** add a extra slot to the user */
void CDownloadManager::DLM_AddUserSlot( CString nick, CString hubname, int slot, bool permanent )
{
	CExtraUserSlot * ExtraUserSlot;

	m_pExtraSlotsMutex->Lock();

	ExtraUserSlot = 0;

	while( (ExtraUserSlot=m_pExtraUserSlotList->Next(ExtraUserSlot)) != 0 )
	{
		if ( (ExtraUserSlot->sNick == nick) && (ExtraUserSlot->sHubName == hubname) )
		{
			if ( slot == 0 )
			{
				ExtraUserSlot->iSlots  = slot;
			}
			else
			{
				ExtraUserSlot->iSlots += slot;
			}
			ExtraUserSlot->bPermanent  = permanent;
			break;
		}
	}

	if ( ExtraUserSlot == 0 )
	{
		ExtraUserSlot = new CExtraUserSlot();
		ExtraUserSlot->sNick      = nick;
		ExtraUserSlot->sHubName   = hubname;
		ExtraUserSlot->iSlots     = slot;
		ExtraUserSlot->bPermanent = permanent;

		m_pExtraUserSlotList->Add(ExtraUserSlot);
	}

	SendSlotInfo(ExtraUserSlot);

	if ( (ExtraUserSlot->iSlots == 0) && (!ExtraUserSlot->bPermanent) )
	{
		m_pExtraUserSlotList->Del(ExtraUserSlot);
	}

	m_pExtraSlotsMutex->UnLock();
}

/** */
bool CDownloadManager::DLM_TransferConnect( CString nick, CString hubname )
{
	bool res = false;

	m_pDownloadQueue->pQueueMutex->Lock();

	DCTransferQueueObject * TransferObject = 0;

	if ( (TransferObject = m_pDownloadQueue->GetUserTransferObject( nick, hubname, CString() )) != 0 )
	{
		TransferObject->tTimeout = 0;
		res = true;
	}

	m_pDownloadQueue->pQueueMutex->UnLock();

	return res;
}

/** */
bool CDownloadManager::DLM_TransferClose( ulonglong transferid )
{
	CTransferObject * TransferObject = 0;
	bool res = false;

	m_pTransfersMutex->Lock();

	if ( m_pTransferList->Get( CString::number(transferid), &TransferObject ) == 0 )
	{
		TransferObject->m_pTransfer->Disconnect(true);
		res = true;
	}

	m_pTransfersMutex->UnLock();

	return res;
}

/** */
bool CDownloadManager::DLM_TransferSetRate( ulonglong transferid, ulonglong rate )
{
	CTransferObject * TransferObject = 0;
	bool res = false;

	m_pTransfersMutex->Lock();

	if ( m_pTransferList->Get( CString::number(transferid), &TransferObject ) == 0 )
	{
		TransferObject->m_pTransfer->SetRate(rate);
		res = true;
	}

	m_pTransfersMutex->UnLock();

	return res;
}

/** */
bool CDownloadManager::DLM_TransferGetRate( ulonglong transferid, ulonglong & rate )
{
	CTransferObject * TransferObject = 0;
	bool res = false;

	m_pTransfersMutex->Lock();

	if ( m_pTransferList->Get( CString::number(transferid), &TransferObject ) == 0 )
	{
		rate = TransferObject->m_pTransfer->GetRate();
		res = true;
	}

	m_pTransfersMutex->UnLock();

	return res;
}

/** */
eDirection CDownloadManager::DLM_TransferDirection( ulonglong transferid )
{
	CTransferObject * TransferObject = 0;
	eDirection direction=edNONE;

	m_pTransfersMutex->Lock();

	if ( m_pTransferList->Get( CString::number(transferid), &TransferObject ) == 0 )
	{
		direction = TransferObject->m_pTransfer->GetSrcDirection();
	}

	m_pTransfersMutex->UnLock();

	return direction;
}

/** */
void CDownloadManager::DLM_QueueAdd( CString nick, CString hubname, CString hubhost,
				CString remotename, CString localname,
				CString localpath, CString localrootpath,
				eltMedium medium, ulonglong size,
				ulonglong startposition,
				ulonglong endposition,
				CString hash,
				bool multi )
{
	CDir dir;
	CString sfile;
	DCTransferQueueObject * TransferObject;
	DCTransferFileObject * TransferFileObject = 0;
	DCHubObject * HubObject;
	
	/*
	 * Although this may not be the best place for this, dclib got stuck in a loop of
	 * get next chunk ... no more chunks when I tried to download a zero byte file.
	 * This will mean zero byte files are never present in the queue, they are just
	 * immediately created.
	 */
	if ( (size == 0) && (medium == eltFILE) )
	{
		if ( startposition != 0 )
		{
			CString logmsg = "Warning: 0B file has non-zero start position of ";
			logmsg += CString::number(startposition);
			SendLogInfo( logmsg );
		}
		
		if ( endposition != 0 )
		{
			CString logmsg = "Warning: 0B file has non-zero end position of ";
			logmsg += CString::number(endposition);
			SendLogInfo( logmsg );
		}
		
		if ( localrootpath.IsEmpty() )
		{
			sfile = CConfig::Instance()->GetDownloadFinishedFolder();
			if ( sfile.IsEmpty() )
			{
				sfile = CConfig::Instance()->GetDownloadFolder();
			}
		}
		else
		{
			sfile = localrootpath;
		}
		
		localpath = localpath.Replace(':',"");
		localname = localname.Replace(':',"");
		
		sfile += '/';
		sfile += localpath;
		sfile += '/';
		
		sfile = CDir::SimplePath(sfile);
		
		if ( dir.IsDir( sfile, false ) == false )
		{
			if ( dir.CreatePath( sfile ) == false )
			{
				CString logmsg = "Create path '";
				logmsg += sfile;
				logmsg += "' for 0B file '";
				logmsg += localname;
				logmsg += "' failed";
				SendLogInfo( logmsg );
				return;
			}
		}
		
		sfile += localname;
		
		if ( dir.IsFile( sfile, false ) == false )
		{
			CFile cfile;
			const int mode = IO_RAW | IO_READONLY | IO_CREAT;
			
			/* From CTransfer::DoInitDownload() */
			const int acc = MO_IRUSR|MO_IWUSR|MO_IRGRP|MO_IWGRP|MO_IROTH|MO_IWOTH;
			
			if ( cfile.Open( sfile, mode, acc ) == false )
			{
				CString logmsg = "Create 0B file '";
				logmsg += sfile;
				logmsg += "' failed";
				SendLogInfo( logmsg );
				return;
			}
			else
			{
				cfile.Close();
			}
		}
		
		/* Either already existed or has been created, send download finished message */
		
		CString logmsg = "Transfer done '";
		logmsg += remotename;
		logmsg += '\'';
		SendLogInfo( logmsg );
		
		if ( CConfig::Instance()->GetLogFile() && CConfig::Instance()->GetLogFinishedDownloads() )
		{
			logmsg = "Transfer done '";
			logmsg += sfile;
			logmsg += '\'';
			CLogFile::Write(CConfig::Instance()->GetLogFileName(),eltINFO,logmsg);
		}
		
		return;
	}
	
	/* this never happens anymore, fixed in 0.3.18, unless something was missed in valknut */
	if ((hash.Left(4)).ToUpper() == "TTH:")
	{
		DPRINTF("DLM_QueueAdd: Removed TTH: prefix from TTH\n");
		hash = hash.Mid(4, hash.Length() - 4);
	}
	
/*	DPRINTF("add queue entry: %s %s %s %s %d %s %s %s %llu %llu\n",
		nick.Data(),
		remotename.Data(),
		hubname.Data(),
		hubhost.Data(),
		medium,
		localname.Data(),
		localpath.Data(),
		localrootpath.Data(),
		startposition,
		size );*/

	m_pDownloadQueue->pQueueMutex->Lock();

	CStringList<DCTransferQueueObject> * StringList = m_pDownloadQueue->GetUserHubList( nick );

	if ( (TransferObject = m_pDownloadQueue->GetUserTransferObject( nick, hubname, hubhost )) == 0 )
	{
		TransferObject = new DCTransferQueueObject();

		TransferObject->sNick    = nick;
		TransferObject->sHubHost = hubhost;
		TransferObject->sHubName = hubname;
		TransferObject->eState   = etwsIDLE;
		TransferObject->iConnections = 0;
		TransferObject->tTimeout = 0;

		HubObject = new DCHubObject();

		HubObject->m_sHubName = hubname;
		HubObject->m_sHubHost = hubhost;
		HubObject->m_bActive  = true;

		TransferObject->pHubList.Add(HubObject);

		if ( StringList == 0 )
		{
			StringList = new CStringList<DCTransferQueueObject>();
			m_pDownloadQueue->pQueue->Add( nick, StringList );
		}

		StringList->Add( hubname, TransferObject );

		//DPRINTF("transfer add\n");
	}
	else
	{
		TransferFileObject = m_pDownloadQueue->GetUserFileObject( nick, hubname, hubhost, remotename );
	}

	// file not found for the nick/hub
	if ( TransferFileObject == 0 )
	{
		TransferFileObject = new DCTransferFileObject();

		TransferFileObject->m_eState      = etfsNONE;
		TransferFileObject->m_nSize       = size;
		TransferFileObject->m_bMulti      = multi;
		TransferFileObject->m_eMedium     = medium;
		TransferFileObject->m_sRemoteFile = remotename;
		TransferFileObject->m_sHash       = hash;

		if ( remotename == DC_USER_FILELIST )
		{
			TransferFileObject->m_nPriority = 0;
			TransferFileObject->m_sJumpTo = localpath;
			localpath.Empty();
			if ( localrootpath.NotEmpty() )
			{
				TransferFileObject->m_pDirList = new std::list<CString>();
				TransferFileObject->m_pDirList->push_back( localrootpath );
				localrootpath.Empty();
			}
		}
		else
		{
			TransferFileObject->m_nPriority = 2;
		}
			
		if ( localrootpath.IsEmpty() )
		{
			sfile = CConfig::Instance()->GetDownloadFolder();
		}
		else
		{
			sfile = localrootpath;
		}

		localpath = localpath.Replace(':',"");
		localname = localname.Replace(':',"");

		sfile += '/';
		sfile += localpath;
		sfile += '/';
		sfile += localname;
		sfile = CDir::SimplePath(sfile);

		TransferFileObject->m_sLocalFile     = sfile;
		TransferFileObject->m_sLocalPath     = localpath;
		TransferFileObject->m_sLocalFileName = localname;

		TransferObject->pTransferFileList.Add( remotename, TransferFileObject );

		//DPRINTF("file add '%s' %ld\n",sfile.Data(),TransferObject->pTransferFileList.Count());

		m_pDownloadQueue->pChunksMutex->Lock();

		DCFileChunkObject * FileChunkObject;

		if ( remotename == DC_USER_FILELIST )
		{
			DPRINTF("no chunk for userlists\n");
		}
		else if ( m_pDownloadQueue->pChunkList->Get( sfile, &FileChunkObject ) != 0 )
		{
			FileChunkObject = new DCFileChunkObject();

			FileChunkObject->m_sLocalFile      = sfile;
			FileChunkObject->m_sHash           = hash;
			FileChunkObject->m_bMulti          = multi;
			FileChunkObject->m_nSize           = size;
			FileChunkObject->m_nSizeDone       = startposition;
			FileChunkObject->m_nReferenceCount = 1;

			// create the first unfinished chunk
			DCChunkObject * ChunkObject = new DCChunkObject();
			ChunkObject->m_nStart = startposition;
			ChunkObject->m_nEnd   = size;

			if( endposition )
			{
				endposition++;
				FileChunkObject->m_nSizeDone = size - (endposition - startposition);
				ChunkObject->m_nEnd = endposition;
			}

			FileChunkObject->m_Chunks.Add(ChunkObject);
			m_pDownloadQueue->pChunkList->Add( sfile, FileChunkObject );

			//DPRINTF("add file chunk object\n");
		}
		else
		{
			FileChunkObject->m_nReferenceCount++;
			DPRINTF("file chunk object found\n");
			
			// fill in any missing information in case a source with TTH is
			// added to a download without TTH
			if ( (FileChunkObject->m_sHash.IsEmpty()) && (!hash.IsEmpty()) )
			{
				FileChunkObject->m_sHash = hash;
				
				// we now need to iterate over the entire queue to fill in other missing TTHs for this local file
				// how to iterate over the queue was taken from InitSearch
				CStringList<DCTransferQueueObject> * sl = 0;
				
				while ( m_pDownloadQueue->pQueue->Next( &sl ) )
				{
					DCTransferQueueObject * tqo = 0;
					
					while ( sl->Next( &tqo ) )
					{
						DCTransferFileObject * tfo = 0;
						
						while ( tqo->pTransferFileList.Next( &tfo ) )
						{
							if ( (tfo->m_sLocalFile == FileChunkObject->m_sLocalFile) && (tfo->m_sHash.IsEmpty()) )
							{
								tfo->m_sHash = hash;
								
								// update GUI
								SendFileInfo( tqo, tfo ); 
							}
						}
					}
				}
			}
			
			// fills in missing information from existing transfer
			// if source without TTH is added to one with a TTH
			if ( TransferFileObject->m_sHash.IsEmpty() )
			{
				TransferFileObject->m_sHash = FileChunkObject->m_sHash;
			}
		}

		m_pDownloadQueue->pChunksMutex->UnLock();
	}
	else
	{
		DPRINTF("file found ...\n");
		
		if ( remotename == DC_USER_FILELIST )
		{
			TransferFileObject->m_sJumpTo = localpath;
			if ( localrootpath.NotEmpty() )
			{
				if ( TransferFileObject->m_pDirList )
				{
					// do not download a folder that is inside one already queued
					// remove any folders that are inside this folder
					std::list<CString>::iterator it = TransferFileObject->m_pDirList->begin();
					while ( it != TransferFileObject->m_pDirList->end() )
					{
						if ( localrootpath.StartsWith( *it ) )
						{
							break;
						}
						else if ( it->StartsWith( localrootpath ) )
						{
							it = TransferFileObject->m_pDirList->erase( it );
						}
						else
						{
							++it;
						}
					}
					
					if ( it == TransferFileObject->m_pDirList->end() )
					{
						TransferFileObject->m_pDirList->push_back( localrootpath );
					}
					else
					{
						DPRINTF("'%s' already queued within '%s'\n",localrootpath.Data(),it->Data());
					}
				}
				else
				{
					TransferFileObject->m_pDirList = new std::list<CString>();
					TransferFileObject->m_pDirList->push_back( localrootpath );
				}
				
				//DPRINTF("directory queue now %zd\n",TransferFileObject->m_pDirList->size());
				DPRINTF("directory queue now %lu\n",(unsigned long) TransferFileObject->m_pDirList->size());
			}
		}
	}

	SendFileInfo( TransferObject, TransferFileObject );

	m_pDownloadQueue->pQueueMutex->UnLock();
}

/**	0: file not in the queue
	1: file already in the queue by same nick and hub
	2: local file with same TTH (same size if no TTH) already in the queue (multi download)
	3: local file with same TTH (same size if no TTH) already in the queue without multi (no multi download)
	4: local file with different TTH (different size if no TTH) already in the queue (multi download error)
*/
int CDownloadManager::DLM_QueueCheck( CString nick, CString hubname, CString hubhost,
				CString remotename, CString localname,
				CString localpath, CString localrootpath,
				eltMedium, ulonglong size, CString tth )
{
	m_pDownloadQueue->pQueueMutex->Lock();

	// this should really be fixed globally
	if ( tth.Left(4).ToUpper() == "TTH:" )
	{
		tth = tth.Mid(4);
	}

	CDir dir;
	CString sfile;
	int res = 0;
	DCTransferQueueObject * TransferObject;
	DCTransferFileObject * TransferFileObject = 0;

	if ( (TransferObject = m_pDownloadQueue->GetUserTransferObject( nick, hubname, hubhost )) != 0 )
	{
		if ( (TransferFileObject = m_pDownloadQueue->GetUserFileObject( nick, hubname, hubhost, remotename )) != 0 )
		{
			res = 1;
		}
	}

	// check if a local file allready exist
	if ( (res == 0) && (remotename != DC_USER_FILELIST) )
	{
		if ( localrootpath.IsEmpty() )
		{
			sfile = CConfig::Instance()->GetDownloadFolder();
		}
		else
		{
			sfile = localrootpath;
		}

		localpath = localpath.Replace(':',"");
		localname = localname.Replace(':',"");

		sfile += '/';
		sfile += localpath;
		sfile += '/';
		sfile += localname;
		sfile = CDir::SimplePath(sfile);

		DCFileChunkObject * FileChunkObject;

		m_pDownloadQueue->pChunksMutex->Lock();

		if ( m_pDownloadQueue->pChunkList->Get( sfile, &FileChunkObject ) == 0 )
		{
			// allow manual addition of non-TTH sources to files with TTHs
			// and allow addition of TTH sources to files with no TTH
			if ( ((tth.IsEmpty() || FileChunkObject->m_sHash.IsEmpty()) && (FileChunkObject->m_nSize == size)) || (FileChunkObject->m_sHash == tth) )
			{
				if ( FileChunkObject->m_bMulti )
				{
					res = 2;
				}
				else
				{
					res = 3;
				}
			}
			else
			{
				res = 4;
			}
		}

		m_pDownloadQueue->pChunksMutex->UnLock();
	}

	m_pDownloadQueue->pQueueMutex->UnLock();

	return res;
}

/** change the src-nick/hubname to dst-nick/hubname in the queue */
bool CDownloadManager::DLM_QueueEdit( CString srcnick, CString srchubname, CString dstnick, CString dsthubname, CString dsthubhost )
{
	bool res;
	DCTransferQueueObject * TransferObject;
	DCTransferFileObject * TransferFileObject;

	m_pDownloadQueue->pQueueMutex->Lock();

	res = false;

	// check if transfer run
	if ( (TransferObject = m_pDownloadQueue->GetUserTransferObject( srcnick, srchubname, CString() )) != 0 )
	{
		if ( (TransferObject->eState != etwsWAIT) &&
		     (TransferObject->eState != etwsRUN) )
		{
			// only rename if the new not exists
			if ( m_pDownloadQueue->GetUserTransferObject( dstnick, dsthubname, dsthubhost ) == 0 )
			{
				// send remove all files
				TransferFileObject = 0;

				while( TransferObject->pTransferFileList.Next( &TransferFileObject ) )
				{
					SendFileInfo( TransferObject, TransferFileObject, true );
				}

				// rename old entry
				m_pDownloadQueue->RenameNick( srcnick, dstnick, srchubname, dsthubname );

				// get hubhost from new entry
				if ( (TransferObject = m_pDownloadQueue->GetUserTransferObject( dstnick, dsthubname, dsthubhost )) != 0 )
				{
					TransferObject->sHubHost = dsthubhost;

					// send new files
					TransferFileObject = 0;

					while( TransferObject->pTransferFileList.Next( &TransferFileObject ) )
					{
						SendFileInfo( TransferObject, TransferFileObject );
					}

					res = true;
				}
			}
			else
			{
				// update hubhost
				if ( TransferObject->sHubHost != dsthubhost )
				{
					TransferObject->sHubHost = dsthubhost;
					SendFileInfo( TransferObject );
				}
				
			}
		}
	}

	m_pDownloadQueue->pQueueMutex->UnLock();

	return res;
}

/** */
bool CDownloadManager::DLM_QueuePause( CString nick, CString hubname, CString remotefile, bool pause )
{
	bool res = false;
	DCTransferFileObject * TransferFileObject = 0;
	DCTransferQueueObject * TransferObject = 0;

	m_pDownloadQueue->pQueueMutex->Lock();

	if ( (TransferObject = m_pDownloadQueue->GetUserTransferObject( nick, hubname, CString() )) != 0 )
	{
		if ( remotefile.NotEmpty() )
		{
			if ( (TransferFileObject = m_pDownloadQueue->GetUserFileObject( nick, hubname, CString(), remotefile )) != 0 )
			{
				if ( TransferFileObject->m_eState != etfsTRANSFER )
				{
					if ( pause )
						TransferFileObject->m_eState = etfsPAUSE;
					else
						TransferFileObject->m_eState = etfsNONE;

					SendFileInfo( TransferObject, TransferFileObject );
					res = true;
				}
			}
		}
		else
		{
			TransferFileObject = 0;

			while ( TransferObject->pTransferFileList.Next( &TransferFileObject ) )
			{
				if ( TransferFileObject->m_eState != etfsTRANSFER )
				{
					if ( pause )
						TransferFileObject->m_eState = etfsPAUSE;
					else
						TransferFileObject->m_eState = etfsNONE;

					SendFileInfo( TransferObject, TransferFileObject );
					res = true;
				}
			}
		}
	}

	m_pDownloadQueue->pQueueMutex->UnLock();

	return res;
}

/** */
bool CDownloadManager::DLM_QueueRemove( CString nick, CString hubname, CString remotefile )
{
	bool res;
	
	m_pDownloadQueue->pQueueMutex->Lock();

	res = RemoveQueueFile( nick, hubname, remotefile );

	m_pDownloadQueue->pQueueMutex->UnLock();
	
	return res;
}

/** */
bool CDownloadManager::DLM_QueueRemove( CString localfile )
{
	bool res;

	m_pDownloadQueue->pQueueMutex->Lock();

	res = RemoveQueueFile(localfile);

	m_pDownloadQueue->pQueueMutex->UnLock();

	return res;
}

/** */
int CDownloadManager::DLM_QueueRemoveDirectory( CString nick, CString hubname, CString directory )
{
	int res;
	
	m_pDownloadQueue->pQueueMutex->Lock();
	
	DCTransferFileObject * TransferFileObject = m_pDownloadQueue->GetUserFileObject( nick, hubname, CString(), DC_USER_FILELIST );
	
	if ( TransferFileObject )
	{
		if ( TransferFileObject->m_pDirList )
		{
			std::list<CString>::iterator it = TransferFileObject->m_pDirList->begin();
			for ( ; it != TransferFileObject->m_pDirList->end(); ++it )
			{
				if ( *it == directory )
				{
					break;
				}
			}
			
			if ( it == TransferFileObject->m_pDirList->end() )
			{
				res = 3;
			}
			else
			{
				TransferFileObject->m_pDirList->erase( it );
				if ( TransferFileObject->m_pDirList->empty() )
				{
					delete TransferFileObject->m_pDirList;
					TransferFileObject->m_pDirList = 0;
				}
				res = 0;
				
				DCTransferQueueObject * TransferQueueObject = m_pDownloadQueue->GetUserTransferObject( nick, hubname, CString() );
				if ( TransferQueueObject )
				{
					SendFileInfo( TransferQueueObject, TransferFileObject );
				}
			}
		}
		else
		{
			res = 2;
		}
	}
	else
	{
		res = 1;
	}
	
	m_pDownloadQueue->pQueueMutex->UnLock();
	
	return res;
}

/** */
bool CDownloadManager::DLM_QueueSetFilePriority( CString nick, CString hubname, CString remotefile, int priority )
{
	m_pDownloadQueue->pQueueMutex->Lock();

	bool res = false;
	DCTransferQueueObject * TransferObject = 0;
	DCTransferFileObject * TransferFileObject = 0;

	if ( (TransferObject = m_pDownloadQueue->GetUserTransferObject( nick, hubname, CString() )) != 0 )
	{
		if ( (remotefile.NotEmpty()) && (priority <= MAX_FILE_PRIORITY) )
		{
			if ( (TransferFileObject = m_pDownloadQueue->GetUserFileObject( nick, hubname, CString(), remotefile )) != 0 )
			{
				// we don't need check if transfer is running
				TransferFileObject->m_nPriority = priority;

				SendFileInfo( TransferObject, TransferFileObject );

				res = true;
			}
		}
	}

	m_pDownloadQueue->pQueueMutex->UnLock();
	
	return res;
}

/** */
bool CDownloadManager::DLM_QueueGetFileInfo( CString nick, CString hubname, CString hubhost, CString remotefile, CUserFileInfo * UserFileInfo )
{
	bool res = false;

	if (!UserFileInfo)
	{
		return res;
	}

	m_pDownloadQueue->pQueueMutex->Lock();

	DCTransferQueueObject * TransferObject;
	DCTransferFileObject * TransferFileObject;

	if ( (TransferObject = m_pDownloadQueue->GetUserTransferObject( nick, hubname, hubhost )) != 0 )
	{
		UserFileInfo->eWaitState    = TransferObject->eState;

		if ( remotefile.NotEmpty() )
		{
			if ( TransferObject->pTransferFileList.Get( remotefile, &TransferFileObject ) == 0 )
			{
				UserFileInfo->eFileState = TransferFileObject->m_eState;
				UserFileInfo->sLocalFile = TransferFileObject->m_sLocalFile;
				UserFileInfo->bMulti     = TransferFileObject->m_bMulti;
				res = true;
			}
		}
		else
		{
			res = true;
		}
	}

	m_pDownloadQueue->pQueueMutex->UnLock();

	return res;
}

/** */
DCFileChunkObject * CDownloadManager::DLM_QueueGetFileChunk( CString file )
{
	m_pDownloadQueue->pQueueMutex->Lock();

	DCFileChunkObject * FileChunkObject1, * FileChunkObject2=0;

	if ( (FileChunkObject1 = m_pDownloadQueue->GetFileChunkObject(file)) != 0 )
	{
		FileChunkObject2 = new DCFileChunkObject(FileChunkObject1);
	}

	m_pDownloadQueue->pQueueMutex->UnLock();

	return FileChunkObject2;
}

/** */
bool CDownloadManager::DLM_QueueUpdateHub( CString nick, CString hubname )
{
	m_pDownloadQueue->pQueueMutex->Lock();

	bool res = false;
	DCConfigHubItem hubitem;
	DCTransferQueueObject * TransferObject;

	if ( (TransferObject = m_pDownloadQueue->GetUserTransferObject( nick, hubname, CString() )) != 0 )
	{
		if ( (res = CConfig::Instance()->GetPublicHub( hubname, &hubitem )) )
			TransferObject->sHubHost = hubitem.m_sHost;
		else if ( (res = CConfig::Instance()->GetBookmarkHub( hubname, &hubitem )) )
			TransferObject->sHubHost = hubitem.m_sHost;

		if ( res == true )
			SendFileInfo( TransferObject );
	}

	m_pDownloadQueue->pQueueMutex->UnLock();

	return res;
}

/** */
void CDownloadManager::DLM_QueueGetHub( CString nick, CString hubname, CList<DCHubObject> * list )
{
	DCTransferQueueObject * TransferObject;
	DCHubObject * HubObject1;

	if (!list)
	{
		return;
	}

	list->Clear();

	m_pDownloadQueue->pQueueMutex->Lock();

	if ( (TransferObject = m_pDownloadQueue->GetUserTransferObject( nick, hubname, CString() )) != 0 )
	{
		HubObject1=0;

		while( (HubObject1=TransferObject->pHubList.Next(HubObject1)) != 0 )
			list->Add( new DCHubObject(HubObject1) );
	}

	m_pDownloadQueue->pQueueMutex->UnLock();

	return;
}



/** */
bool CDownloadManager::DLM_AddTransferRequest( CString nick, CString userhost, CString hubname, CString hubhost )
{
	bool res;
	DCTransferWait * TransferWait;

	if ( m_eShutdownState != essNONE )
	{
		return false;
	}
	
	m_pWaitListMutex->Lock();

	res = false;
	TransferWait = 0;

	DPRINTF("ATR: '%s' '%s' '%s' '%s'\n",nick.Data(),userhost.Data(),hubname.Data(),hubhost.Data());

	DPRINTF("ATR COUNT: %ld\n",m_pTransferWaitList->Count());

	if ( m_pTransferWaitList->Count() < 250 )
	{
		// check if the user allready in the waitlist
		while ( (TransferWait = m_pTransferWaitList->Next(TransferWait)) != 0 )
		{
			if ( TransferWait->sHubName == hubname )
			{
				// here one of "nick" or "userhost" will be empty
				if ( (TransferWait->sNick == nick) && (TransferWait->sUserHost == userhost) )
				{
					DPRINTF("ATR FOUND\n");

					// reset timeout, ignore it on to fast connections
					if ( (time(0)-TransferWait->tTimeout) > 2 )
					{
						// count in requests from a client
						TransferWait->m_nCount++;

						TransferWait->tTimeout = time(0);
						res = true;
					}
					else
					{
						DPRINTF("ATR to fast connections\n");
					}

					break;
				}
			}
		}

		if ( TransferWait == 0 )
		{
			// user not found, add it
			DPRINTF("ATR ADD\n");

			TransferWait = new DCTransferWait();
			TransferWait->sNick     = nick;
			TransferWait->sUserHost = userhost;
			TransferWait->sHubName  = hubname;
			TransferWait->sHubHost  = hubhost;
			TransferWait->tTimeout  = time(0);
			TransferWait->m_nCount  = 1;

			m_pTransferWaitList->Add(TransferWait);
			
			res = true;
		}
	}
	else
	{
		// TODO: warn message ...
	}

	m_pWaitListMutex->UnLock();

	return res;
}

/** */
void CDownloadManager::DLM_AddTransferRequest( CString host, int port, bool crypto, CString hubname, CString hubhost )
{
	if ( m_eShutdownState != essNONE )
	{
		return;
	}

	DPRINTF("ATR: '%s:%d' %d '%s' '%s'\n",host.Data(),port,crypto,hubname.Data(),hubhost.Data());

	// check private address space
	if ( !((CConfig::Instance()->GetCheckPrivateAddressSpace() &&
	     CNetAddr::IsPrivateI4(host.Data())) ||
	     (CConfig::Instance()->GetPrivateAddressSpaceOnly() &&
	     (CNetAddr::IsPrivateI4(host.Data()) == false)) ) )
	{
		// TODO: check if client allready in the transfer wait list
		// create a new transfer
		CTransferObject * TransferObject = new CTransferObject();
		TransferObject->m_pTransfer = new CTransfer();

		TransferObject->m_pTransfer->SetTransferID( GetNewID() );
		TransferObject->m_pTransfer->SetNick( CConfig::Instance()->GetNick( hubname, hubhost ) );

		TransferObject->m_pTransfer->SetHubName(hubname);
		TransferObject->m_pTransfer->SetHubHost(hubhost);

		TransferObject->m_pTransfer->SetHost( host, port );
		TransferObject->m_pTransfer->SetRate( CConfig::Instance()->GetMaxUploadRate() );

		if ( crypto )
		{
			if ( TransferObject->m_pTransfer->ChangeSocketMode( esmFULLSSLCLIENT, CConfig::Instance()->GetTransferCert(), CConfig::Instance()->GetTransferKey() ) == false )
			{
				DPRINTF("New transfer change to SSL client mode failed\n");
				delete TransferObject;
				return;
			}
		}

		// add this transfer to the wait list
		if ( DLM_AddTransferRequest( CString(), TransferObject->m_pTransfer->GetHost(), hubname, hubhost ) == false )
		{
			// request allready in the list
			delete TransferObject;
		}
		else
		{
			m_pTransfersMutex->Lock();

			m_pTransferList->Add( CString::number(TransferObject->m_pTransfer->GetTransferID()), TransferObject );
			TransferObject->m_pTransfer->SetCallBackFunction( new CCallback2<CDownloadManager, CTransfer, CDCMessage*>( this, &CDownloadManager::DM_TransferCallBack ) );

			DPRINTF("ATR CONNECT: %s:%d %d %s %s\n",host.Data(),port,crypto,hubname.Data(),hubhost.Data());

			// connect to the client
			TransferObject->m_pTransfer->Connect();

			m_pTransfersMutex->UnLock();
		}
	}
	else
	{
		// send warning
		CString logmsg = "Ignoring connection to: ";
		logmsg += host;
		logmsg += ':';
		logmsg += CString::number(port);
		logmsg += " at hub '";
		logmsg += hubname;
		logmsg += "' (";
		logmsg += hubhost;
		logmsg += ") due to private address space settings";
		SendLogInfo( logmsg );
	}
}

/** */
bool CDownloadManager::DLM_GetDownloadManagerInfo( CDownloadManagerInfo * pinfo )
{
	*pinfo = DownloadManagerInfo;

	return true;
}

/** handle search */
bool CDownloadManager::DLM_HandleSearch( CMessageSearchResult * MessageSearchResult )
{
	bool res = false;
	CMessageSearchResult * msg = 0;
	DCTransferFileObject * tfo1=0, * tfo2=0;

	while ( (msg = m_pSearchList->Next(msg)) != 0 )
	{
		//MessageSearchResult->sNick += "+";
		if ( msg->m_sHash == MessageSearchResult->m_sHash )
		{
			// handle this file
			m_pDownloadQueue->pQueueMutex->Lock();
			
			// check if this file allready in the queue
			if ( m_pDownloadQueue->GetUserFileObject( MessageSearchResult->m_sNick, MessageSearchResult->m_sHubName, MessageSearchResult->m_sHubHost, MessageSearchResult->m_sFile ) == 0 )
			{
				// get original file
				if ( (tfo1=m_pDownloadQueue->GetUserFileObject( msg->m_sNick, msg->m_sHubName, msg->m_sHubHost, msg->m_sFile )) != 0 )
				{
					tfo2 = new DCTransferFileObject(*tfo1);
				}
			}

			m_pDownloadQueue->pQueueMutex->UnLock();
			
			if ( tfo2 )
				break;
		}
	}

	if ( tfo2 )
	{
		CDir dir;
		CString path,file;
		int adj = 0;

		dir.SplitPathFile( tfo2->m_sLocalFile, path, file );
		
		// remove tfo2->m_sLocalPath from path
		if ( (path.Right(1) == DIRSEPARATOR) && (tfo2->m_sLocalPath.Right(1) != DIRSEPARATOR) )
		{
			adj = 1;
		}
		else if ( (path.Right(1) != DIRSEPARATOR) && (tfo2->m_sLocalPath.Right(1) == DIRSEPARATOR) )
		{
			adj = -1;
		}
		
		path = path.Left( path.Length() - (tfo2->m_sLocalPath.Length() + adj) );

		DLM_QueueAdd( MessageSearchResult->m_sNick,
			MessageSearchResult->m_sHubName,
			MessageSearchResult->m_sHubHost,
			MessageSearchResult->m_sFile,
			tfo2->m_sLocalFileName,
			tfo2->m_sLocalPath,
			path,
			tfo2->m_eMedium,
			tfo2->m_nSize,
			0, 0,
			MessageSearchResult->m_sHash,
			true );
		delete tfo2;
	}

	return res;
}

/** */
void CDownloadManager::SendFileInfo( DCTransferQueueObject * TransferObject, DCTransferFileObject * TransferFileObject, bool bRemoveFile )
{
	if ( m_eShutdownState != essNONE )
		return;

	LogMutex.Lock();

	CMessageDMFileObject * fo = new CMessageDMFileObject();

	fo->m_sNick              = TransferObject->sNick;
	fo->m_sHubName           = TransferObject->sHubName;
	fo->m_sHubHost           = TransferObject->sHubHost;
	fo->m_eTransferWaitState = TransferObject->eState;
	fo->m_tTimeout           = TransferObject->tTimeout;
	fo->m_bRemoveFile        = bRemoveFile;
	fo->m_nConnections       = TransferObject->iConnections;

	if ( TransferFileObject )
	{
		fo->m_sRemoteFile        = TransferFileObject->m_sRemoteFile;
		fo->m_sLocalFile         = TransferFileObject->m_sLocalFile;
		fo->m_nSize              = TransferFileObject->m_nSize;
		fo->m_eTransferFileState = TransferFileObject->m_eState;
		fo->m_bMulti             = TransferFileObject->m_bMulti;
		fo->m_nPriority          = TransferFileObject->m_nPriority;
		fo->m_sTTH               = TransferFileObject->m_sHash;
		
		if ( TransferFileObject->m_pDirList )
		{
			fo->m_pDirList = new std::list<CString>();
			*(fo->m_pDirList) = *(TransferFileObject->m_pDirList);
		}
	}

	if ( DC_DownloadManagerCallBack(fo) == -1 )
		delete fo;

	LogMutex.UnLock();
}

/** */
CMessageDMTransferObject * CDownloadManager::CreateDMTransferObject( CTransfer * Transfer )
{
	DCFileChunkObject * FileChunkObject;
	CMessageDMTransferObject * to = new CMessageDMTransferObject();

	to->m_nTransferID    = Transfer->GetTransferID();
	to->m_sSrcNick       = Transfer->GetNick();
	to->m_sDstNick       = Transfer->GetDstNick();
	to->sHost            = Transfer->GetHost();
	to->m_sHubHost       = Transfer->GetHubHost();
	to->sHubName         = Transfer->GetHubName();
	to->eState           = Transfer->GetMode();
	to->m_sDstFile       = Transfer->GetDstFilename();
	to->m_sSrcFile       = Transfer->GetSrcFilename();
	to->lSize            = Transfer->GetLength();
	to->lStartPosition   = Transfer->GetStartPosition();
	to->lEndPosition     = Transfer->GetEndPosition();
	to->lRate            = Transfer->GetTransferrate();
	to->lTransfered      = Transfer->GetTransfered();
	to->m_bEncrypted     = Transfer->GetEncrypted();
	to->m_sTTH           = Transfer->GetTTH();

	// get size done from the chunk list
	if ( Transfer->GetSrcDirection() == edUPLOAD )
	{
		to->lSizeDone = to->lStartPosition + to->lTransfered;
	}
	else
	{
		m_pDownloadQueue->pChunksMutex->Lock();

		if ( Transfer->GetMedium() == eltBUFFER )
		{
			to->lSizeDone = to->lStartPosition + to->lTransfered;
		}
		else if ( (FileChunkObject = m_pDownloadQueue->GetFileChunkObject(Transfer->GetSrcFilename())) != 0 )
		{
			to->lSizeDone = FileChunkObject->m_nSizeDone+to->lTransfered;
		}
		else
		{
			to->lSizeDone = to->lSize;
		}

		m_pDownloadQueue->pChunksMutex->UnLock();	
	}

	return to;
}

/** */
CList<CMessageDMTransferObject> * CDownloadManager::DLM_TransferGetList()
{
	CTransferObject * TransferObject = 0;
	CList<CMessageDMTransferObject> * list;
	CMessageDMTransferObject * to;

	m_pTransfersMutex->Lock();

	list = new CList<CMessageDMTransferObject>();

	while( m_pTransferList->Next( &TransferObject ) )
	{
 		to = CreateDMTransferObject(TransferObject->m_pTransfer);
		list->Add(to);
	}

	m_pTransfersMutex->UnLock();

	return list;
}

/** */
void CDownloadManager::SendTransferInfo( CTransfer * Transfer, bool remove )
{
	LogMutex.Lock();

	CMessageDMTransferObject * to = CreateDMTransferObject(Transfer);

	to->bRemoveTransfer  = remove;

	if ( DC_DownloadManagerCallBack(to) == -1 )
		delete to;

	LogMutex.UnLock();
}

/** */
void CDownloadManager::SendSlotInfo( CExtraUserSlot * Object )
{
	LogMutex.Lock();

	CMessageDMSlotObject * so = new CMessageDMSlotObject();

	so->sNick      = Object->sNick;
	so->sHubName   = Object->sHubName;
	so->iSlots     = Object->iSlots;
	so->bPermanent = Object->bPermanent;

	if ( DC_DownloadManagerCallBack(so) == -1 )
		delete so;

	LogMutex.UnLock();
}

/** */
void CDownloadManager::SendLogInfo( CString message, CTransfer * Transfer )
{
	LogMutex.Lock();

	CMessageLog * log = new CMessageLog();

	if ( Transfer != 0 )
	{
		log->sMessage = "[";
		if ( Transfer->GetDstNick().IsEmpty() )
			log->sMessage += "???";
		else
			log->sMessage += Transfer->GetDstNick();
		log->sMessage += "] ";
	}
	
	log->sMessage += message;

	// write message in log tab in transfer window to logfile
	if ( CConfig::Instance()->GetLogFile() && CConfig::Instance()->GetLogDetails() )
	{
		CLogFile::Write(CConfig::Instance()->GetLogFileName(),eltINFO,log->sMessage);
	}

	if ( DC_DownloadManagerCallBack(log) == -1 )
		delete log;

	LogMutex.UnLock();
}

/** */
void CDownloadManager::SendDownloadManagerInfo( CDownloadManagerInfo * dminfo )
{
	if ( !dminfo )
	{
		return;
	}

	LogMutex.Lock();

	CDownloadManagerInfo * dmi = new CDownloadManagerInfo;

	*dmi = *dminfo;

	if ( DC_DownloadManagerCallBack(dmi) == -1 )
		delete dmi;

	LogMutex.UnLock();
}

/** */
void CDownloadManager::SendTrafficInfo()
{
	LogMutex.Lock();

	DCMessageTraffic * to = new DCMessageTraffic();

	to->m_nRx        = CSocket::m_Traffic.GetTraffic(ettRX);
	to->m_nTx        = CSocket::m_Traffic.GetTraffic(ettTX);
	to->m_nDataRx    = CSocket::m_Traffic.GetTraffic(ettDATARX);
	to->m_nDataTx    = CSocket::m_Traffic.GetTraffic(ettDATATX);
	to->m_nControlRx = CSocket::m_Traffic.GetTraffic(ettCONTROLRX);
	to->m_nControlTx = CSocket::m_Traffic.GetTraffic(ettCONTROLTX);

	if ( DC_DownloadManagerCallBack(to) == -1 )
		delete to;

	LogMutex.UnLock();
}

/** */
void CDownloadManager::SendFileManagerInfo( CFileManagerInfo * info )
{
	if ( m_eShutdownState != essNONE )
	{
		return;
	}
	
	if ( !info )
	{
		return;
	}

	LogMutex.Lock();

	CFileManagerInfo * fmi = new CFileManagerInfo();

	*fmi = *info;

	if ( DC_DownloadManagerCallBack(fmi) == -1 )
		delete fmi;

	LogMutex.UnLock();
}

/** */
bool CDownloadManager::CheckUserSlot( CString nick, CString hubname )
{
	bool res;
	CExtraUserSlot * ExtraUserSlot;

	m_pExtraSlotsMutex->Lock();

	res = false;
	ExtraUserSlot = 0;

	while( (ExtraUserSlot=m_pExtraUserSlotList->Next(ExtraUserSlot)) != 0 )
	{
		if ( (ExtraUserSlot->sNick == nick) && (ExtraUserSlot->sHubName == hubname) )
		{
			if ( ExtraUserSlot->bPermanent )
			{
				res = true;
			}
			else if ( ExtraUserSlot->iSlots > 0 )
			{
				ExtraUserSlot->iSlots--;

				SendSlotInfo(ExtraUserSlot);

				if ( ExtraUserSlot->iSlots == 0 )
				{
					m_pExtraUserSlotList->Del(ExtraUserSlot);
				}

				res = true;
			}
			else
			{
				printf("Warning extra user slot for '%s' on '%s' with slots %d deleted!\n",ExtraUserSlot->sNick.Data(),ExtraUserSlot->sHubName.Data(),ExtraUserSlot->iSlots);
				m_pExtraUserSlotList->Del(ExtraUserSlot);
			}

			break;
		}
	}

	m_pExtraSlotsMutex->UnLock();

	return res;
}

/** */
void CDownloadManager::FileListDone( CTransfer * Transfer, DCTransferFileObject * TransferFileObject )
{
	DCTransferQueueObject * TransferObject;
	CString fn;
	
	// CTransfer has been modified to:
	// set the correct src name DC_USER_FILELIST_XMLBZ or DC_USER_FILELIST_BZ or DC_USER_FILELIST_HE3
	// and not bz2/he3 decompress the buffer
	// and provide a function to save it's buffer to a file
	if ( (TransferObject = m_pDownloadQueue->GetUserTransferObject( Transfer->GetDstNick(), Transfer->GetHubName(), Transfer->GetHubHost() )) != 0 )
	{
		fn  = Transfer->GetDstNick();
		fn += '@';
		fn += Transfer->GetHubHost();
		
		// convert invalid characters - from dcchat.cpp line 1156
		fn.Swap( '/', '_' );
		fn.Swap( '\\', '_' );
		fn.Swap( ':', '_' );
		
		if ( Transfer->GetSrcFilename() == DC_USER_FILELIST_XMLBZ )
		{
			fn += ".xml.bz2";
		}
		else if ( Transfer->GetSrcFilename() == DC_USER_FILELIST_BZ )
		{
			fn += ".bz2";
		}
		else if ( Transfer->GetSrcFilename() == DC_USER_FILELIST_HE3 )
		{
			fn += ".DcLst";
		}
		else if ( Transfer->GetSrcFilename() == DC_USER_FILELIST_XML )
		{
			fn += ".xml";
		}
		else
		{
			printf("CDownloadManager::FileListDone: unknown filelist type '%s'\n", Transfer->GetSrcFilename().Data());
			fn += ".filelist";
		}
		
		fn = CConfig::Instance()->GetFileListPath() + fn;
		
		if ( Transfer->SaveBufferToFile(fn) )
		{
			CMessageDMFileListObject * flo = new CMessageDMFileListObject();
			
			flo->sNick         = TransferObject->sNick;
			flo->sHubName      = TransferObject->sHubName;
			flo->sHubHost      = TransferObject->sHubHost;
			flo->sLocalFile	   = fn;
			
			flo->sJumpTo = TransferFileObject->m_sJumpTo;
			
			if ( TransferFileObject->m_pDirList )
			{
				flo->m_pDirList = new std::list<CString>();
				*(flo->m_pDirList) = *(TransferFileObject->m_pDirList);
			}
			
			LogMutex.Lock();
			
			if ( DC_DownloadManagerCallBack(flo) == -1 )
				delete flo;
			
			LogMutex.UnLock();
		}
		else
		{
			TransferFileObject->m_eState = etfsERROR;
			SendFileInfo( TransferObject, TransferFileObject );
			SendLogInfo( "Error saving filelist " + fn, Transfer );
		}
	}
}

/** */
bool CDownloadManager::CheckHash( CTransfer * Transfer )
{
	bool res = false;
	CByteArray ba;
	DCTransferFileObject * TransferFileObject;
	DCFileChunkObject * FileChunkObject;
	md5_ctx * context = 0;
	unsigned char resbuf[16];
	char c[3];
	int i = 0;
	CString result;

	if ( (TransferFileObject = m_pDownloadQueue->GetUserFileObject( Transfer->GetDstNick(), Transfer->GetHubName(), Transfer->GetHubHost(), Transfer->GetDstFilename() )) != 0 )
	{
		if ( Transfer->GetBuffer(&ba) != 0 )
		{
			if ( (TransferFileObject->m_stHash.IsEmpty()) && TransferFileObject->m_bMulti )
			{
				// calc hash
				context = new md5_ctx();
				
				md5_init_ctx( context );
				md5_process_bytes( ba.Data(), ba.Size(), context );
				md5_finish_ctx( context, &resbuf );
				
				delete context;
				
				// convert digest to hexadecimal
				for ( i = 0; i < 16; i++ )
				{
#ifdef WIN32
					_snprintf( c, 3, "%02x", resbuf[i] );
#else
					snprintf( c, 3, "%02x", resbuf[i] );
#endif
					result += c;
				}

				TransferFileObject->m_stHash = result;

				DPRINTF("hash is :'%s'\n",TransferFileObject->m_stHash.Data());

				m_pDownloadQueue->pChunksMutex->Lock();

				if ( m_pDownloadQueue->pChunkList->Get( Transfer->GetSrcFilename(), &FileChunkObject ) == 0 )
				{
					if ( FileChunkObject->m_stHash.IsEmpty() )
					{
						DPRINTF("Set hash ...\n");
						FileChunkObject->m_stHash = TransferFileObject->m_stHash;

						res = true;
					}
					else if ( FileChunkObject->m_stHash == TransferFileObject->m_stHash )
					{
						DPRINTF("Hash ok...\n");

						res = true;
					}
					else
					{
						DPRINTF("Wrong hash !!!\n");
						TransferFileObject->m_eState = etfsERROR;
					}
				}
				else
				{
					DPRINTF("warning file chunk object not found\n");
				}

				m_pDownloadQueue->pChunksMutex->UnLock();
			}
			else
			{
				DPRINTF("warning hash not empty or no multi download\n");
			}
		}
		else
		{
			DPRINTF("warning file object not found\n");
		}
	}
	else
	{
		DPRINTF("warning get buffer error\n");
	}

	return res;
}

/** 
bool CDownloadManager::SetMultiDownload( CString nick, CString hubname, CString remotefile )
{
	Lock();
	bool res = false;
	DCTransferQueueObject * TransferObject;
	DCTransferFileObject * TransferFileObject;
	DCFileChunkObject * FileChunkObject;

	if ( (TransferObject = m_pDownloadQueue->GetUserTransferObject( nick, hubname )) != 0 )
	{
		UserFileInfo->eWaitState    = TransferObject->eState;
		UserFileInfo->sUserFileList = TransferObject->sUserFileList;

		if ( TransferObject->pTransferFileList.Get( remotefile, &TransferFileObject ) == 0 )
		{
			if ( (TransferFileObject->eTransferFileState != etfsTRANSFER) &&
			     (TransferFileObject->bMulti == false) )
			{
				m_pDownloadQueue->pChunksMutex->Lock();

				if ( (FileChunkObject = m_pDownloadQueue->GetFileChunkObject(TransferFileObject->sLocalFile)) != 0 )
				{
					DCChunkObject * ChunkObject = 0;
					while( (ChunkObject=FileChunkObject->pChunks.Next(ChunkObject)) != 0 )
					{
						if ( ChunkObject->lStart <= TEST_CHUNK_SIZE )
						{
							// error
							break;
						}
					}

					if ( ChunkObject == 0 )
				}

				m_pDownloadQueue->pChunksMutex->UnLock();
			}

			UserFileInfo->eFileState = TransferFileObject->eState;
			UserFileInfo->sLocalFile = TransferFileObject->sLocalFile;
			UserFileInfo->bMulti     = TransferFileObject->bMulti;
			res = true;
		}
	}

	UnLock();

	return res;
}
*/

/** */
ulonglong CDownloadManager::GetNewID()
{
	return ((++m_nID)==0) ? (++m_nID) : (m_nID);
}

/** */
bool CDownloadManager::RemoveQueueFile( CString localfile )
{
	bool res = false;
	CStringList<DCTransferQueueObject> * StringList = 0;
	DCTransferQueueObject * TransferObject;
	DCTransferFileObject * TransferFileObject;

	// remove file from the chunk list
	m_pDownloadQueue->pChunksMutex->Lock();
	m_pDownloadQueue->pChunkList->Del( localfile );
	m_pDownloadQueue->pChunksMutex->UnLock();

	// remove file from the queue

	while( m_pDownloadQueue->pQueue->Next( &StringList ) )
	{
		TransferObject = 0;

		while( StringList->Next( &TransferObject) )
		{
			TransferFileObject = 0;

			while( TransferObject->pTransferFileList.Next( &TransferFileObject) )
			{
				if ( TransferFileObject->m_sLocalFile == localfile )
				{
					// on file transfer don't remove the file
					if ( TransferFileObject->m_eState != etfsTRANSFER )
					{
						SendFileInfo( TransferObject, TransferFileObject, true );
						TransferObject->pTransferFileList.Del(TransferFileObject->m_sRemoteFile);
						TransferFileObject = 0;
						res = true;
					}
					else
					{
						DPRINTF("WARNING: RemoveQueueFile: file transfer is running\n");
					}
				}
			}
		}
	}

	return res;
}

/** */
bool CDownloadManager::RemoveQueueFile( CString nick, CString hubname, CString remotefile )
{
	bool res = false;
	DCTransferFileObject * TransferFileObject = 0;
	DCTransferQueueObject * TransferObject = 0;

	if ( (TransferObject = m_pDownloadQueue->GetUserTransferObject( nick, hubname, CString() )) != 0 )
	{
		if ( remotefile.NotEmpty() )
		{
			if ( (TransferFileObject = m_pDownloadQueue->GetUserFileObject( nick, hubname, CString(), remotefile )) != 0 )
			{
				if ( TransferFileObject->m_eState != etfsTRANSFER )
				{
					res = true;
				}
				else
				{
					DPRINTF("WARNING: RemoveQueueFile: file transfer is running\n");
				}
			}
		}
		else
		{
			if ( TransferObject->eState != etwsRUN )
			{
				res = true;
			}
			else
			{
				DPRINTF("WARNING: RemoveQueueFile: transfer is running\n");
			}
		}

		if ( res )
		{
			SendFileInfo( TransferObject, TransferFileObject, true );
			res = m_pDownloadQueue->DelUserFileObject( nick, hubname, CString(), remotefile );
		}
	}

	return res;
}

/** */
bool CDownloadManager::UpdateWaitTransfer( CTransfer * Transfer, bool rem )
{
	bool res = false;
	DCTransferWait * TransferWait = 0;
	CString thost;

	// search in the wait list ...
	m_pWaitListMutex->Lock();

	/*
	 * For an incoming connection the port it came from is random, so will
	 * be different for each attempt, so do not put it in into the list.
	 */
	if ( Transfer->IsListener() )
	{
		thost = Transfer->GetIP();
	}
	else
	{
		thost = Transfer->GetHost();
	}
	
	DPRINTF("UWT: Search user %s %s in the waitlist\n",Transfer->GetDstNick().Data(),thost.Data());

	while ( (TransferWait = m_pTransferWaitList->Next(TransferWait)) != 0 )
	{
		// here we have both nick and host from the CTransfer
		// search for the nick+host or one of them and fill in the other
		if ( TransferWait->Match( Transfer->GetDstNick(), thost ) )
		{
			Transfer->SetHubName(TransferWait->sHubName);
			Transfer->SetHubHost(TransferWait->sHubHost);

			DPRINTF("UWT: User found\n");

			res = true;
			break;
		}
	}

	if ( !TransferWait )
	{
		DPRINTF("UWT: User not found\n");
	}
	else
	{
		if ( rem )
		{
			TransferWait->m_nCount--;
		
			DPRINTF("UWT: Remove user %lld\n",TransferWait->m_nCount);

			if ( TransferWait->m_nCount == 0 )
				m_pTransferWaitList->Del(TransferWait);
		}
	}

	m_pWaitListMutex->UnLock();

	return res;
}

/** */
eDirection CDownloadManager::CheckWaitTransfer( CTransfer * Transfer )
{
	m_pDownloadQueue->pQueueMutex->Lock();

	eDirection res = edNONE;
	DCTransferQueueObject * TransferObject;
	DCTransferBanObject * TransferBanObject = 0;
	CString host;
	bool waittransfer;
	int port;
	bool ban = false;
	long int i;
	
	DPRINTF("CWT: '%s' on '%s'\n",
			Transfer->GetDstNick().Data(),
			Transfer->GetHubName().Data() );

	// allow only 1 request in 30 seconds
	m_pBanListMutex->Lock();

	if ( Transfer->GetPeerName( &host, &port ) == false )
	{
		// error
		DPRINTF("CWT: Error: Can't get peername\n");
	}
	else if ( m_pTransferBanList->Get( Transfer->GetDstNick(), &TransferBanObject ) != 0 )
	{
		DPRINTF("CWT: Create new TransferBanObject '%s'\n",host.Data());

		TransferBanObject = new DCTransferBanObject();

		TransferBanObject->m_sIP   = host;
		TransferBanObject->m_tTime = time(0);

		m_pTransferBanList->Add( Transfer->GetDstNick(), TransferBanObject );

		DPRINTF("CWT: Banlist count %ld objects\n",m_pTransferBanList->Count());
	}

	if ( TransferBanObject && (TransferBanObject->m_nRequestCount > 0) )
	{
		i = lrint(ceil((time(0)-TransferBanObject->m_tTime)/60.0)*4);

		if ( i < TransferBanObject->m_nRequestCount )
		{
			ban = true;
		}
	}

	waittransfer = UpdateWaitTransfer( Transfer );
	
	DPRINTF("CWT: CheckWaitTransfer II: %s on %s\n",
			Transfer->GetDstNick().Data(),
			Transfer->GetHubName().Data() );

	// now we check if we need to set the correct nick for the hub
	if ( Transfer->GetNick().IsEmpty() )
	{
		Transfer->SetNick( CConfig::Instance()->GetNick( Transfer->GetHubName(), Transfer->GetHubHost() ) );

		DPRINTF("CWT: Set transfer NICK: '%s'\n",Transfer->GetNick().Data() );

		// send the correct nick
		Transfer->SendMyNick( Transfer->GetNick(), Transfer->GetHubHost() );
	}

	if ( (TransferObject = m_pDownloadQueue->GetUserTransferObject( Transfer->GetDstNick(), Transfer->GetHubName(), Transfer->GetHubHost() )) != 0 )
	{
		DPRINTF("CWT: Waiting: %s on %s %s\n",
				TransferObject->sNick.Data(),
				TransferObject->sHubName.Data(),
				TransferObject->sHubHost.Data() );

		if ( TransferObject->eState == etwsIDLE )
		{
			SendLogInfo( "WARNING: Increase the response timeout." );
		}

		if ( (TransferObject->eState == etwsWAIT) || (TransferObject->eState == etwsIDLE) )
		{
			DPRINTF("CWT: wait found ...\n");

			TransferObject->eState = etwsRUN;
			TransferObject->iConnections++;

			// waiting transfer found ...
			res = edDOWNLOAD;

			SendFileInfo( TransferObject );
		}
		else
		{
			DPRINTF("CWT: ERROR: wait in wrong state (please report!) (%d/%d)\n",TransferObject->eState,TransferObject->iConnections);
		}
	}

	if ( res == edNONE )
	{
		if ( waittransfer )
		{
			if ( TransferBanObject )
			{
				TransferBanObject->m_nRequestCount++;
				DPRINTF("CWT: Requestcount is now %d\n",TransferBanObject->m_nRequestCount);
			}
			
			if ( ban )
			{
				Transfer->Disconnect(true);
				SendLogInfo( "WARNING: Disconnect aggressive client " + host );

				DPRINTF("CWT: Host banned\n");
			}
			else
			{
				res = edUPLOAD;
			}
		}
		else
		{
			DPRINTF("CWT: Warning: no wait transfer found for '%s'\n",Transfer->GetDstNick().Data());
			Transfer->Disconnect(true);
		}
	}

	m_pBanListMutex->UnLock();

	m_pDownloadQueue->pQueueMutex->UnLock();

	return res;
}

/** */
bool CDownloadManager::ChangeDirection( CTransfer * Transfer )
{
	bool res = false;
	DCTransferQueueObject * TransferObject;

	m_pDownloadQueue->pQueueMutex->Lock();

	if ( (TransferObject = m_pDownloadQueue->GetUserTransferObject( Transfer->GetDstNick(), Transfer->GetHubName(), Transfer->GetHubHost() )) != 0 )
	{
		DPRINTF("Waiting: %s on %s %s\n",
			TransferObject->sNick.Data(),
			TransferObject->sHubName.Data(),
			TransferObject->sHubHost.Data() );

		// change to upload ...
		if ( Transfer->GetSrcDirection() == edDOWNLOAD )
		{
			if ( TransferObject->eState == etwsRUN )
			{
				if ( TransferObject->iConnections > 0 )
					TransferObject->iConnections--;
				else
					DPRINTF("WARNING: ChangeDirection: RUN:0\n");
				if ( TransferObject->iConnections == 0 )
					TransferObject->eState = etwsIDLE;
				SendFileInfo( TransferObject );

				DPRINTF("change transfer -> upload ...\n");

				res = true;
			}
			else
			{
				DPRINTF("can't change transfer upload ...\n");
			}
		}
	}

	m_pDownloadQueue->pQueueMutex->UnLock();

	return res;
}

/** */
bool CDownloadManager::SetNextFile( CTransfer * Transfer )
{
	bool res;

	m_pDownloadQueue->pQueueMutex->Lock();
	res = SetFile(Transfer);
	m_pDownloadQueue->pQueueMutex->UnLock();

	return res;
}

/** */
bool CDownloadManager::SetDirection( CTransfer * Transfer )
{
	CTransferObject * TransferObject;
	int i;
	bool res = false;

	// check if transfer mode correct
	if ( (Transfer->GetSrcDirection() == edNONE) ||
	     (Transfer->GetDstDirection() == edNONE) )
	{
		// never use slots for wrong transfer modes
		return res;
	}

	// check max uploads for a user
	if ( Transfer->GetSrcDirection() == edUPLOAD )
	{
		// check for max user upload slots
		TransferObject = 0;
		i = 0;

		// the list is allready locked by the thread
		while( m_pTransferList->Next( &TransferObject ) )
		{
			if ( TransferObject->m_pTransfer->GetDstDirection() == edDOWNLOAD )
			{
				if ( Transfer->GetDstNick() == TransferObject->m_pTransfer->GetDstNick() )
				{
					i++;
				}
			}
		}

		// the current transfer is allready in the list
		if ( (CConfig::Instance()->GetUserUploadSlots() == 0) ||
		     (CConfig::Instance()->GetUserUploadSlots() >= i) )
		{
			// check slot limit
			i = CConfig::Instance()->GetMaxUpload();

			// unlimited slots == 0 or not all slots used
			if ( (i == 0) || (i > DownloadManagerInfo.slot_use_settings) )
			{
				DownloadManagerInfo.slot_use_settings++;
				Transfer->SetTransferType(ettSETTINGS);
				res = true;
			}
			else if ( (CConfig::Instance()->GetExtraSlotsRate() > 0) &&
			          (DownloadManagerInfo.slot_use_rate_extra < CConfig::Instance()->GetMaxExtraSlots()) &&
				  (DownloadManagerInfo.Rate() < CConfig::Instance()->GetExtraSlotsRate())
				)
			{
				const time_t now = time(0);
				if ( now > (m_tLastRateExtra + 60) )
				{
					DownloadManagerInfo.slot_use_rate_extra++;
					Transfer->SetTransferType(ettRATE_EXTRA);
					m_tLastRateExtra = now;
					res = true;
				}
			}
			
			/* check for extra user slots, they are used up even if they already got a free slot above */
			if ( CheckUserSlot( Transfer->GetDstNick(), Transfer->GetHubName() ) )
			{
				if ( res == false )
				{
					DownloadManagerInfo.slot_use_user++;
					Transfer->SetTransferType(ettUSER);
					res = true;
				}
				// else they got a slot already but still used up a granted slot
			}
			
			if ( res == false ) // no normal slot granted, allocate special than operator slot
			{
				if ( Transfer->GetDstNick().IsEmpty() )
				{
					DPRINTF("WARNING: get a free slot -> remote nick is empty\n");
				}
				else
				{
					// no free normal slot - use special slot (lists, small files, leaves)
					// set special slot if available
					if ( DownloadManagerInfo.slot_use_special < 4 )
					{
						DownloadManagerInfo.slot_use_special++;
						Transfer->SetTransferType(ettSPECIAL);
						res = true;
					}
					
					// no more special slots - use operator slot (list only)
					if ( !res && CConnectionManager::Instance()->IsAdmin(Transfer->GetHubName(),Transfer->GetHubHost(),Transfer->GetDstNick()) )
					{
						// set operator slot if available
						if ( DownloadManagerInfo.slot_use_operator < 4 )
						{
							DownloadManagerInfo.slot_use_operator++;
							Transfer->SetTransferType(ettOPERATOR);
							res = true;
						}
					}
				}
			}
		}
	}
	// allow all downloads
	else
	{
		res = true;
	}

	return res;
}

/** */
void CDownloadManager::OptimizeChunks( DCFileChunkObject * FileChunkObject )
{
	DCChunkObject *co, *co1;

	// get first not locked chunk
	co = 0;
	while ( (co=FileChunkObject->m_Chunks.Next(co)) != 0 )
	{
		if ( co->m_eChunkState == ecsFREE )
		{
			co1 = co;
			while ( (co1=FileChunkObject->m_Chunks.Next(co1)) != 0 )
			{
				if ( co1 == co )
				{
					continue;
				}

				if ( co1->m_eChunkState == ecsFREE )
				{
					if ( co->m_nEnd == co1->m_nStart )
					{
						co->m_nEnd = co1->m_nEnd;
						FileChunkObject->m_Chunks.Del(co1);
						co1 = co;
					}
					else if ( co->m_nStart == co1->m_nEnd )
					{
						co->m_nStart = co1->m_nStart;
						FileChunkObject->m_Chunks.Del(co1);
						co1 = co;
					}
				}
			}
		}
	}

}

/** */
bool CDownloadManager::GetNextChunk( CString file, ulonglong * lstart, ulonglong * lend )
{
	bool res = false;
	DCFileChunkObject * FileChunkObject;
	DCChunkObject * ChunkObject, *co, *co1, *bigco, *firstco;
	ulonglong size;
	bool split, standalone;
	ulonglong minsegsize;
	if ( CConfig::Instance() )
	{
		minsegsize = CConfig::Instance()->GetMinSegSize();
	}
	else
	{
		minsegsize = 1048576;
	}

	DPRINTF("get the next chunk for '%s'\n",file.Data());

	if ( (FileChunkObject = m_pDownloadQueue->GetFileChunkObject(file)) != 0 )
	{
		OptimizeChunks(FileChunkObject);

		co = 0;
		ChunkObject = 0;
		bigco = 0;
		firstco = 0;
		size = 0;

		// get a free chunk
		while ( (ChunkObject=FileChunkObject->m_Chunks.Next(ChunkObject)) != 0 )
		{
			if ( ChunkObject->m_eChunkState == ecsFREE )
			{
				// find the biggest chunk
				if ( (ChunkObject->m_nEnd-ChunkObject->m_nStart) > size )
				{
					bigco = ChunkObject;
					size = ChunkObject->m_nEnd-ChunkObject->m_nStart;
				}
				// find the first chunk with nothing before it
				standalone = true;
				while ( (co=FileChunkObject->m_Chunks.Next(co)) != 0 )
				{
					if ( co->m_nEnd == ChunkObject->m_nStart )
					{
						standalone = false;
					}
				}
				if (standalone) {
					if ( firstco == 0 || firstco->m_nStart > ChunkObject->m_nStart ) {
						firstco = ChunkObject;
					}
				}
			}
		}

		// prefer the first free chunk to the biggest
		if ( firstco != 0 ) {
			co = firstco;
		} else {
			co = bigco;
		}

		// found a free chunk
		if ( co != 0 )
		{
			split = false;

			// split if the half chunk size > max. chunk size
			if ( ((co->m_nEnd-co->m_nStart)/2) > minsegsize )
			{
				ChunkObject = 0;

				// check if a running chunk for this chunk
				while ( (ChunkObject=FileChunkObject->m_Chunks.Next(ChunkObject)) != 0 )
				{
					if ( ChunkObject->m_eChunkState == ecsLOCKED )
					{	
						if ( ChunkObject->m_nEnd == co->m_nStart )
						{
							split = true;
						}
					}
				}
			}

			ChunkObject = co;

			if ( ( (ChunkObject->m_nEnd - ChunkObject->m_nStart) > minsegsize ) && FileChunkObject->m_bMulti )
			{
				// create a new chunk
				co = new DCChunkObject();
	
				// split chunk
				if ( (ChunkObject->m_nStart == 0) || (split == false) )
				{
					co->m_nStart  = ChunkObject->m_nStart;
					co->m_nEnd    = ChunkObject->m_nStart+minsegsize;
					ChunkObject->m_nStart += minsegsize;
				}
				else
				{
					DPRINTF("CHUNK SET 1: %llu %llu\n",ChunkObject->m_nStart,ChunkObject->m_nEnd);

					co->m_nStart = ChunkObject->m_nStart+((ChunkObject->m_nEnd - ChunkObject->m_nStart)/2);

					if ( (ChunkObject->m_nEnd-co->m_nStart) > minsegsize )
					{
						co->m_nEnd = co->m_nStart+minsegsize;
						co1 = new DCChunkObject();
						co1->m_nStart = co->m_nEnd;
						co1->m_nEnd   = ChunkObject->m_nEnd;
						FileChunkObject->m_Chunks.Add(co1);
						DPRINTF("CHUNK SET 2: %llu %llu\n",co1->m_nStart,co1->m_nEnd);
					}
					else
					{
						co->m_nEnd = ChunkObject->m_nEnd;
					}

					ChunkObject->m_nEnd = co->m_nStart;

					DPRINTF("CHUNK SET 3: %llu %llu\n",ChunkObject->m_nStart,ChunkObject->m_nEnd);
					DPRINTF("CHUNK SET 4: %llu %llu\n",co->m_nStart,co->m_nEnd);
				}

				co->m_eChunkState = ecsLOCKED;

				FileChunkObject->m_Chunks.Add(co);

				*lstart = co->m_nStart;
				*lend   = co->m_nEnd;

				DPRINTF("NEW CHUNK SPLIT/LOCKED: %llu %llu\n",*lstart,*lend);

				res = true;
			}
			else
			{
				ChunkObject->m_eChunkState = ecsLOCKED;

				*lstart = ChunkObject->m_nStart;
				*lend   = ChunkObject->m_nEnd;

				DPRINTF("NEW CHUNK LOCKED: %llu %llu\n",*lstart,*lend);

				res = true;
			}
		}
	}
	else
	{
		DPRINTF("warning file not found in the chunk list\n");
	}

	return res;
}

/** 0: not found; 1: found and update; 2: no more chunks (file download ok) */
int CDownloadManager::UpdateChunk( CString file, ulonglong lstart, ulonglong lend, ulonglong lcurrent )
{
	int res = 0;
	DCFileChunkObject * FileChunkObject;
	DCChunkObject * ChunkObject, *co;

	DPRINTF("update chunk for '%s'\n",file.Data());

	m_pDownloadQueue->pChunksMutex->Lock();

	if ( (FileChunkObject = m_pDownloadQueue->GetFileChunkObject(file)) != 0 )
	{
		ChunkObject = 0;

		// search the chunk
		while ( (ChunkObject=FileChunkObject->m_Chunks.Next(ChunkObject)) != 0 )
		{
			if ( (ChunkObject->m_nStart == lstart) && (ChunkObject->m_nEnd == lend) )
			{
				// chunk found
				res = 1;

				if ( ChunkObject->m_eChunkState == ecsFREE )
				{
					DPRINTF("warning wrong chunk state\n");
				}

				ChunkObject->m_eChunkState = ecsFREE;

				if ( lstart != lcurrent )
				{
					// update size done
					FileChunkObject->m_nSizeDone += (lcurrent-lstart);

					DPRINTF("FILESTATE: %llu %llu\n",FileChunkObject->m_nSizeDone,FileChunkObject->m_nSize);

					if ( lcurrent == lend )
					{
						FileChunkObject->m_Chunks.Del(ChunkObject);
						ChunkObject = 0;
					}
					else
					{
						ChunkObject->m_nStart = lcurrent;
					}

					if ( FileChunkObject->m_nSizeDone == FileChunkObject->m_nSize )
					{
						// remove the complete chunk from the list
						m_pDownloadQueue->pChunkList->Del(file);
						res = 2;
						break;
					}
				}

				if ( ChunkObject != 0 )
				{
					co = 0;
					while ( (co=FileChunkObject->m_Chunks.Next(co)) != 0 )
					{
						if ( co == ChunkObject )
							continue;

						if ( ChunkObject->m_nEnd == co->m_nStart )
						{
							if ( co->m_eChunkState == ecsFREE )
							{
								co->m_nStart = ChunkObject->m_nStart;
								FileChunkObject->m_Chunks.Del(ChunkObject);
								ChunkObject = co;
								DPRINTF("CHUNK FIX1: %llu %llu\n",ChunkObject->m_nStart,ChunkObject->m_nEnd);
							}
							break;
						}
					}

					co = 0;
					while ( (co=FileChunkObject->m_Chunks.Next(co)) != 0 )
					{
						if ( co == ChunkObject )
							continue;

						if ( ChunkObject->m_nStart == co->m_nEnd )
						{
							if ( co->m_eChunkState == ecsFREE )
							{
								co->m_nEnd = ChunkObject->m_nEnd;
								FileChunkObject->m_Chunks.Del(ChunkObject);
								ChunkObject = 0;
								DPRINTF("CHUNK FIX2: %llu %llu\n",co->m_nStart,co->m_nEnd);
							}
							break;
						}
					}
				}

				break;
			}
		}
	}
	else
	{
		DPRINTF("warning file not found in the chunk list\n");
	}

	m_pDownloadQueue->pChunksMutex->UnLock();

	return res;
}

/** */
bool CDownloadManager::GetNewChunkEnd( CString file, ulonglong lstart, ulonglong lend, ulonglong lcurrent, ulonglong * lnstart, ulonglong *lnend )
{
	m_pDownloadQueue->pChunksMutex->Lock();

	bool res = false;
	DCFileChunkObject * FileChunkObject;
	DCChunkObject * ChunkObject, *co1=0,*co2=0;
	ulonglong chunk_size;
	ulonglong minsegsize;
	if ( CConfig::Instance() )
	{
		minsegsize = CConfig::Instance()->GetMinSegSize();
	}
	else
	{
		minsegsize = 1048576;
	}

	if ( (FileChunkObject = m_pDownloadQueue->GetFileChunkObject(file)) != 0 )
	{
		ChunkObject = 0;

		while ( (ChunkObject=FileChunkObject->m_Chunks.Next(ChunkObject)) != 0 )
		{
			if ( (ChunkObject->m_nStart == lstart) && (ChunkObject->m_nEnd == lend) )
			{
				// start chunk found
				co1 = ChunkObject;

				if ( co2 )
				{
					// next chunk is set -> stop search
					break;
				}
			}
			else if ( ChunkObject->m_nStart == lend )
			{
				// next chunk found
				if ( ChunkObject->m_eChunkState == ecsLOCKED )
				{
					// the chunk after the original chunk is locked -> stop search
					break;
				}
				else
				{
					// set next chunk
					co2 = ChunkObject;
       	
					if ( co1 )
					{
						// start chunk is set -> stop search
						break;
					}
				}
			}
		}

		// found start and next chunk
		if ( (co1 != 0) && (co2 != 0) )
		{
			DPRINTF("set new chunk end for '%s'\n",file.Data());

			// calc chunk size
			if ( (lend-lcurrent) > minsegsize )
				chunk_size = minsegsize;
			else
				chunk_size = minsegsize-(lend-lcurrent);

			// if chunk > max chunk split chunk to chunk size
			if ( (co2->m_nEnd-co2->m_nStart) > chunk_size )
			{
				// set new chunk end
				co1->m_nEnd   += chunk_size;
				// set correct new chunk start
				co2->m_nStart += chunk_size;
			}
			else
			{
				// set new chunk end
				co1->m_nEnd = co2->m_nEnd;
				// remote the chunk
				FileChunkObject->m_Chunks.Del(co2);
			}

			if ( (lcurrent-lstart) > 0 )
			{
				FileChunkObject->m_nSizeDone += (lcurrent-lstart);
				co1->m_nStart = lcurrent;
			}

			*lnstart = co1->m_nStart;
			*lnend   = co1->m_nEnd;

			DPRINTF("new chunk end set %llu -> %llu [%llu/%llu]\n",lend,*lnend,(*lnend)-(*lnstart),chunk_size);

			res = true;
		}
	}
	else
	{
		DPRINTF("warning file not found in the chunk list\n");
	}

	m_pDownloadQueue->pChunksMutex->UnLock();

	return res;
}

/** */
bool CDownloadManager::SetFile( CTransfer * Transfer )
{
	bool res;
	DCTransferQueueObject * TransferObject;
	ulonglong lstart,lend;
	int priority = 0;
	res = false;

	if ( (TransferObject = m_pDownloadQueue->GetUserTransferObject( Transfer->GetDstNick(), Transfer->GetHubName(), Transfer->GetHubHost() )) != 0 )
	{
		if ( TransferObject->pTransferFileList.Count() > 0 )
		{
			DCTransferFileObject * TransferFileObject = 0;

			while( (priority <= MAX_FILE_PRIORITY) && (TransferFileObject == 0) )
			{
			while( TransferObject->pTransferFileList.Next(&TransferFileObject) )
			{
				// check the priority
				if ( priority != TransferFileObject->m_nPriority )
					continue;

				if ( TransferFileObject->m_eState == etfsNONE )
				{
					DPRINTF("set file: '%s'\n",TransferFileObject->m_sRemoteFile.Data());

					CString sPath,sLocalPath,sFile;
					CDir dir;

					if ( TransferFileObject->m_eMedium == eltFILE )
					{
						sFile = TransferFileObject->m_sLocalFile;

						DPRINTF("DEBUG: file: '%s'\n",sFile.Data());

						// extract file
						int pos = sFile.FindRev(DIRSEPARATOR);
						if( pos != -1 )
						{
							sPath = sFile.Left(pos);
						}

						DPRINTF("DEBUG: path: '%s'\n", sPath.Data());

						// create the path
						if ( dir.CreatePath(sPath) == false )
						{
							TransferFileObject->m_eState = etfsERROR;
							SendFileInfo( TransferObject, TransferFileObject );
							SendLogInfo( "Create path failed: " + sPath, Transfer );
							DPRINTF("DEBUG: create path failed: '%s'\n", sPath.Data());
						}
						else
						{
							DPRINTF("DOWNLOAD: '%s' %llu '%s'\n",
								TransferFileObject->m_sRemoteFile.Data(),
								TransferFileObject->m_nSize,
								sFile.Data());

								res = true;
						}
					}
					else
					{
						res = true;
					}

					if ( res )
					{
						// first we create the hash over the first 1MB of a multi download file in a buffer
						if ( TransferFileObject->m_bMulti &&
						     (TransferFileObject->m_stHash.IsEmpty()))
						{
							DPRINTF("create the hash for the file\n");

							Transfer->SetMedium(eltBUFFER);

							lstart = 0;
							lend   = TEST_CHUNK_SIZE;
						}
						else // get the next chunk of the file
						{
							if ( TransferFileObject->m_eMedium == eltCLIENTVERSION )
							{
								DPRINTF("DEBUG: resolve client version ...\n");
								lstart = 0;
								lend   = 0;
							}
							else if ( TransferFileObject->m_sRemoteFile == DC_USER_FILELIST )
							{
								lstart = 0;
								lend   = 0;
							}
							else if ( GetNextChunk( TransferFileObject->m_sLocalFile, &lstart, &lend ) == false )
							{
								// no more chunks
								DPRINTF("no more chunks ...\n");
								continue;
							}

							Transfer->SetMedium(TransferFileObject->m_eMedium);
						}

						Transfer->SetDone(etsNONE);
						
						/* This no longer happens because prefixes are removed at the lowest possible level */
						CString TTH = TransferFileObject->m_sHash;
						
						/* should be fixed since 0.3.18 and this check is unnecessary */
						if ((TTH.Left(4)).ToUpper() == "TTH:")
						{
							DPRINTF("CDownloadManager::SetFile: Removed TTH: prefix from TTH\n");
							TTH = TTH.Mid(4, TTH.Length() - 4);
						}

						if ( Transfer->StartDownload(TransferFileObject->m_sRemoteFile,lstart,lend,TransferFileObject->m_nSize,lend-lstart,sFile,TTH) == -1 )
						{
							Transfer->Disconnect();
						}
						else
						{
							// mark file as transfered
							TransferFileObject->m_eState = etfsTRANSFER;
						}
					}
					else
					{
						continue;
					}

					SendFileInfo( TransferObject, TransferFileObject );

					break;
				}
				else
				{
//					DPRINTF("state file: '%s' %d\n",TransferFileItem->sRemoteName.Data(),TransferFileItem->eState);
				}
			}

			priority++;
			}
		}
	}

	if ( res == false )
	{
		// set disconnect timeout ... save remote slot ;-)
		if ( Transfer->GetDone() != etsIDLE )
		{
			Transfer->SetStartTime(time(0));
			Transfer->SetDone(etsIDLE);
		}
	}

	return res;
}

/** */
void CDownloadManager::UpdateFileState( CTransfer * Transfer, eTransferFileState eState )
{
	DCTransferQueueObject * TransferObject;
	DCTransferFileObject * TransferFileObject;

	m_pDownloadQueue->pQueueMutex->Lock();

	DPRINTF("updatefile\n");
	if ( (TransferObject = m_pDownloadQueue->GetUserTransferObject( Transfer->GetDstNick(), Transfer->GetHubName(), Transfer->GetHubHost() )) != 0 )
	{
		TransferFileObject = m_pDownloadQueue->GetUserFileObject( Transfer->GetDstNick(), Transfer->GetHubName(), Transfer->GetHubHost(), Transfer->GetDstFilename() );

		if ( TransferFileObject != 0 )
		{
			if ( TransferFileObject->m_eState != etfsTRANSFER )
			{
				DPRINTF("warning, wrong state in updatefile\n");
			}
			// if this a check hash dl ?
			else if ( (Transfer->GetMedium() == eltBUFFER) && TransferFileObject->m_bMulti )
			{
				DPRINTF("updatefile hash\n");
				if ( eState == etfsNONE )
				{
					if ( (Transfer->GetStartPosition()+Transfer->GetTransfered()) == Transfer->GetEndPosition() )
					{
						if ( CheckHash(Transfer) )
						{
							CString logmsg = "Hash ok '";
							logmsg += TransferFileObject->m_sRemoteFile;
							logmsg += '\'';
							SendLogInfo( logmsg, Transfer );

							TransferFileObject->m_eState = etfsNONE;

							// reconnect if remote dont support chunk dl
							if ( Transfer->SupportsChunks() == false )
							{
								// wan't a reconnect
								TransferObject->bReconnect = true;
							}
						}
						else
						{
							CString logmsg = "Hash failed '";
							logmsg += TransferFileObject->m_sRemoteFile;
							logmsg += '\'';
							SendLogInfo( logmsg, Transfer );
							TransferFileObject->m_eState = etfsERROR;
						}
					}
					else
					{
						TransferFileObject->m_eState = eState;
					}
				}
				else
				{
					TransferFileObject->m_eState = eState;
				}

				SendFileInfo( TransferObject, TransferFileObject );
			}
			else
			{
				DPRINTF("updatefile normal\n");

				int state=0;

				TransferFileObject->m_eState = eState;

				if ( Transfer->GetMedium() == eltCLIENTVERSION )
				{
					state = 2;
				}
				// check if mylist.dclist done
				else if ( TransferFileObject->m_sRemoteFile == DC_USER_FILELIST )
				{
					if ( (Transfer->GetLength() != 0) &&
					     (Transfer->GetLength() == Transfer->GetTransfered()) )
					{
						FileListDone( Transfer, TransferFileObject );
						state = 2;
					}
				}
				else
				{
					state = UpdateChunk( TransferFileObject->m_sLocalFile, Transfer->GetStartPosition(),
						Transfer->GetEndPosition(), Transfer->GetStartPosition()+Transfer->GetTransfered() );
				}

				TransferFileObject->m_nSize = Transfer->GetLength();

				// download finished
				if ( state == 2 )
				{
					if ( Transfer->GetMedium() != eltCLIENTVERSION )
					{
						CString logmsg = "Transfer done '";
						logmsg += TransferFileObject->m_sRemoteFile;
						logmsg += '\'';
						SendLogInfo( logmsg, Transfer );
					}
					SendFileInfo( TransferObject, TransferFileObject, true );
					SendTransferInfo( Transfer, false );

					// finished downloads are now a log option
					if ( (TransferFileObject->m_eMedium == eltFILE) &&
					CConfig::Instance()->GetLogFile() &&
					CConfig::Instance()->GetLogFinishedDownloads() &&
					(TransferFileObject->m_sRemoteFile != DC_USER_FILELIST) )
					{
						CString logfilemsg = "Transfer done '";
						logfilemsg += TransferFileObject->m_sLocalFile;
						logfilemsg += '\'';
						CLogFile::Write(CConfig::Instance()->GetLogFileName(),eltINFO,logfilemsg);
					}
					
					// move the file to the finished path
					if ( (TransferFileObject->m_eMedium == eltFILE) && // only files
					     (CConfig::Instance()->GetDownloadFinishedFolder().NotEmpty()) && // only if dlfolder set
					     (TransferFileObject->m_sRemoteFile != DC_USER_FILELIST) && // only if this not a filelist
					     (CDir::ConvertSeparators(TransferFileObject->m_sLocalFile).Find(CDir::ConvertSeparators(CConfig::Instance()->GetDownloadFolder())) == 0) ) // only if the download in the download folder
					{
						CString s;

						// close file
						Transfer->CloseFile();
						
						CDir dir(CConfig::Instance()->GetDownloadFinishedFolder());

						if ( dir.CreatePath(TransferFileObject->m_sLocalPath) )
						{
							dir.SetPath(CConfig::Instance()->GetDownloadFinishedFolder()+DIRSEPARATOR+TransferFileObject->m_sLocalPath);
#ifdef WIN32
							s  = "move ";
							s += '"';
							s += TransferFileObject->m_sLocalFile;
							s += "\" \"";
							s += dir.Path();
							s += '"';

							DPRINTF("move file '%s'\n",s.Data());

							if ( system(s.Data()) != 0 )
							{
								DPRINTF("move failed !\n");
							}
#else
							s = dir.Path();
							s += DIRSEPARATOR;
							s += TransferFileObject->m_sLocalFileName;
							DPRINTF("move file: '%s' ---> '%s'\n",TransferFileObject->m_sLocalFile.Data(), s.Data());
							if ( rename(TransferFileObject->m_sLocalFile.Data(), s.Data()) != 0 )
							{
								if (errno == EXDEV)
								{
									// we have to copy it manually
									if ( CFile::Copy( TransferFileObject->m_sLocalFile, s ) == false )
									{
										DPRINTF("move failed !\n");
									}
									else
									{
										unlink(TransferFileObject->m_sLocalFile.Data());
									}
								}
							}
#endif
						}
						else
						{
							DPRINTF("move failed (create path)!\n");
						}
					}

					if ( TransferFileObject->m_bMulti )
					{
						// remove all files from the wait queue
						RemoveQueueFile(TransferFileObject->m_sLocalFile);
					}
					else
					{
						// remove a userlist only from this user/hub
						RemoveQueueFile( TransferObject->sNick, TransferObject->sHubName, TransferFileObject->m_sRemoteFile );
					}
				}
				else
				{
					SendFileInfo( TransferObject, TransferFileObject );
				}
			}
		}
	}
	else
	{
		DPRINTF("updatefile no GetUserTransferObject\n");
	}

	m_pDownloadQueue->pQueueMutex->UnLock();
}

/** */
void CDownloadManager::UpdateBanList( time_t ttimeout )
{
	DCTransferBanObject * TransferBanObject = 0, * tbo = 0;

	// remove old entrys
	m_pBanListMutex->Lock();

	if ( m_pTransferBanList->Count() > 0 )
	{
		CString s;

		while ( m_pTransferBanList->Next( s, &TransferBanObject ) == 1 )
		{
			if ( (ttimeout-TransferBanObject->m_tTime) > 180 )
			{
				m_pTransferBanList->Del(s);
				TransferBanObject = tbo;
			}
			else
			{
				tbo = TransferBanObject;
			}
		}
	}

	m_pBanListMutex->UnLock();
}

/** */
void CDownloadManager::UpdateTransferList( time_t ttimeout )
{
	long count,rate,tmprate;
	CTransferObject * TransferObject;
	CTransferObject *obj,*oobj;
	CStringList<CMessageFileTransferRate> * ratelist=0;
	CMessageFileTransferRate * filetransferrate;
	CList<CTransfer> * transferlist=0;

	m_pTransfersMutex->Lock();

	count = m_pTransferList->Count();

	// check if shutdown ready
	if ( (m_eShutdownState == essSHUTDOWN) && (count == 0) )
	{
		m_eShutdownState = essSHUTDOWNREADY;
	}

	if ( (ttimeout-m_tUpdateTransferTimeout) >= 1 )
	{
		// update slots
		DownloadManagerInfo.slot_max = CConfig::Instance()->GetMaxUpload();
		DownloadManagerInfo.rate_ul_settings = 0;
		DownloadManagerInfo.rate_ul_operator = 0;
		DownloadManagerInfo.rate_ul_user     = 0;
		DownloadManagerInfo.rate_ul_special  = 0;
		DownloadManagerInfo.rate_ul_rate_extra = 0;
		DownloadManagerInfo.rate_dl          = 0;

		UpdateBanList( ttimeout );
	}

	if ( count > 0 )
	{
		obj = oobj = 0;

		ratelist = new CStringList<CMessageFileTransferRate>();
		transferlist = new CList<CTransfer>();

		while( m_pTransferList->Next( &obj ) )
		{
			TransferObject = obj;

			// check if user offline and disconnect
			if ( (ttimeout-TransferObject->m_UserDisconnectTimeout) > 180 )
			{
				eCloseType closetype = CConfig::Instance()->GetHubOfflineTransferClose();

				TransferObject->m_UserDisconnectTimeout = ttimeout;

				if ( CConnectionManager::Instance()->IsHubOnline( TransferObject->m_pTransfer->GetHubName(), TransferObject->m_pTransfer->GetHubHost() ) == ehsONLINE )
				{
					if ( CConnectionManager::Instance()->IsUserOnline( TransferObject->m_pTransfer->GetDstNick(), TransferObject->m_pTransfer->GetHubName(), TransferObject->m_pTransfer->GetHubHost(), 0 ) == false )
					{
						if ( (TransferObject->m_pTransfer->GetSrcDirection()==edDOWNLOAD) &&
					   	( (closetype==ectBOTH) || (closetype==ectDLD) ) )
						{
							TransferObject->m_pTransfer->Disconnect(true);
							CString logmsg = "Disconnect offline user: ";
							logmsg += TransferObject->m_pTransfer->GetDstNick();
							logmsg += '@';
							logmsg += TransferObject->m_pTransfer->GetHubName();
							SendLogInfo(logmsg);
						}
						else if ( (TransferObject->m_pTransfer->GetSrcDirection()==edUPLOAD) &&
					        	( (closetype==ectBOTH) || (closetype==ectUPLD) ) )
						{
							TransferObject->m_pTransfer->Disconnect(true);
							CString logmsg = "Disconnect offline user: ";
							logmsg += TransferObject->m_pTransfer->GetDstNick();
							logmsg += '@';
							logmsg += TransferObject->m_pTransfer->GetHubName();
							SendLogInfo(logmsg);
						}
					}
				}
				else
				{
					//DPRINTF("Not disconnecting user because hub is offline\n");
				}
			}

			// refresh transfer
			TransferObject->m_pTransfer->Thread();

			if ( (ttimeout-m_tUpdateTransferTimeout) >= 1 )
			{
				// done==2 set new file or disconnect
				if ( (TransferObject->m_pTransfer->GetDone() == etsIDLE) &&
				     (m_eShutdownState == essNONE) )
				{
					bool newfile = false;

					// set new file on download mode
					if ( TransferObject->m_pTransfer->GetSrcDirection() == edDOWNLOAD )
					{
						while(1)
						{
							// stop if we cant set a new file
							if ( SetFile(TransferObject->m_pTransfer) == false )
							{
								break;
							}

							// check for clientcheck
							if ( TransferObject->m_pTransfer->GetMedium() == eltCLIENTVERSION )
							{
								// remove clientcheck from queue (we have the client info at connect)
								UpdateFileState(TransferObject->m_pTransfer,etfsNONE);
							}
							else
							{
								// new file set successful
								newfile = true;
								break;
							}
						}
						
					}

					// no new file or another transfer mode
					if ( newfile == false )
					{
						// check timeout for ul/dl
						if ( TransferObject->m_pTransfer->GetStartTime() != 0 )
						{
							if ( (ttimeout-TransferObject->m_pTransfer->GetStartTime()) >= 60 )
							{
								TransferObject->m_pTransfer->SetStartTime(0);
								TransferObject->m_pTransfer->Disconnect(true);
							}
						}
					}

					SendTransferInfo(TransferObject->m_pTransfer);

					oobj = obj;
				}
				else if ( TransferObject->m_pTransfer->GetDone() == etsREADY )
				{
					if ( TransferObject->m_pTransfer->GetDstDirection() == edDOWNLOAD )
					{
						// TODO: update slot infos etc. in downloadmanager and gui
						if ( TransferObject->m_pTransfer->GetTransferType() == ettSETTINGS )
						{
							if ( DownloadManagerInfo.slot_use_settings > 0 )
								DownloadManagerInfo.slot_use_settings--;
						}
						else if ( TransferObject->m_pTransfer->GetTransferType() == ettOPERATOR )
						{
							if ( DownloadManagerInfo.slot_use_operator > 0 )
								DownloadManagerInfo.slot_use_operator--;
						}
						else if ( TransferObject->m_pTransfer->GetTransferType() == ettUSER )
						{
							if ( DownloadManagerInfo.slot_use_user > 0 )
								DownloadManagerInfo.slot_use_user--;
						}
						else if ( TransferObject->m_pTransfer->GetTransferType() == ettSPECIAL )
						{
							if ( DownloadManagerInfo.slot_use_special > 0 )
								DownloadManagerInfo.slot_use_special--;
						}
						else if ( TransferObject->m_pTransfer->GetTransferType() == ettRATE_EXTRA )
						{
							if ( DownloadManagerInfo.slot_use_rate_extra > 0 )
							{
								DownloadManagerInfo.slot_use_rate_extra--;
							}
						}

						// update used slots
						CConnectionManager::Instance()->SendMyInfoToConnectedServers();
					}

					SendTransferInfo( TransferObject->m_pTransfer, true );

					// update internal transfer state for this user/hubname
					// set to idle (this work only with 1 transfer per user/hubname ...)
					DCTransferQueueObject * TransferQueueObject;

					if ( TransferObject->m_pTransfer->GetSrcDirection() == edDOWNLOAD )
					{
						if ( (TransferQueueObject = m_pDownloadQueue->GetUserTransferObject( TransferObject->m_pTransfer->GetDstNick(), TransferObject->m_pTransfer->GetHubName(), TransferObject->m_pTransfer->GetHubHost() )) != 0 )
						{
							if ( TransferQueueObject->eState == etwsRUN )
							{
								if ( TransferQueueObject->iConnections > 0 )
									TransferQueueObject->iConnections--;
								else
									DPRINTF("WARNING: UpdateTransferList: RUN:0\n");
								if ( TransferQueueObject->iConnections == 0 )
									TransferQueueObject->eState = etwsIDLE;
							}
							else
							{
								DPRINTF("WARNING: UpdateTransferList: wrong queue state\n");
							}

							// wan't reconnect ?
							if ( TransferQueueObject->bReconnect )
							{
								TransferQueueObject->tTimeout = 0;
								TransferQueueObject->bReconnect = false;
							}
							else
							{
								TransferQueueObject->tTimeout = ttimeout;
							}

							SendFileInfo( TransferQueueObject );
						}
					}

					m_pTransferList->Del( CString::number(TransferObject->m_pTransfer->GetTransferID()) );

					obj = oobj;
				}
				else if ( m_eShutdownState == essNONE )
				{
					if ( TransferObject->m_pTransfer->GetSrcDirection() == edUPLOAD )
					{
						switch(TransferObject->m_pTransfer->GetTransferType())
						{
							case ettSETTINGS:
								DownloadManagerInfo.rate_ul_settings += TransferObject->m_pTransfer->GetTransferrate();
								break;
							case ettOPERATOR:
								DownloadManagerInfo.rate_ul_operator += TransferObject->m_pTransfer->GetTransferrate();
								break;
							case ettUSER:
								DownloadManagerInfo.rate_ul_user += TransferObject->m_pTransfer->GetTransferrate();
								break;
							case ettSPECIAL:
								DownloadManagerInfo.rate_ul_special += TransferObject->m_pTransfer->GetTransferrate();
								break;
							case ettRATE_EXTRA:
								DownloadManagerInfo.rate_ul_rate_extra += TransferObject->m_pTransfer->GetTransferrate();
								break;
							default:
								break;
						}

						SendTransferInfo(TransferObject->m_pTransfer);
					}
					else if ( TransferObject->m_pTransfer->GetSrcDirection() == edDOWNLOAD )
					{
						DownloadManagerInfo.rate_dl += TransferObject->m_pTransfer->GetTransferrate();

						// create the filetransferratelist
						if ( TransferObject->m_pTransfer->GetDstFilename() != DC_USER_FILELIST )
						{
							filetransferrate = 0;
							if ( ratelist->Get( TransferObject->m_pTransfer->GetSrcFilename(), &filetransferrate ) == 0 )
							{
								filetransferrate->m_nRate += TransferObject->m_pTransfer->GetTransferrate();
							}
							else
							{
								filetransferrate = new CMessageFileTransferRate();
								filetransferrate->m_sLocalFile = TransferObject->m_pTransfer->GetSrcFilename();
								filetransferrate->m_nRate = TransferObject->m_pTransfer->GetTransferrate();

								ratelist->Add( TransferObject->m_pTransfer->GetSrcFilename(), filetransferrate );
							}
						}

						transferlist->Add(TransferObject->m_pTransfer);
					}
					else
					{
						SendTransferInfo(TransferObject->m_pTransfer);
					}

					oobj = obj;
				}
				else
				{
					oobj = obj;
				}
			}
		}
	}


	// send all download with global filetransferrate
	if ( (ratelist != 0) && (transferlist != 0) )
	{
		CTransfer *tr=0;
		filetransferrate = 0;

		while ( (tr=transferlist->Next(0)) != 0 )
		{
			if ( ratelist->Get( tr->GetSrcFilename(), &filetransferrate ) == 0 )
			{
				LogMutex.Lock();

				CMessageDMTransferObject * to = CreateDMTransferObject(tr);

				to->m_nMultiRate = filetransferrate->m_nRate;

				if ( DC_DownloadManagerCallBack(to) == -1 )
					delete to;

				LogMutex.UnLock();
			}
			else
			{
				SendTransferInfo(tr);
			}

			transferlist->Remove(tr);
		}

		delete ratelist;
		delete transferlist;
	}

	if ( ((ttimeout-m_tUpdateTransferTimeout) >= 1) && (count > 0) )
	{
		// dynamic upload rate
		if ( CConfig::Instance()->GetDynamicUploadRate() &&
		     (CConfig::Instance()->GetMaxUploadRate() > 0) &&
		     (DownloadManagerInfo.slot_use_settings > 0) &&
		     (DownloadManagerInfo.slot_max > 0) &&
		     (DownloadManagerInfo.rate_ul_settings<(CConfig::Instance()->GetMaxUploadRate()*DownloadManagerInfo.slot_max)) )
		{
			// calculate max rate for every slot
			rate = CConfig::Instance()->GetMaxUploadRate()*DownloadManagerInfo.slot_max;

			// this is the max rate for every slot but if a slot use less than
			// we give the other slots the "restrate"
			rate /= DownloadManagerInfo.slot_use_settings;

			if ( rate > 0 )
			{
				obj     = 0;
				count   = 0;
				tmprate = 0;

				while( m_pTransferList->Next( &obj ) )
				{
					TransferObject = obj;

					if ( (TransferObject->m_pTransfer->GetDone() == etsNONE) &&
					     (TransferObject->m_pTransfer->GetSrcDirection() == edUPLOAD) &&
					     (TransferObject->m_pTransfer->GetTransferType() == ettSETTINGS) )
					{
						// check if the slot use less than 1/2 of the max rate
						if ( (TransferObject->m_pTransfer->GetTransferrate() < (TransferObject->m_pTransfer->GetRate()/2)) && ((ulonglong)rate >= TransferObject->m_pTransfer->GetRate()) )
						{
							// set the new rate for this transfer (3/4)
							TransferObject->m_pTransfer->SetRate((TransferObject->m_pTransfer->GetRate()*3/4));
							// give other slots the restrate
							tmprate += rate-TransferObject->m_pTransfer->GetRate();
						}
						// check if the slot use less than 3/4 but more as 1/2 of the current rate we made no changes
						else if ( (TransferObject->m_pTransfer->GetTransferrate() < (TransferObject->m_pTransfer->GetRate()*3/4)) && ((ulonglong)rate >= TransferObject->m_pTransfer->GetRate()) )
						{
							// give other slots the restrate
							tmprate += rate-TransferObject->m_pTransfer->GetRate();
						}
						// all other slots use more as 3/4 or the max rate for a slot is less than the current rate for the transfer
						else
						{
							count++;
							tmprate += rate;
						}
					}
				}

				// calc new rate
				if ( (tmprate > 0) && (count > 0) )
				{
					tmprate /= count;
					obj     = 0;

					while( m_pTransferList->Next( &obj ) )
					{
						TransferObject = obj;

						if ( (TransferObject->m_pTransfer->GetDone() == etsNONE) &&
						     (TransferObject->m_pTransfer->GetSrcDirection() == edUPLOAD) )
						{
							if ( (TransferObject->m_pTransfer->GetTransferrate() >= (TransferObject->m_pTransfer->GetRate()*3/4)) || ((ulonglong)rate < TransferObject->m_pTransfer->GetRate()) )
							{
								TransferObject->m_pTransfer->SetRate(tmprate);
							}
						}
					}
				}
			}
		}

		// send dl manager info
		SendDownloadManagerInfo(&DownloadManagerInfo);
	}

	if ( (ttimeout-m_tUpdateTransferTimeout) >= 1 )
	{
		// send traffic info
		SendTrafficInfo();
	}

	m_pTransfersMutex->UnLock();
}

/** */
void CDownloadManager::UpdateQueueList( time_t ttimeout )
{
	int i;
	CString nick;
	CStringList<DCTransferQueueObject> * StringList = 0;
	CStringList<DCTransferQueueObject> * OldStringList = 0;
	CList<DCHubObject> hublist;
	DCHubObject * HubObject1 = 0, * HubObject2 = 0;
	CString hubname;

	while( m_pDownloadQueue->pQueue->Next( nick, &StringList) )
	{
		DCTransferQueueObject * TransferObject = 0;

		while( StringList->Next( &TransferObject) )
		{
			// check for empty filelist
			if ( TransferObject->pTransferFileList.Count() == 0 )
			{
				// empty filelist and no connections
				if ( TransferObject->iConnections == 0 )
				{
					// remove from queue
					SendFileInfo( TransferObject, 0, true );
					StringList->Del(TransferObject->sHubName);
				}
				break;
			}
			// handle wait state
			else if ( (TransferObject->eState == etwsWAIT) && (m_eShutdownState == essNONE) )
			{
				// check response timeout
				if ( (ttimeout-TransferObject->tTimeout) >= CConfig::Instance()->GetTransferResponseTimeout() )
				{
					// set timeout
					TransferObject->eState = etwsIDLE;
					TransferObject->tTimeout = ttimeout;
					SendFileInfo( TransferObject );
				}
			}
			// handle none wait state
			else if ( (m_eShutdownState == essNONE) &&
				( (TransferObject->eState == etwsIDLE) ||
				  (TransferObject->eState == etwsHUBOFFLINE) ||
				  (TransferObject->eState == etwsUSEROFFLINE) ||
				  (TransferObject->eState == etwsUSERBUSY) ||
				  (TransferObject->eState == etwsSENDERROR) ) )
			{

				// idle state ... check for timeout to reconnect
				if ( TransferObject->tTimeout == 0 )
				{
					// check for max download rate before new connection start
					if ( !((CConfig::Instance()->GetMaxDownloadRate() != 0)  &&
					     (CConfig::Instance()->GetMaxDownloadRate() < DownloadManagerInfo.rate_dl)) )
					{
						// reset hubname
						hubname = TransferObject->sHubName;

						// update internal hublist
						if ( CConnectionManager::Instance()->IsUserOnline( TransferObject->sNick, CString(), CString(), &hublist ) )
						{
							DPRINTF("user is online on:\n");

							HubObject1 = 0;
							while( (HubObject1=hublist.Next(HubObject1)) != 0 )
							{
								DPRINTF("'%s' '%s'\n",HubObject1->m_sHubName.Data(),HubObject1->m_sHubHost.Data());

								HubObject2 = 0;
								while( (HubObject2=TransferObject->pHubList.Next(HubObject2)) != 0 )
								{
									if ( HubObject1->m_sHubName == HubObject2->m_sHubName )
										break;
								}

								// add new entry
								if ( HubObject2 == 0 )
								{
									DPRINTF("NEW '%s' '%s'\n",HubObject1->m_sHubName.Data(),HubObject1->m_sHubHost.Data());
									HubObject2 = new DCHubObject();
									HubObject2->m_sHubName = HubObject1->m_sHubName;
									HubObject2->m_sHubHost = HubObject1->m_sHubHost;
									HubObject2->m_bActive = true;

									TransferObject->pHubList.Add(HubObject2);
								}
								else // set entry to connect
								{
									if ( (hubname.IsEmpty()) && HubObject2->m_bActive )
									{
										DPRINTF("USE '%s'\n",HubObject2->m_sHubName.Data());
										hubname = HubObject2->m_sHubName;
									}
								}
							}

							hublist.Clear();
						}

						// check for available files
						DCTransferFileObject * TransferFileObject = 0;

						while( TransferObject->pTransferFileList.Next( &TransferFileObject ) )
						{
							if ( TransferFileObject->m_eState == etfsNONE )
							{
								break;
							}
						}

						if ( TransferFileObject != 0 )
						{
							// send connection request to the hub
							i = CConnectionManager::Instance()->SendConnectionRequest( TransferObject->sNick, hubname, TransferObject->sHubHost );

							switch (i)
							{
								case 0:
									TransferObject->eState = etwsWAIT;
									break;
								case -1:
									TransferObject->eState = etwsUSEROFFLINE;
									break;
								case -2:
								case -3:
									TransferObject->eState = etwsHUBOFFLINE;
									break;
								case -4:
									TransferObject->eState = etwsSENDERROR;
									break;
								default:
									break;
							}
						}
					}

					// update timeout
					TransferObject->tTimeout = ttimeout;
					// send info
					SendFileInfo( TransferObject );
				}
				// check resend timeout
				else if ( (ttimeout-TransferObject->tTimeout) >= CConfig::Instance()->GetTransferResendTimeout() )
				{
					// reset timeout
					TransferObject->tTimeout = 0;
					// send info
					SendFileInfo( TransferObject );
				}
			}
		}

		// remove nick on empty list
		if ( StringList->Count() == 0 )
		{
			m_pDownloadQueue->pQueue->Del(nick);
			StringList = OldStringList;
		}
		else
		{
			OldStringList = StringList;
		}
	}
}

/** */
bool CDownloadManager::InitSearch( time_t /*ttimeout*/ )
{
	CStringList<DCTransferQueueObject> * StringList = 0;

	// clear searchlist
	// TODO: we can leave it and search again on the last break, 
	//       but we need a check against the current queue (traffic)
	m_pSearchList->Clear();
	m_pSearchQueryList->Clear();

	if ( !CSearchManager::Instance() )
	{
		return false;
	}

	m_pDownloadQueue->pQueueMutex->Lock();

	while( m_pDownloadQueue->pQueue->Next( &StringList ) )
	{
		DCTransferQueueObject * TransferObject = 0;

		while( StringList->Next( &TransferObject ) )
		{
			DCTransferFileObject * TransferFileObject = 0;

			while( TransferObject->pTransferFileList.Next( &TransferFileObject ) )
			{
				if ( TransferFileObject->m_bMulti && // only if md enabled
				     TransferFileObject->m_sHash.NotEmpty() && // only if we have a TTH
				     (TransferFileObject->m_eMedium == eltFILE) ) // only if medium file
				{
					bool dupe = false;
					CMessageSearchResult * it = 0;
					while ( (it = m_pSearchList->Next(it)) != 0 )
					{
						if ( it->m_sHash == TransferFileObject->m_sHash )
						{
							dupe = true;
							break;
						}
					}
					
					if ( dupe )
					{
						continue;
					}
					
					CMessageSearchResult * msg = new CMessageSearchResult();

					msg->m_nSize    = TransferFileObject->m_nSize;
					msg->m_sFile    = TransferFileObject->m_sRemoteFile;
					msg->m_sNick    = TransferObject->sNick;
					msg->m_sHubName = TransferObject->sHubName;
					msg->m_sHash    = TransferFileObject->m_sHash;

					CMessageSearchFile * smsg = new CMessageSearchFile();
					
					smsg->m_sString   = TransferFileObject->m_sHash;
					smsg->m_eFileType = eftHASH;
					smsg->m_bLocal = (CConfig::Instance()->GetMode() == ecmPASSIVE);
					/* CClient sets the source (nick or IP) before sending the search */
				
					m_pSearchList->Add(msg);
					m_pSearchQueryList->Add(smsg);
				}
			}
		}
	}

	m_pDownloadQueue->pQueueMutex->UnLock();

	if ( m_pSearchList->Count() <= 0 )
	{
		return false;
	}

	if ( CSearchManager::Instance()->StartSearch(esmCONNECTEDALL,estyEXTERNAL,m_pSearchQueryList,0) != eseNONE )
	{
		return false;
	}

	return true;
}

/** thread callbackfunction */
int CDownloadManager::Callback()
{
	int i;
	time_t ttimeout;

	if ( m_eShutdownState == essSHUTDOWNREADY )
	{
		return 0;
	}

	ttimeout = time(0);

	if ( m_eShutdownState == essNONE )
	{
		// save queue
		i = CConfig::Instance()->GetDownloadQueueTime();

		if ( i > 0 )
		{
			i *= 60;

			if ( (ttimeout-m_tDownloadQueueTimeout) > i )
			{
				DLM_SaveQueue();
				m_tDownloadQueueTimeout = ttimeout;
			}
		}
	}

	// update the transfer list
	UpdateTransferList(ttimeout);

	// update the queue list
	if ( (ttimeout-m_tUpdateTransferTimeout) >= 1 )
	{
		m_pDownloadQueue->pQueueMutex->Lock();

		if ( m_pDownloadQueue->pQueue->Count() > 0 )
		{
			UpdateQueueList(ttimeout);
		}

		m_pDownloadQueue->pQueueMutex->UnLock();
	}

	/*
	 * If dclib sent a ConnectToMe but there was no response,
	 * the entry was never removed from m_pTransferWaitList.
	 * m_pTransferWaitList is limited to 250 entries, after which
	 * no more transfers will be allowed, i.e. if you had 250 entries
	 * stuck in it you had to restart valknut.
	 */
	if ( (ttimeout-m_tWaitListCleaned) >= 60 )
	{
		m_pWaitListMutex->Lock();
		
		DCTransferWait *tw = 0, *tw_prev = 0;
		int max_age = CConfig::Instance()->GetTransferResendTimeout() * 5;
		
		if ( max_age < 5*60 )
		{
			max_age = 5*60;
		}
		
		while ( (tw = m_pTransferWaitList->Next(tw)) != 0 )
		{
			/*
			 * dclib 0.3.23 fills in the empty info, an
			 * empty nick or user ip means this transfer
			 * has not been established.
			 */
			if ( tw->sNick.IsEmpty() || tw->sUserHost.IsEmpty() )
			{
				if ( (ttimeout - tw->tTimeout) > max_age )
				{
					DPRINTF("Wait on %s/%s expired\n",tw->sNick.Data(),tw->sUserHost.Data());
					m_pTransferWaitList->Del(tw);
					tw = tw_prev;
				}
			}
			
			tw_prev = tw;
		}
		
		m_pWaitListMutex->UnLock();
		
		m_tWaitListCleaned = ttimeout;
	}

	if ( CConfig::Instance()->GetTransferAutoSearch() &&
	     CSearchManager::Instance() )
	{
		// if there is no search running and the timeout is not set it can be set
		// but if the user searches the timeout must be unset and not be reset until the user
		// has finished searching
		if ( (m_tHubSearchTimeout == 0) && (CSearchManager::Instance()->SearchType() == estyNONE) )
		{
			// start countdown
			m_tHubSearchTimeout = ttimeout;
		}
		else if ( (m_tHubSearchTimeout != 0) && ((CSearchManager::Instance()->SearchType() == estySINGLE) || (CSearchManager::Instance()->SearchType() == estyMULTI)) )
		{
			// stop countdown
			m_tHubSearchTimeout = 0;
		}
		
		// search for new sources
		if ( (m_tHubSearchTimeout != 0) && (ttimeout-m_tHubSearchTimeout) >= CConfig::Instance()->GetAutoSearchInterval() )
		{
			DPRINTF("init search\n");
			
			if ( InitSearch(ttimeout) == false )
			{
				DPRINTF("failed\n");
				m_tHubSearchTimeout = ttimeout;
			}
			else
			{
				m_tHubSearchTimeout = 0;
			}
		}
	}

	// update timeout
	m_tUpdateTransferTimeout = ttimeout;

	return 0;
}

/** Called directly by either of the listen managers */
int CDownloadManager::ListenCallbackHandler( int handle, bool crypto )
{
	bool disc = false;

	if ( m_eShutdownState == essNONE )
	{
		m_pWaitListMutex->Lock();

		if ( m_pTransferWaitList->Count() == 0 )
		{
			// no waiting transfers, disconnect incoming connection
			disc = true;
		}

		m_pWaitListMutex->UnLock();
	}
	else
	{
		// dont accept connections on shutdown state
		disc = true;
	}

	if ( disc )
	{
#ifdef WIN32
		closesocket(handle);
#else
		close(handle);
#endif
		handle = -1;
	}

	if ( handle == -1 )
	{
		return -1;
	}

	CTransferObject * TransferObject = new CTransferObject();
	TransferObject->m_pTransfer = new CTransfer(true);

	if ( crypto )
	{
		if ( TransferObject->m_pTransfer->ChangeSocketMode( esmFULLSSLSERVER, CConfig::Instance()->GetTransferCert(), CConfig::Instance()->GetTransferKey() ) == false )
		{
			DPRINTF("New transfer change to SSL server mode failed\n");
			delete TransferObject;
			return -1;
		}
	}

	TransferObject->m_pTransfer->SetTransferID( GetNewID() );
	TransferObject->m_pTransfer->SetRate( CConfig::Instance()->GetMaxUploadRate() );
	TransferObject->m_pTransfer->SetCallBackFunction( new CCallback2<CDownloadManager, CTransfer, CDCMessage*>( this, &CDownloadManager::DM_TransferCallBack ) );

	if ( TransferObject->m_pTransfer->SetSocket(handle) == 0 )
	{
		CString logmsg = "Incoming connection from '";
		logmsg += TransferObject->m_pTransfer->GetHost();
		logmsg += '\'';
		SendLogInfo(logmsg);

		m_pTransfersMutex->Lock();
		m_pTransferList->Add( CString::number(TransferObject->m_pTransfer->GetTransferID()), TransferObject );
		m_pTransfersMutex->UnLock();
	}
	else
	{
		delete TransferObject;
#ifdef WIN32
		closesocket(handle);
#else
		close(handle);
#endif
	}

	return 0;
}

/** */
int CDownloadManager::DM_TransferCallBack( CTransfer * Transfer, CDCMessage * DCMsg )
{
	TransferCallBackMutex.Lock();

	CByteArray ba;
	CString s,r;
	ulonglong  len;
	CDir dir;
	bool remove,bdirec;
	eShareBufferType stype;
	eDirection direction;

	switch ( DCMsg->m_eType )
	{
		case DC_MESSAGE_MYNICK:
		{
			// check with the nick for download or upload
			direction = CheckWaitTransfer( Transfer );
			Transfer->SetSrcDirection(direction);

			DCMessageConnectClient mcc;					
			mcc.m_sHubHost = Transfer->GetHost();
			CConnectionManager::Instance()->SetUserTransferInfo( Transfer->GetHubName(), Transfer->GetHubHost(), Transfer->GetDstNick(), &mcc );
			
			break;
		}

		case DC_MESSAGE_GET:
		{
			// remote want a file ...
			CMessageGet * msg = (CMessageGet*)DCMsg;

			if ( (Transfer->GetSrcDirection() == edUPLOAD) &&
			     (Transfer->GetDstDirection() == edDOWNLOAD) )
			{
				// reset done flags
				Transfer->SetDone(etsNONE);

				// send a log info
				CString logmsg = "Upload: ";
				logmsg += msg->m_sFilename;
				logmsg += " (";
				logmsg += CString::number(msg->m_nPos);
				logmsg += '/';
				logmsg += CString::number(msg->m_nSize);
				logmsg += ')';
				SendLogInfo( logmsg, Transfer );

				stype = esbtNONE;

				if ( msg->m_sFilename == DC_USER_FILELIST_HE3 )
				{
					stype = esbtHE3;
				}
				else if ( msg->m_sFilename == DC_USER_FILELIST_BZ )
				{
					stype = esbtBZ;
				}
				else if ( msg->m_sFilename == DC_USER_FILELIST_XMLBZ )
				{
					stype = esbtXMLBZ;
				}
				else if ( msg->m_sFilename == DC_USER_FILELIST_XML )
				{
					stype = esbtXML;
				}
				else if ( Transfer->GetTransferType() == ettOPERATOR )
				{
					SendLogInfo( "Operator Transfer not for the filelist", Transfer );
					Transfer->SendMaxedOut();
					Transfer->Disconnect(true);
					break;
				}

				// check special transfer file size
				if ( stype != esbtNONE )
				{
					if ( (CFileManager::Instance()->GetShareBuffer( stype, &ba ) == 0) && (ba.Size() > 0) )
					{
						// set send buffer
						Transfer->SetBuffer(&ba);
						// set local transfer medium to buffer
						Transfer->SetMedium(eltBUFFER);
						// start upload
						if ( Transfer->StartUpload( msg->m_sFilename, ba.Size(), msg->m_nPos-1, 0, msg->m_sFilename, msg->m_bUGet, false, CString(), msg->m_bZLib ) == -1 )
						{
							Transfer->Disconnect(true);
						}
					}
					else
					{
						Transfer->SendError("File Not Available");
						SendLogInfo( "Upload (no sharebuffer): " + msg->m_sFilename, Transfer );
					}
				}
				else // remote download from the share ...
				{
					// search the wanted file in the share
					s = CConfig::Instance()->AliasToPath(msg->m_sFilename);
					if ( s.IsEmpty() )
					{
						Transfer->SendError("File Not Available");

						SendLogInfo( "Upload (File Not Available): " + msg->m_sFilename, Transfer );
					}
					else
					{
						// file found, check filesize
						len = dir.getFileSize(s,false);

						if ( msg->m_nPos > len ) // if the wanted filepos > local length send an error
						{
							Transfer->SendError("File allready download.");
						}
						else if ( msg->m_nPos == 0 ) // we begin at 1 (see dc protocoll)
						{
							Transfer->SendError("Wrong file position.");
						}
						else // upload the wanted file ...
						{
							// special transfer only for files <= Configured small file size
							if ( (Transfer->GetTransferType() == ettSPECIAL) &&
							     (len > (CConfig::Instance()->GetSmallFileSize())) )
							{
								// disconnect ...
								SendLogInfo( "Special Transfer not for files > " + CUtils::GetSizeString( CConfig::Instance()->GetSmallFileSize(), euAUTO ), Transfer );
								Transfer->SendMaxedOut();
								Transfer->Disconnect(true);
							}
							else
							{
								// set local transfer medium to buffer
								Transfer->SetMedium(eltFILE);
								// start upload
								if ( Transfer->StartUpload( msg->m_sFilename, len, msg->m_nPos-1, msg->m_nSize, s, msg->m_bUGet, false, CString(), msg->m_bZLib ) == -1 )
								{
									Transfer->Disconnect(true);
								}
							}
						}
					}
				}
			}
			else
			{
				SendLogInfo( "Warning wrong mode", Transfer );
				Transfer->Disconnect(true);
			}

			break;
		}

		case DC_MESSAGE_ADCGET:
		{
			// remote want a file by TTH (or files.xml.bz2)
			CMessageADCGet * msg = (CMessageADCGet*)DCMsg;

			if ( (Transfer->GetSrcDirection() == edUPLOAD) &&
			     (Transfer->GetDstDirection() == edDOWNLOAD) )
			{
				// reset done flags
				Transfer->SetDone(etsNONE);

				if ( msg->m_eADCType == eAdcTTHL )
				{
					if ( Transfer->GetTransferType() == ettOPERATOR )
					{
						SendLogInfo( "Operator Transfer not for the filelist", Transfer );
						Transfer->SendMaxedOut();
						Transfer->Disconnect(true);
					}
					else
					{
						CByteArray * leaves = CFileManager::Instance()->GetHashLeaves( msg->m_sTTH );
						if ( leaves )
						{
							Transfer->SetMedium(eltTTHL);
							
							const unsigned long lsize = leaves->Size();
							
							Transfer->ClearAndAppendBuffer( leaves->Data(), lsize );
							delete leaves;
							
							CString logmsg = "Send leaves for ";
							logmsg += msg->m_sTTH;
							logmsg += " (";
							logmsg += CString::number(msg->m_nPos);
							logmsg += '/';
							logmsg += CString::number(lsize);
							logmsg += ')';
							SendLogInfo( logmsg, Transfer );
							
							// start upload
							if ( Transfer->StartUpload( CString(), lsize, msg->m_nPos, lsize, CString(), false, true, msg->m_sTTH, msg->m_bZlib ) == -1 )
							{
								Transfer->Disconnect(true);
							}
						}
						else
						{
							/* alternatively could base32 decode the TTH and send those 24 bytes */
							DPRINTF("Hash leaves not found for %s\n", msg->m_sTTH.Data());
							SendLogInfo( "Hash leaves not found for " + msg->m_sTTH, Transfer );
							Transfer->SendError("File Not Available");
							Transfer->Disconnect(true);
						}
					}
					break;
				}
				else if ( msg->m_eADCType == eAdcList )
				{
					Transfer->SetMedium(eltLIST);
					
					CString displayname = "Listing for ";
					displayname += msg->m_sFile;
					
					CString listing;
					/*
					 * FIXME where do we get the depth from? msg->m_nPos is always 0.
					 * It doesn't really matter because we are allowed to send
					 * less than the requested depth.
					 */
					CFileManager::Instance()->GetPartialListing( msg->m_sFile, listing );
					
					if ( listing.IsEmpty() )
					{
						SendLogInfo( "Upload (File Not Available): " + displayname, Transfer );
						Transfer->SendError("File Not Available");
						Transfer->Disconnect(true);
					}
					else
					{
						CString logmsg = "Upload: ";
						logmsg += displayname;
						logmsg += " (";
						logmsg += CString::number(msg->m_nPos);
						logmsg += '/';
						logmsg += CString::number(listing.Length());
						logmsg += ')';
						SendLogInfo( logmsg, Transfer );
						
						Transfer->ClearAndAppendBuffer( (const unsigned char*) listing.Data(), listing.Length() );
						
						if ( Transfer->StartUpload(
							msg->m_sFile,
							listing.Length(),
							msg->m_nPos,
							listing.Length(),
							displayname,
							false,
							true,
							CString(),
							msg->m_bZlib
						   ) == -1 )
						{
							Transfer->Disconnect(true);
						}
					}
					
					break;
				}
				else if ( msg->m_eADCType != eAdcFile )
				{
					CString logmsg = "Unknown ADCGET transfer type ";
					logmsg += CString::number(msg->m_eADCType);
					
					SendLogInfo( logmsg, Transfer );
					
					Transfer->SendError(logmsg);
					
					Transfer->Disconnect();
					
					break;
				}
				
				stype = esbtNONE;

				if ( msg->m_sFile == DC_USER_FILELIST_XMLBZ )
				{
					stype = esbtXMLBZ;
				}
				else if ( msg->m_sFile == DC_USER_FILELIST_XML )
				{
					stype = esbtXML;
				}
				else if ( Transfer->GetTransferType() == ettOPERATOR )
				{
					SendLogInfo( "Operator Transfer not for the filelist", Transfer );
					Transfer->SendMaxedOut();
					Transfer->Disconnect(true);
					break;
				}

				// check special transfer file size
				if ( stype != esbtNONE )
				{
					if ( (CFileManager::Instance()->GetShareBuffer( stype, &ba ) == 0) && (ba.Size() > 0) )
					{
						// set send buffer
						Transfer->SetBuffer(&ba);
						// set local transfer medium to buffer
						Transfer->SetMedium(eltBUFFER);
						// start upload
						
						/* Filelists always start at 0 */
						CString logmsg = "Upload: ";
						logmsg += msg->m_sFile;
						logmsg += " (0/";
						logmsg += CString::number(ba.Size());
						logmsg += ')';
						SendLogInfo( logmsg, Transfer );
						
						if ( Transfer->StartUpload( msg->m_sFile, ba.Size(), msg->m_nPos, 0, msg->m_sFile, false, true, CString(), msg->m_bZlib ) == -1 )
						{
							Transfer->Disconnect(true);
						}
					}
					else
					{
						Transfer->SendError("File Not Available");

						SendLogInfo( "Upload (no sharebuffer): " + msg->m_sFile, Transfer );
					}
				}
				else // remote download from the share ...
				{
					CString fileName;
					
					if ( msg->m_sTTH.IsEmpty() )
					{
						DPRINTF("Warning! ADCGet without TTH is undocumented behaviour!\n");
						if ( msg->m_sFile.NotEmpty() && (msg->m_sFile.Data()[0] == '/') )
						{
							// DPRINTF("Warning! Removing leading / from filename for ADCGet without TTH\n");
							fileName = msg->m_sFile.Mid(1, msg->m_sFile.Length() - 1);
						}
						else
						{
							fileName = msg->m_sFile;
						}
						
						CString logmsg = "Upload: ";
						logmsg += fileName;
						logmsg += " (";
						logmsg += CString::number(msg->m_nPos);
						logmsg += '/';
						logmsg += CString::number(msg->m_nSize);
						logmsg += ')';
						SendLogInfo( logmsg, Transfer );
					}
					else
					{
						// search the share for the file matching the TTH, the "TTH/" prefix is now added/removed at a lower level
						std::set<unsigned long> * results = CFileManager::Instance()->SearchHash( msg->m_sTTH );
					
						if ( (results == 0) || (results->empty()) )
						{
							Transfer->SendError("File Not Available");
							SendLogInfo( "Upload: No file found for TTH:" + msg->m_sTTH, Transfer );
							
							delete results;
							
							break;
						}
						else
						{
							if ( results->size() > 1 )
							{
								SendLogInfo( "Upload: Warning: Multiple files match TTH:" + msg->m_sTTH, Transfer );
							}
						
							fileName = CFileManager::Instance()->GetFileName(*(results->begin()));
							delete results;
							
							CString logmsg = "Upload: ";
							logmsg += fileName;
							logmsg += " (";
							logmsg += msg->m_sTTH;
							logmsg += ") (";
							logmsg += CString::number(msg->m_nPos);
							logmsg += '/';
							logmsg += CString::number(msg->m_nSize);
							logmsg += ')';
							SendLogInfo( logmsg, Transfer );
						}
					}
					
					s = CConfig::Instance()->AliasToPath(fileName);
					if ( s.IsEmpty() )
					{
						DPRINTF("Error: didn't find path to file from share alias!\n");
						Transfer->SendError("File Not Available");
						SendLogInfo( "Upload: File Not Available: " + fileName, Transfer );
					}
					else
					{
						// file found, check filesize
						len = dir.getFileSize(s,false);
						
						if ( msg->m_nSize == -1 )
						{
							msg->m_nSize = len - msg->m_nPos;
						}
						
						if ( msg->m_nPos > len ) // if the wanted filepos > local length send an error
						{
							Transfer->SendError("File already downloaded.");
						}
						else // upload the wanted file ...
						{
							// special transfer only for files <= Configured small file size
							if ( (Transfer->GetTransferType() == ettSPECIAL) &&
					     		(len > (CConfig::Instance()->GetSmallFileSize())) )
							{
								// disconnect ...
								SendLogInfo( "Special Transfer not for files > " + CUtils::GetSizeString( CConfig::Instance()->GetSmallFileSize(), euAUTO ), Transfer );
								Transfer->SendMaxedOut();
								Transfer->Disconnect(true);
							}
							else
							{
								Transfer->SetMedium(eltFILE);
								
								// start upload
								if ( Transfer->StartUpload( fileName, len, msg->m_nPos, msg->m_nSize, s, false, true, msg->m_sTTH, msg->m_bZlib ) == -1 )
								{
									Transfer->Disconnect(true);
								}
							}
						}
					}
				}
			}
			else
			{
				SendLogInfo( "Warning wrong mode", Transfer );
				Transfer->Disconnect(true);
			}

			break;
		}

		case DC_MESSAGE_DIRECTION:
		{
			break;
		}

		case DC_MESSAGE_KEY:
		{
			// direction message ...
			//CMessageDirection * msg = (CMessageDirection*)DCMsg;
			bdirec = false;

			DPRINTF("DIRECTION: level: LOCAL: %d REMOTE: %d\n",Transfer->GetSrcLevel(), Transfer->GetDstLevel() );
			DPRINTF("DIRECTION: direc: LOCAL: %d REMOTE: %d\n",Transfer->GetSrcDirection(), Transfer->GetDstDirection() );

			// equal direction ...
			if ( Transfer->GetDstDirection() == Transfer->GetSrcDirection() )
			{
				// check the level ...
				if ( Transfer->GetDstLevel() < Transfer->GetSrcLevel() )
				{
					// now we must change the dst direction
					if ( Transfer->GetSrcDirection() == edDOWNLOAD )
					{
						Transfer->SetDstDirection(edUPLOAD);
						bdirec = true;
					}
					else if ( Transfer->GetSrcDirection() == edUPLOAD )
					{
						Transfer->SetDstDirection(edDOWNLOAD);
						bdirec = true;
					}
				}
				else if ( Transfer->GetDstLevel() > Transfer->GetSrcLevel() )
				{
					if ( Transfer->GetSrcDirection() == edDOWNLOAD )
					{
						// change direction from download to upload and update the queue state
						if ( ChangeDirection(Transfer) )
						{
							Transfer->SetSrcDirection(edUPLOAD);
							bdirec = true;
						}
					}
					else if ( (Transfer->GetSrcDirection() == edUPLOAD) && (Transfer->GetDstDirection() == edUPLOAD) )
					{
						SendLogInfo( "Warning: remote want to upload a file !", Transfer );
					}
					else
					{
						SendLogInfo( "Warning: change direction not supported !", Transfer );
					}
				}
			}
			else
			{
				bdirec = true;
			}

			if ( bdirec )
			{
				// now we try to set direction with a slot check (update slots)
				if ( SetDirection(Transfer) == false )
				{
					bdirec = false;
				}
			}

			if ( bdirec == false )
			{
				SendLogInfo( "Warning no more free slots", Transfer );
				Transfer->SetDstDirection(edNONE);
				Transfer->SendMaxedOut();
				Transfer->Disconnect(true);
			}
			// check if both modes set correct
			else if ( (Transfer->GetSrcDirection() == edNONE) ||
				  (Transfer->GetDstDirection() == edNONE))
			{
				DPRINTF("DIRECTION: wrong mode ...\n");
				SendLogInfo( "Warning wrong transfer mode", Transfer );
				Transfer->SetDstDirection(edNONE);
				Transfer->Disconnect(true);
			}
			else
			{
				if ( Transfer->GetSrcDirection() == edDOWNLOAD )
				{
					DPRINTF("DIRECTION: download mode ...\n");

					while (1)
					{
						if ( SetNextFile(Transfer) == false )
						{
							DPRINTF("DIRECTION: download mode without destination file -> disconnecting!\n");
							Transfer->Disconnect(true);
							break;
						}
						else if ( Transfer->GetMedium() == eltCLIENTVERSION )
						{
							UpdateFileState(Transfer,etfsNONE);
						}
						else
						{
							break;
						}
					}
				}
				else if ( Transfer->GetSrcDirection() == edUPLOAD )
				{
					DPRINTF("DIRECTION: we are in upload mode ...\n");
				}
			}

			break;
		}

		case DC_MESSAGE_CONNECTION_STATE:
		{
			// connection-state message, generated by the ctransfer-class
			remove = true;
			CMessageConnectionState *msg = (CMessageConnectionState*)DCMsg;

			switch(msg->m_eState)
			{
				case estDISCONNECTED:
					SendLogInfo( "Disconnected from "+Transfer->GetHost(), Transfer );
					break;

//				case estCONNECTED:
//				case estSOCKETERROR:
//				case estCONNECTIONTIMEOUT:
				default:
					remove = false;
					break;
			}

			if ( remove && (Transfer->GetSrcDirection() == edDOWNLOAD) )
			{
				if ( Transfer->GetMedium() == eltBUFFER )
					Transfer->SetStartPosition(0);
				UpdateFileState(Transfer,etfsNONE);
			}

			if ( remove )
			{
				UpdateWaitTransfer(Transfer,true);

				Transfer->SetDone(etsREADY);
			}

			break;
		}

		case DC_MESSAGE_TRANSFER:
		{
			// transfer message, generated by the ctransfer-class
			// CMessageTransfer *msg = (CMessageTransfer*)DCMsg;

			// filetransfer done
			if ( Transfer->GetChunkSize() == Transfer->GetTransfered() )
			{
				// download ready
				if ( Transfer->GetSrcDirection() == edDOWNLOAD )
				{
					bool b = false;

					// set new chunk-end if available
					if ( (Transfer->GetMedium() == eltFILE) &&
				             (Transfer->SupportsChunks() == false) )
					{
						ulonglong lstart,lend;

						// get new chunk end
						if ( GetNewChunkEnd( Transfer->GetSrcFilename(), Transfer->GetStartPosition(), Transfer->GetEndPosition(), Transfer->GetStartPosition()+Transfer->GetTransfered(), &lstart, &lend ) )
						{
							Transfer->SetStartPosition(lstart);
							Transfer->SetEndPosition(lend);
							Transfer->SetChunkSize(lend-lstart);
							Transfer->SetTransfered(0);
							// reset stattime
							Transfer->SetStartTime(time(0));

							b = true;
						}

					}

					// no new chunkend set
					if ( b == false )
					{
						UpdateFileState(Transfer,etfsNONE);

						// check if transfer in idle state and set next file
						if ( Transfer->IsIdle() )
						{
							while ( SetNextFile(Transfer) )
							{
								if ( Transfer->GetMedium() == eltCLIENTVERSION )
								{
									UpdateFileState(Transfer,etfsNONE);
								}
								else
								{
									break;
								}
							}
						}
					}
				}
				else // upload ready
				{
					// set timeout to save local slots
					if ( Transfer->GetDone() != etsIDLE )
					{
						Transfer->SetStartTime(time(0));
						Transfer->SetDone(etsIDLE);
					}
				}
			}

			break;
		}

		case DC_MESSAGE_FILELENGTH:
		{
			// remote send a filelength
			CMessageFileLength * msg = (CMessageFileLength*)DCMsg;

			if ( msg->m_nFileLength == 0 )
			{
				SendLogInfo( "Warning: Filelength is NULL.", Transfer );

				if ( Transfer->GetSrcDirection() == edDOWNLOAD )
				{
					UpdateFileState(Transfer,etfsERROR);
				}

				Transfer->Disconnect(true);
			}
			else
			{
				SendTransferInfo(Transfer);
			}

			break;
		}

		case DC_MESSAGE_SENDING:
		{
			CMessageSending * msg = (CMessageSending*)DCMsg;
			
			if ( msg->m_nLength == 0)
			{
				SendLogInfo( "Warning: $Sending length is 0", Transfer );
			}
			
			SendTransferInfo(Transfer);
			
			break;
		}

		case DC_MESSAGE_ADCSND:
		{
			CMessageADCSnd * msg = (CMessageADCSnd*)DCMsg;
			
			if ( msg->m_nSize == 0 )
			{
				SendLogInfo( "Warning: $ADCSnd size is 0", Transfer );
			}
			
			SendTransferInfo(Transfer);
			
			break;
		}

		case DC_MESSAGE_SEND:
		{
			// handled by CTransfer, but send message to update GUI
			SendTransferInfo(Transfer);
			break;
		}

		case DC_MESSAGE_MAXEDOUT:
		{
			// no free slots on the remote side
			SendLogInfo( "Busy", Transfer );
			Transfer->Disconnect(true);
			break;
		}

		case DC_MESSAGE_GETLISTLEN:
		{
			// remote want the listlen
			if ( Transfer->GetSupport().m_bXMLBZList )
				Transfer->SendListLen(CFileManager::Instance()->GetShareBufferSize(esbtXMLBZ));
			else if ( Transfer->GetSupport().m_bBZList )
				Transfer->SendListLen(CFileManager::Instance()->GetShareBufferSize(esbtBZ));
			else
				Transfer->SendListLen(CFileManager::Instance()->GetShareBufferSize(esbtHE3));
			break;
		}

		case DC_MESSAGE_ERROR:
		{
			// remote send a error message
			CMessageError * msg = (CMessageError*)DCMsg;

			SendLogInfo( "Error: " + msg->m_sError, Transfer );

			if ( Transfer->GetSrcDirection() == edDOWNLOAD )
			{
				UpdateFileState(Transfer,etfsERROR);

				while ( SetNextFile(Transfer) )
				{
					if ( Transfer->GetMedium() == eltCLIENTVERSION )
					{
						UpdateFileState(Transfer,etfsNONE);
					}
					else
					{
						break;
					}
				}
			}
			break;
		}

		case DC_MESSAGE_LOCK:
		{
			CMessageLock * msg = (CMessageLock*)DCMsg;

			CConnectionManager::Instance()->SetUserTransferInfo( Transfer->GetHubName(), Transfer->GetHubHost(), Transfer->GetDstNick(), msg );
			
			CString s = "Client: ";
			
			switch(msg->m_eClientVersion)
			{
				case eucvDCPP:
					s += "DC++";
					break;
				case eucvDCGUI:
					s += "Valknut"; // really dclib...
					break;
				case eucvMICRODC:
					s += "microdc";
					break;
				case eucvSHAKESPEER:
					s += "ShakesPeer";
					break;
				default:
					s += "Unknown";
					break;
			}

			if ( msg->m_sVersionString.NotEmpty() )
			{
				s += " (";
				s += msg->m_sVersionString;
				s += ')';
			}

			SendLogInfo( s, Transfer );

			break;
		}

		case DC_MESSAGE_SUPPORTS:
		{
			CMessageSupports * msg = (CMessageSupports*)DCMsg;
			
			CConnectionManager::Instance()->SetUserTransferInfo( Transfer->GetHubName(), Transfer->GetHubHost(), Transfer->GetDstNick(), msg );
			
			break;
		}
		
		case DC_MESSAGE_LOG:
		{
			SendLogInfo( ((CMessageLog*)DCMsg)->sMessage, Transfer );
			break;
		}
					
		default:
		{
			DPRINTF("dctransfer unknown message: %d\n",DCMsg->m_eType);
			break;
		}
	}

	delete DCMsg;

	TransferCallBackMutex.UnLock();

	return 0;
}
