/** 
 * @file lltransfermanager.cpp
 * @brief Improved transfer mechanism for moving data through the
 * message system.
 *
 * $LicenseInfo:firstyear=2004&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * 
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#include "linden_common.h"

#include "lltransfermanager.h"

#include "llerror.h"
#include "message.h"
#include "lldatapacker.h"

#include "lltransfersourcefile.h"
#include "lltransfersourceasset.h"
#include "lltransfertargetfile.h"
#include "lltransfertargetvfile.h"

const S32 MAX_PACKET_DATA_SIZE = 2048;
const S32 MAX_PARAMS_SIZE = 1024;

LLTransferManager gTransferManager;
LLTransferSource::stype_scfunc_map LLTransferSource::sSourceCreateMap;

//
// LLTransferManager implementation
//

LLTransferManager::LLTransferManager() :
	mValid(FALSE)
{
	S32 i;
	for (i = 0; i < LLTTT_NUM_TYPES; i++)
	{
		mTransferBitsIn[i] = 0;
		mTransferBitsOut[i] = 0;
	}
}


LLTransferManager::~LLTransferManager()
{
	if (mValid)
	{
		LL_WARNS() << "LLTransferManager::~LLTransferManager - Should have been cleaned up by message system shutdown process" << LL_ENDL;
		cleanup();
	}
}


void LLTransferManager::init()
{
	if (mValid)
	{
		LL_ERRS() << "Double initializing LLTransferManager!" << LL_ENDL;
	}
	mValid = TRUE;

	// Register message system handlers
	gMessageSystem->setHandlerFunc("TransferRequest", processTransferRequest, NULL);
	gMessageSystem->setHandlerFunc("TransferInfo", processTransferInfo, NULL);
	gMessageSystem->setHandlerFunc("TransferPacket", processTransferPacket, NULL);
	gMessageSystem->setHandlerFunc("TransferAbort", processTransferAbort, NULL);
}


void LLTransferManager::cleanup()
{
	mValid = FALSE;

	host_tc_map::iterator iter;
	for (iter = mTransferConnections.begin(); iter != mTransferConnections.end(); iter++)
	{
		delete iter->second;
	}
	mTransferConnections.clear();
}


void LLTransferManager::updateTransfers()
{
	host_tc_map::iterator iter,cur;

	iter = mTransferConnections.begin();

	while (iter !=mTransferConnections.end())
	{
		cur = iter;
		iter++;
		cur->second->updateTransfers();
	}
}


void LLTransferManager::cleanupConnection(const LLHost &host)
{
	host_tc_map::iterator iter;
	iter = mTransferConnections.find(host);
	if (iter == mTransferConnections.end())
	{
		// This can happen legitimately if we've never done a transfer, and we're
		// cleaning up a circuit.
		//LL_WARNS() << "Cleaning up nonexistent transfer connection to " << host << LL_ENDL;
		return;
	}
	LLTransferConnection *connp = iter->second;
	delete connp;
	mTransferConnections.erase(iter);
}


LLTransferConnection *LLTransferManager::getTransferConnection(const LLHost &host)
{
	host_tc_map::iterator iter;
	iter = mTransferConnections.find(host);
	if (iter == mTransferConnections.end())
	{
		mTransferConnections[host] = new LLTransferConnection(host);
		return mTransferConnections[host];
	}

	return iter->second;
}


LLTransferSourceChannel *LLTransferManager::getSourceChannel(const LLHost &host, const LLTransferChannelType type)
{
	LLTransferConnection *tcp = getTransferConnection(host);
	if (!tcp)
	{
		return NULL;
	}
	return tcp->getSourceChannel(type);
}



LLTransferTargetChannel *LLTransferManager::getTargetChannel(const LLHost &host, const LLTransferChannelType type)
{
	LLTransferConnection *tcp = getTransferConnection(host);
	if (!tcp)
	{
		return NULL;
	}
	return tcp->getTargetChannel(type);
}

// virtual
LLTransferSourceParams::~LLTransferSourceParams()
{ }


LLTransferSource *LLTransferManager::findTransferSource(const LLUUID &transfer_id)
{
	// This linear traversal could screw us later if we do lots of
	// searches for sources.  However, this ONLY happens right now
	// in asset transfer callbacks, so this should be relatively quick.
	host_tc_map::iterator iter;
	for (iter = mTransferConnections.begin(); iter != mTransferConnections.end(); iter++)
	{
		LLTransferConnection *tcp = iter->second;
		LLTransferConnection::tsc_iter sc_iter;
		for (sc_iter = tcp->mTransferSourceChannels.begin(); sc_iter != tcp->mTransferSourceChannels.end(); sc_iter++)
		{
			LLTransferSourceChannel *scp = *sc_iter;
			LLTransferSource *sourcep = scp->findTransferSource(transfer_id);
			if (sourcep)
			{
				return sourcep;
			}
		}
	}

	return NULL;
}

//
// Message handlers
//

//static
void LLTransferManager::processTransferRequest(LLMessageSystem *msgp, void **)
{
	//LL_INFOS() << "LLTransferManager::processTransferRequest" << LL_ENDL;

	LLUUID transfer_id;
	LLTransferSourceType source_type;
	LLTransferChannelType channel_type;
	F32 priority;

	msgp->getUUID("TransferInfo", "TransferID", transfer_id);
	msgp->getS32("TransferInfo", "SourceType", (S32 &)source_type);
	msgp->getS32("TransferInfo", "ChannelType", (S32 &)channel_type);
	msgp->getF32("TransferInfo", "Priority", priority);

	LLTransferSourceChannel *tscp = gTransferManager.getSourceChannel(msgp->getSender(), channel_type);

	if (!tscp)
	{
		LL_WARNS() << "Source channel not found" << LL_ENDL;
		return;
	}

	if (tscp->findTransferSource(transfer_id))
	{
		LL_WARNS() << "Duplicate request for transfer " << transfer_id << ", aborting!" << LL_ENDL;
		return;
	}

	S32 size = msgp->getSize("TransferInfo", "Params");
	if(size > MAX_PARAMS_SIZE)
	{
		LL_WARNS() << "LLTransferManager::processTransferRequest params too big."
			<< LL_ENDL;
		return;
	}

	//LL_INFOS() << transfer_id << ":" << source_type << ":" << channel_type << ":" << priority << LL_ENDL;
	LLTransferSource* tsp = LLTransferSource::createSource(
		source_type,
		transfer_id,
		priority);
	if(!tsp)
	{
		LL_WARNS() << "LLTransferManager::processTransferRequest couldn't create"
			<< " transfer source!" << LL_ENDL;
		return;
	}
	U8 tmp[MAX_PARAMS_SIZE];
	msgp->getBinaryData("TransferInfo", "Params", tmp, size);

	LLDataPackerBinaryBuffer dpb(tmp, MAX_PARAMS_SIZE);
	BOOL unpack_ok = tsp->unpackParams(dpb);
	if (!unpack_ok)
	{
		// This should only happen if the data is corrupt or
		// incorrectly packed.
		// *NOTE: We may want to call abortTransfer().
		LL_WARNS() << "LLTransferManager::processTransferRequest: bad parameters."
			<< LL_ENDL;
		delete tsp;
		return;
	}

	tscp->addTransferSource(tsp);
	tsp->initTransfer();
}


//static
void LLTransferManager::processTransferInfo(LLMessageSystem *msgp, void **)
{
	//LL_INFOS() << "LLTransferManager::processTransferInfo" << LL_ENDL;

	LLUUID transfer_id;
	LLTransferTargetType target_type;
	LLTransferChannelType channel_type;
	LLTSCode status;
	S32 size;

	msgp->getUUID("TransferInfo", "TransferID", transfer_id);
	msgp->getS32("TransferInfo", "TargetType", (S32 &)target_type);
	msgp->getS32("TransferInfo", "ChannelType", (S32 &)channel_type);
	msgp->getS32("TransferInfo", "Status", (S32 &)status);
	msgp->getS32("TransferInfo", "Size", size);

	//LL_INFOS() << transfer_id << ":" << target_type<< ":" << channel_type << LL_ENDL;
	LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(msgp->getSender(), channel_type);
	if (!ttcp)
	{
		LL_WARNS() << "Target channel not found" << LL_ENDL;
		// Should send a message to abort the transfer.
		return;
	}

	LLTransferTarget *ttp = ttcp->findTransferTarget(transfer_id);
	if (!ttp)
	{
		LL_WARNS() << "TransferInfo for unknown transfer!  Not able to handle this yet!" << LL_ENDL;
		// This could happen if we're doing a push transfer, although to avoid confusion,
		// maybe it should be a different message.
		return;
	}

	if (status != LLTS_OK)
	{
		LL_WARNS() << transfer_id << ": Non-ok status, cleaning up" << LL_ENDL;
		ttp->completionCallback(status);
		// Clean up the transfer.
		ttcp->deleteTransfer(ttp);
		return;
	}

	// unpack the params
	S32 params_size = msgp->getSize("TransferInfo", "Params");
	if(params_size > MAX_PARAMS_SIZE)
	{
		LL_WARNS() << "LLTransferManager::processTransferInfo params too big."
			<< LL_ENDL;
		return;
	}
	else if(params_size > 0)
	{
		U8 tmp[MAX_PARAMS_SIZE];
		msgp->getBinaryData("TransferInfo", "Params", tmp, params_size);
		LLDataPackerBinaryBuffer dpb(tmp, MAX_PARAMS_SIZE);
		if (!ttp->unpackParams(dpb))
		{
			// This should only happen if the data is corrupt or
			// incorrectly packed.
			LL_WARNS() << "LLTransferManager::processTransferRequest: bad params."
				<< LL_ENDL;
			ttp->abortTransfer();
			ttcp->deleteTransfer(ttp);
			return;
		}
	}

	//LL_INFOS() << "Receiving " << transfer_id << ", size " << size << " bytes" << LL_ENDL;
	ttp->setSize(size);
	ttp->setGotInfo(TRUE);

	// OK, at this point we to handle any delayed transfer packets (which could happen
	// if this packet was lost)

	// This is a lame cut and paste of code down below.  If we change the logic down there,
	// we HAVE to change the logic up here.

	while (1)
	{
		S32 packet_id = 0;
		U8 tmp_data[MAX_PACKET_DATA_SIZE];
		// See if we've got any delayed packets
		packet_id = ttp->getNextPacketID();
		if (ttp->mDelayedPacketMap.find(packet_id) != ttp->mDelayedPacketMap.end())
		{
			// Perhaps this stuff should be inside a method in LLTransferPacket?
			// I'm too lazy to do it now, though.
// 			LL_INFOS() << "Playing back delayed packet " << packet_id << LL_ENDL;
			LLTransferPacket *packetp = ttp->mDelayedPacketMap[packet_id];

			// This is somewhat inefficient, but avoids us having to duplicate
			// code between the off-the-wire and delayed paths.
			packet_id = packetp->mPacketID;
			size = packetp->mSize;
			if (size)
			{
				if ((packetp->mDatap != NULL) && (size<(S32)sizeof(tmp_data)))
				{
					memcpy(tmp_data, packetp->mDatap, size);	/*Flawfinder: ignore*/
				}
			}
			status = packetp->mStatus;
			ttp->mDelayedPacketMap.erase(packet_id);
			delete packetp;
		}
		else
		{
			// No matching delayed packet, we're done.
			break;
		}

		LLTSCode ret_code = ttp->dataCallback(packet_id, tmp_data, size);
		if (ret_code == LLTS_OK)
		{
			ttp->setLastPacketID(packet_id);
		}

		if (status != LLTS_OK)
		{
			if (status != LLTS_DONE)
			{
				LL_WARNS() << "LLTransferManager::processTransferInfo Error in playback!" << LL_ENDL;
			}
			else
			{
				LL_DEBUGS() << "LLTransferManager::processTransferInfo replay FINISHED for " << transfer_id << LL_ENDL;
			}
			// This transfer is done, either via error or not.
			ttp->completionCallback(status);
			ttcp->deleteTransfer(ttp);
			return;
		}
	}
}


//static
void LLTransferManager::processTransferPacket(LLMessageSystem *msgp, void **)
{
	//LL_INFOS() << "LLTransferManager::processTransferPacket" << LL_ENDL;

	LLUUID transfer_id;
	LLTransferChannelType channel_type;
	S32 packet_id;
	LLTSCode status;
	S32 size;
	msgp->getUUID("TransferData", "TransferID", transfer_id);
	msgp->getS32("TransferData", "ChannelType", (S32 &)channel_type);
	msgp->getS32("TransferData", "Packet", packet_id);
	msgp->getS32("TransferData", "Status", (S32 &)status);

	// Find the transfer associated with this packet.
	//LL_INFOS() << transfer_id << ":" << channel_type << LL_ENDL;
	LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(msgp->getSender(), channel_type);
	if (!ttcp)
	{
		LL_WARNS() << "Target channel not found" << LL_ENDL;
		return;
	}

	LLTransferTarget *ttp = ttcp->findTransferTarget(transfer_id);
	if (!ttp)
	{
		LL_WARNS() << "Didn't find matching transfer for " << transfer_id
			<< " processing packet " << packet_id
			<< " from " << msgp->getSender() << LL_ENDL;
		return;
	}

	size = msgp->getSize("TransferData", "Data");

	S32 msg_bytes = 0;
	if (msgp->getReceiveCompressedSize())
	{
		msg_bytes = msgp->getReceiveCompressedSize();
	}
	else
	{
		msg_bytes = msgp->getReceiveSize();
	}
	gTransferManager.addTransferBitsIn(ttcp->mChannelType, msg_bytes*8);

	if ((size < 0) || (size > MAX_PACKET_DATA_SIZE))
	{
		LL_WARNS() << "Invalid transfer packet size " << size << LL_ENDL;
		return;
	}

	U8 tmp_data[MAX_PACKET_DATA_SIZE];
	if (size > 0)
	{
		// Only pull the data out if the size is > 0
		msgp->getBinaryData("TransferData", "Data", tmp_data, size);
	}

	if ((!ttp->gotInfo()) || (ttp->getNextPacketID() != packet_id))
	{
		// Put this on a list of packets to be delivered later.
		if(!ttp->addDelayedPacket(packet_id, status, tmp_data, size))
		{
			// Whoops - failed to add a delayed packet for some reason.
			LL_WARNS() << "Too many delayed packets processing transfer "
				<< transfer_id << " from " << msgp->getSender() << LL_ENDL;
			ttp->abortTransfer();
			ttcp->deleteTransfer(ttp);
			return;
		}
#if 0
		// Spammy!
		const S32 LL_TRANSFER_WARN_GAP = 10;
		if(!ttp->gotInfo())
		{
			LL_WARNS() << "Got data packet before information in transfer "
				<< transfer_id << " from " << msgp->getSender()
				<< ", got " << packet_id << LL_ENDL;
		}
		else if((packet_id - ttp->getNextPacketID()) > LL_TRANSFER_WARN_GAP)
		{
			LL_WARNS() << "Out of order packet in transfer " << transfer_id
				<< " from " << msgp->getSender() << ", got " << packet_id
				<< " expecting " << ttp->getNextPacketID() << LL_ENDL;
		}
#endif
		return;
	}

	// Loop through this until we're done with all delayed packets
	
	//
	// NOTE: THERE IS A CUT AND PASTE OF THIS CODE IN THE TRANSFERINFO HANDLER
	// SO WE CAN PLAY BACK DELAYED PACKETS THERE!!!!!!!!!!!!!!!!!!!!!!!!!
	//
	BOOL done = FALSE;
	while (!done)
	{
		LLTSCode ret_code = ttp->dataCallback(packet_id, tmp_data, size);
		if (ret_code == LLTS_OK)
		{
			ttp->setLastPacketID(packet_id);
		}

		if (status != LLTS_OK)
		{
			if (status != LLTS_DONE)
			{
				LL_WARNS() << "LLTransferManager::processTransferPacket Error in transfer!" << LL_ENDL;
			}
			else
			{
// 				LL_INFOS() << "LLTransferManager::processTransferPacket done for " << transfer_id << LL_ENDL;
			}
			// This transfer is done, either via error or not.
			ttp->completionCallback(status);
			ttcp->deleteTransfer(ttp);
			return;
		}

		// See if we've got any delayed packets
		packet_id = ttp->getNextPacketID();
		if (ttp->mDelayedPacketMap.find(packet_id) != ttp->mDelayedPacketMap.end())
		{
			// Perhaps this stuff should be inside a method in LLTransferPacket?
			// I'm too lazy to do it now, though.
// 			LL_INFOS() << "Playing back delayed packet " << packet_id << LL_ENDL;
			LLTransferPacket *packetp = ttp->mDelayedPacketMap[packet_id];

			// This is somewhat inefficient, but avoids us having to duplicate
			// code between the off-the-wire and delayed paths.
			packet_id = packetp->mPacketID;
			size = packetp->mSize;
			if (size)
			{
				if ((packetp->mDatap != NULL) && (size<(S32)sizeof(tmp_data)))
				{
					memcpy(tmp_data, packetp->mDatap, size);	/*Flawfinder: ignore*/
				}
			}
			status = packetp->mStatus;
			ttp->mDelayedPacketMap.erase(packet_id);
			delete packetp;
		}
		else
		{
			// No matching delayed packet, abort it.
			done = TRUE;
		}
	}
}


//static
void LLTransferManager::processTransferAbort(LLMessageSystem *msgp, void **)
{
	//LL_INFOS() << "LLTransferManager::processTransferPacket" << LL_ENDL;

	LLUUID transfer_id;
	LLTransferChannelType channel_type;
	msgp->getUUID("TransferInfo", "TransferID", transfer_id);
	msgp->getS32("TransferInfo", "ChannelType", (S32 &)channel_type);

	// See if it's a target that we're trying to abort
	// Find the transfer associated with this packet.
	LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(msgp->getSender(), channel_type);
	if (ttcp)
	{
		LLTransferTarget *ttp = ttcp->findTransferTarget(transfer_id);
		if (ttp)
		{
			ttp->abortTransfer();
			ttcp->deleteTransfer(ttp);
			return;
		}
	}

	// Hmm, not a target.  Maybe it's a source.
	LLTransferSourceChannel *tscp = gTransferManager.getSourceChannel(msgp->getSender(), channel_type);
	if (tscp)
	{
		LLTransferSource *tsp = tscp->findTransferSource(transfer_id);
		if (tsp)
		{
			tsp->abortTransfer();
			tscp->deleteTransfer(tsp);
			return;
		}
	}

	LL_WARNS() << "Couldn't find transfer " << transfer_id << " to abort!" << LL_ENDL;
}


//static
void LLTransferManager::reliablePacketCallback(void **user_data, S32 result)
{
	LLUUID *transfer_idp = (LLUUID *)user_data;
	if (result &&
		transfer_idp != NULL)
	{
		LLTransferSource *tsp = gTransferManager.findTransferSource(*transfer_idp);
		if (tsp)
		{
			LL_WARNS() << "Aborting reliable transfer " << *transfer_idp << " due to failed reliable resends!" << LL_ENDL;
			LLTransferSourceChannel *tscp = tsp->mChannelp;
			tsp->abortTransfer();
			tscp->deleteTransfer(tsp);
		}
		else
		{
			LL_WARNS() << "Aborting reliable transfer " << *transfer_idp << " but can't find the LLTransferSource object" << LL_ENDL;
		}
	}
	delete transfer_idp;
}

//
// LLTransferConnection implementation
//

LLTransferConnection::LLTransferConnection(const LLHost &host)
{
	mHost = host;
}

LLTransferConnection::~LLTransferConnection()
{
	tsc_iter itersc;
	for (itersc = mTransferSourceChannels.begin(); itersc != mTransferSourceChannels.end(); itersc++)
	{
		delete *itersc;
	}
	mTransferSourceChannels.clear();

	ttc_iter itertc;
	for (itertc = mTransferTargetChannels.begin(); itertc != mTransferTargetChannels.end(); itertc++)
	{
		delete *itertc;
	}
	mTransferTargetChannels.clear();
}


void LLTransferConnection::updateTransfers()
{
	// Do stuff for source transfers (basically, send data out).
	tsc_iter iter, cur;
	iter = mTransferSourceChannels.begin();

	while (iter !=mTransferSourceChannels.end())
	{
		cur = iter;
		iter++;
		(*cur)->updateTransfers();
	}

	// Do stuff for target transfers
	// Primarily, we should be aborting transfers that are irredeemably broken
	// (large packet gaps that don't appear to be getting filled in, most likely)
	// Probably should NOT be doing timeouts for other things, as new priority scheme
	// means that a high priority transfer COULD block a transfer for a long time.
}


LLTransferSourceChannel *LLTransferConnection::getSourceChannel(const LLTransferChannelType channel_type)
{
	tsc_iter iter;
	for (iter = mTransferSourceChannels.begin(); iter != mTransferSourceChannels.end(); iter++)
	{
		if ((*iter)->getChannelType() == channel_type)
		{
			return *iter;
		}
	}

	LLTransferSourceChannel *tscp = new LLTransferSourceChannel(channel_type, mHost);
	mTransferSourceChannels.push_back(tscp);
	return tscp;
}


LLTransferTargetChannel *LLTransferConnection::getTargetChannel(const LLTransferChannelType channel_type)
{
	ttc_iter iter;
	for (iter = mTransferTargetChannels.begin(); iter != mTransferTargetChannels.end(); iter++)
	{
		if ((*iter)->getChannelType() == channel_type)
		{
			return *iter;
		}
	}

	LLTransferTargetChannel *ttcp = new LLTransferTargetChannel(channel_type, mHost);
	mTransferTargetChannels.push_back(ttcp);
	return ttcp;
}


//
// LLTransferSourceChannel implementation
//

const S32 DEFAULT_PACKET_SIZE = 1000;


LLTransferSourceChannel::LLTransferSourceChannel(const LLTransferChannelType channel_type, const LLHost &host) :
	mChannelType(channel_type),
	mHost(host),
	mTransferSources(LLTransferSource::sSetPriority, LLTransferSource::sGetPriority),
	mThrottleID(TC_ASSET)
{
}


LLTransferSourceChannel::~LLTransferSourceChannel()
{
	LLPriQueueMap<LLTransferSource*>::pqm_iter iter =
		mTransferSources.mMap.begin();
	LLPriQueueMap<LLTransferSource*>::pqm_iter end =
		mTransferSources.mMap.end();
	for (; iter != end; ++iter)
	{
		// Just kill off all of the transfers
		(*iter).second->abortTransfer();
		delete iter->second;
	}
	mTransferSources.mMap.clear();
}

void LLTransferSourceChannel::updatePriority(LLTransferSource *tsp, const F32 priority)
{
	mTransferSources.reprioritize(priority, tsp);
}

void LLTransferSourceChannel::updateTransfers()
{
	// Actually, this should do the following:
	// Decide if we can actually send data.
	// If so, update priorities so we know who gets to send it.
	// Send data from the sources, while updating until we've sent our throttle allocation.

	LLCircuitData *cdp = gMessageSystem->mCircuitInfo.findCircuit(getHost());
	if (!cdp)
	{
		return;
	}

	if (cdp->isBlocked())
	{
		// *NOTE: We need to make sure that the throttle bits
		// available gets reset.

		// We DON'T want to send any packets if they're blocked, they'll just end up
		// piling up on the other end.
		//LL_WARNS() << "Blocking transfers due to blocked circuit for " << getHost() << LL_ENDL;
		return;
	}

	const S32 throttle_id = mThrottleID;

	LLThrottleGroup &tg = cdp->getThrottleGroup();

	if (tg.checkOverflow(throttle_id, 0.f))
	{
		return;
	}

	LLPriQueueMap<LLTransferSource *>::pqm_iter iter, next;

	BOOL done = FALSE;
	for (iter = mTransferSources.mMap.begin(); (iter != mTransferSources.mMap.end()) && !done;)
	{
		//LL_INFOS() << "LLTransferSourceChannel::updateTransfers()" << LL_ENDL;
		// Do stuff. 
		next = iter;
		next++;

		LLTransferSource *tsp = iter->second;
		U8 *datap = NULL;
		S32 data_size = 0;
		BOOL delete_data = FALSE;
		S32 packet_id = 0;
		S32 sent_bytes = 0;
		LLTSCode status = LLTS_OK;

		// Get the packetID for the next packet that we're transferring.
		packet_id = tsp->getNextPacketID();
		status = tsp->dataCallback(packet_id, DEFAULT_PACKET_SIZE, &datap, data_size, delete_data);

		if (status == LLTS_SKIP)
		{
			// We don't have any data, but we're not done, just go on.
			// This will presumably be used for streaming or async transfers that
			// are stalled waiting for data from another source.
			iter=next;
			continue;
		}

		LLUUID *cb_uuid = new LLUUID(tsp->getID());
		LLUUID transaction_id = tsp->getID();

		// Send the data now, even if it's an error.
		// The status code will tell the other end what to do.
		gMessageSystem->newMessage("TransferPacket");
		gMessageSystem->nextBlock("TransferData");
		gMessageSystem->addUUID("TransferID", tsp->getID());
		gMessageSystem->addS32("ChannelType", getChannelType());
		gMessageSystem->addS32("Packet", packet_id);	// HACK!  Need to put in a REAL packet id
		gMessageSystem->addS32("Status", status);
		gMessageSystem->addBinaryData("Data", datap, data_size);
		sent_bytes = gMessageSystem->getCurrentSendTotal();
		gMessageSystem->sendReliable(getHost(), LL_DEFAULT_RELIABLE_RETRIES, TRUE, F32Seconds(0.f),
									 LLTransferManager::reliablePacketCallback, (void**)cb_uuid);

		// Do bookkeeping for the throttle
		done = tg.throttleOverflow(throttle_id, sent_bytes*8.f);
		gTransferManager.addTransferBitsOut(mChannelType, sent_bytes*8);

		// Clean up our temporary data.
		if (delete_data)
		{
			delete[] datap;
			datap = NULL;
		}

		if (findTransferSource(transaction_id) == NULL)
		{
			//Warning!  In the case of an aborted transfer, the sendReliable call above calls 
			//AbortTransfer which in turn calls deleteTransfer which means that somewhere way 
			//down the chain our current iter can get invalidated resulting in an infrequent
			//sim crash.  This check gets us to a valid transfer source in this event.
			iter=next;
			continue;
		}

		// Update the packet counter
		tsp->setLastPacketID(packet_id);

		switch (status)
		{
		case LLTS_OK:
			// We're OK, don't need to do anything.  Keep sending data.
			break;
		case LLTS_ERROR:
			LL_WARNS() << "Error in transfer dataCallback!" << LL_ENDL;
			// fall through
		case LLTS_DONE:
			// We need to clean up this transfer source.
			//LL_INFOS() << "LLTransferSourceChannel::updateTransfers() " << tsp->getID() << " done" << LL_ENDL;
			tsp->completionCallback(status);
			delete tsp;
			
			mTransferSources.mMap.erase(iter);
			iter = next;
			break;
		default:
			LL_ERRS() << "Unknown transfer error code!" << LL_ENDL;
		}

		// At this point, we should do priority adjustment (since some transfers like
		// streaming transfers will adjust priority based on how much they've sent and time,
		// but I'm not going to bother yet. - djs.
	}
}


void LLTransferSourceChannel::addTransferSource(LLTransferSource *sourcep)
{
	sourcep->mChannelp = this;
	mTransferSources.push(sourcep->getPriority(), sourcep);
}


LLTransferSource *LLTransferSourceChannel::findTransferSource(const LLUUID &transfer_id)
{
	LLPriQueueMap<LLTransferSource *>::pqm_iter iter;
	for (iter = mTransferSources.mMap.begin(); iter != mTransferSources.mMap.end(); iter++)
	{
		LLTransferSource *tsp = iter->second;
		if (tsp->getID() == transfer_id)
		{
			return tsp;
		}
	}
	return NULL;
}


void LLTransferSourceChannel::deleteTransfer(LLTransferSource *tsp)
{
	if (tsp)
	{
		LLPriQueueMap<LLTransferSource *>::pqm_iter iter;
		for (iter = mTransferSources.mMap.begin(); iter != mTransferSources.mMap.end(); iter++)
		{
			if (iter->second == tsp)
			{
				delete tsp;
				mTransferSources.mMap.erase(iter);
				return;
			}
		}

		LL_WARNS() << "Unable to find transfer source id " 
			<< tsp->getID()
			<< " to delete!" 
			<< LL_ENDL;
	}
}


//
// LLTransferTargetChannel implementation
//

LLTransferTargetChannel::LLTransferTargetChannel(const LLTransferChannelType channel_type, const LLHost &host) :
	mChannelType(channel_type),
	mHost(host)
{
}

LLTransferTargetChannel::~LLTransferTargetChannel()
{
	tt_iter iter;
	for (iter = mTransferTargets.begin(); iter != mTransferTargets.end(); iter++)
	{
		// Abort all of the current transfers
		(*iter)->abortTransfer();
		delete *iter;
	}
	mTransferTargets.clear();
}


void LLTransferTargetChannel::requestTransfer(
	const LLTransferSourceParams& source_params,
	const LLTransferTargetParams& target_params,
	const F32 priority)
{
	LLUUID id;
	id.generate();
	LLTransferTarget* ttp = LLTransferTarget::createTarget(
		target_params.getType(),
		id,
		source_params.getType());
	if (!ttp)
	{
		LL_WARNS() << "LLTransferManager::requestTransfer aborting due to target creation failure!" << LL_ENDL;
		return;
	}

	ttp->applyParams(target_params);
	addTransferTarget(ttp);

	sendTransferRequest(ttp, source_params, priority);
}


void LLTransferTargetChannel::sendTransferRequest(LLTransferTarget *targetp,
												  const LLTransferSourceParams &params,
												  const F32 priority)
{
	//
	// Pack the message with data which explains how to get the source, and
	// send it off to the source for this channel.
	//
	llassert(targetp);
	llassert(targetp->getChannel() == this);

	gMessageSystem->newMessage("TransferRequest");
	gMessageSystem->nextBlock("TransferInfo");
	gMessageSystem->addUUID("TransferID", targetp->getID());
	gMessageSystem->addS32("SourceType", params.getType());
	gMessageSystem->addS32("ChannelType", getChannelType());
	gMessageSystem->addF32("Priority", priority);

	U8 tmp[MAX_PARAMS_SIZE];
	LLDataPackerBinaryBuffer dp(tmp, MAX_PARAMS_SIZE);
	params.packParams(dp);
	S32 len = dp.getCurrentSize();
	gMessageSystem->addBinaryData("Params", tmp, len);

	gMessageSystem->sendReliable(mHost);
}


void LLTransferTargetChannel::addTransferTarget(LLTransferTarget *targetp)
{
	targetp->mChannelp = this;
	mTransferTargets.push_back(targetp);
}


LLTransferTarget *LLTransferTargetChannel::findTransferTarget(const LLUUID &transfer_id)
{
	tt_iter iter;
	for (iter = mTransferTargets.begin(); iter != mTransferTargets.end(); iter++)
	{
		LLTransferTarget *ttp = *iter;
		if (ttp->getID() == transfer_id)
		{
			return ttp;
		}
	}
	return NULL;
}


void LLTransferTargetChannel::deleteTransfer(LLTransferTarget *ttp)
{
	if (ttp)
	{
		tt_iter iter;
		for (iter = mTransferTargets.begin(); iter != mTransferTargets.end(); iter++)
		{
			if (*iter == ttp)
			{
				delete ttp;
				mTransferTargets.erase(iter);
				return;
			}
		}

		LL_WARNS() << "Unable to find transfer target id " 
			<< ttp->getID()
			<< " to delete!" 
			<< LL_ENDL;
	}
}


//
// LLTransferSource implementation
//

LLTransferSource::LLTransferSource(const LLTransferSourceType type,
								   const LLUUID &transfer_id,
								   const F32 priority) :
	mType(type),
	mID(transfer_id),
	mChannelp(NULL),
	mPriority(priority),
	mSize(0),
	mLastPacketID(-1)
{
	setPriority(priority);
}


LLTransferSource::~LLTransferSource()
{
	// No actual cleanup of the transfer is done here, this is purely for
	// memory cleanup.  The completionCallback is guaranteed to get called
	// before this happens.
}


void LLTransferSource::sendTransferStatus(LLTSCode status)
{
	gMessageSystem->newMessage("TransferInfo");
	gMessageSystem->nextBlock("TransferInfo");
	gMessageSystem->addUUID("TransferID", getID());
	gMessageSystem->addS32("TargetType", LLTTT_UNKNOWN);
	gMessageSystem->addS32("ChannelType", mChannelp->getChannelType());
	gMessageSystem->addS32("Status", status);
	gMessageSystem->addS32("Size", mSize);
	U8 tmp[MAX_PARAMS_SIZE];
	LLDataPackerBinaryBuffer dp(tmp, MAX_PARAMS_SIZE);
	packParams(dp);
	S32 len = dp.getCurrentSize();
	gMessageSystem->addBinaryData("Params", tmp, len);
	gMessageSystem->sendReliable(mChannelp->getHost());

	// Abort if there was as asset system issue.
	if (status != LLTS_OK)
	{
		completionCallback(status);
		mChannelp->deleteTransfer(this);
	}
}


// This should never be called directly, the transfer manager is responsible for
// aborting the transfer from the channel.  I might want to rethink this in the
// future, though.
void LLTransferSource::abortTransfer()
{
	// Send a message down, call the completion callback
	LL_INFOS() << "LLTransferSource::Aborting transfer " << getID() << " to " << mChannelp->getHost() << LL_ENDL;
	gMessageSystem->newMessage("TransferAbort");
	gMessageSystem->nextBlock("TransferInfo");
	gMessageSystem->addUUID("TransferID", getID());
	gMessageSystem->addS32("ChannelType", mChannelp->getChannelType());
	gMessageSystem->sendReliable(mChannelp->getHost());

	completionCallback(LLTS_ABORT);
}


//static
void LLTransferSource::registerSourceType(const LLTransferSourceType stype, LLTransferSourceCreateFunc func)
{
	if (sSourceCreateMap.count(stype))
	{
		// Disallow changing what class handles a source type
		// Unclear when you would want to do this, and whether it would work.
		LL_ERRS() << "Reregistering source type " << stype << LL_ENDL;
	}
	else
	{
		sSourceCreateMap[stype] = func;
	}
}

//static
LLTransferSource *LLTransferSource::createSource(const LLTransferSourceType stype,
												 const LLUUID &id,
												 const F32 priority)
{
	switch (stype)
	{
	// *NOTE: The source file transfer mechanism is highly insecure and could
	// lead to easy exploitation of a server process.
	// I have removed all uses of it from the codebase. Phoenix.
	// 
	//case LLTST_FILE:
	//	return new LLTransferSourceFile(id, priority);
	case LLTST_ASSET:
		return new LLTransferSourceAsset(id, priority);
	default:
		{
			if (!sSourceCreateMap.count(stype))
			{
				// Use the callback to create the source type if it's not there.
				LL_WARNS() << "Unknown transfer source type: " << stype << LL_ENDL;
				return NULL;
			}
			return (sSourceCreateMap[stype])(id, priority);
		}
	}
}


// static
void LLTransferSource::sSetPriority(LLTransferSource *&tsp, const F32 priority)
{
	tsp->setPriority(priority);
}


// static
F32 LLTransferSource::sGetPriority(LLTransferSource *&tsp)
{
	return tsp->getPriority();
}


//
// LLTransferPacket implementation
//

LLTransferPacket::LLTransferPacket(const S32 packet_id, const LLTSCode status, const U8 *datap, const S32 size) :
	mPacketID(packet_id),
	mStatus(status),
	mDatap(NULL),
	mSize(size)
{
	if (size == 0)
	{
		return;
	}
	
	mDatap = new U8[size];
	if (mDatap != NULL)
	{
		memcpy(mDatap, datap, size);	/*Flawfinder: ignore*/
	}
}

LLTransferPacket::~LLTransferPacket()
{
	delete[] mDatap;
}

//
// LLTransferTarget implementation
//

LLTransferTarget::LLTransferTarget(
	LLTransferTargetType type,
	const LLUUID& transfer_id,
	LLTransferSourceType source_type) : 
	mType(type),
	mSourceType(source_type),
	mID(transfer_id),
	mChannelp(NULL),
	mGotInfo(FALSE),
	mSize(0),
	mLastPacketID(-1)
{
}

LLTransferTarget::~LLTransferTarget()
{
	// No actual cleanup of the transfer is done here, this is purely for
	// memory cleanup.  The completionCallback is guaranteed to get called
	// before this happens.
	tpm_iter iter;
	for (iter = mDelayedPacketMap.begin(); iter != mDelayedPacketMap.end(); iter++)
	{
		delete iter->second;
	}
	mDelayedPacketMap.clear();
}

// This should never be called directly, the transfer manager is responsible for
// aborting the transfer from the channel.  I might want to rethink this in the
// future, though.
void LLTransferTarget::abortTransfer()
{
	// Send a message up, call the completion callback
	LL_INFOS() << "LLTransferTarget::Aborting transfer " << getID() << " from " << mChannelp->getHost() << LL_ENDL;
	gMessageSystem->newMessage("TransferAbort");
	gMessageSystem->nextBlock("TransferInfo");
	gMessageSystem->addUUID("TransferID", getID());
	gMessageSystem->addS32("ChannelType", mChannelp->getChannelType());
	gMessageSystem->sendReliable(mChannelp->getHost());

	completionCallback(LLTS_ABORT);
}

bool LLTransferTarget::addDelayedPacket(
	const S32 packet_id,
	const LLTSCode status,
	U8* datap,
	const S32 size)
{
	const transfer_packet_map::size_type LL_MAX_DELAYED_PACKETS = 100;
	if(mDelayedPacketMap.size() > LL_MAX_DELAYED_PACKETS)
	{
		// too many delayed packets
		return false;
	}

	LLTransferPacket* tpp = new LLTransferPacket(
		packet_id,
		status,
		datap,
		size);

#ifdef _DEBUG
    transfer_packet_map::iterator iter = mDelayedPacketMap.find(packet_id);
	if (iter != mDelayedPacketMap.end())
	{
        if (!(iter->second->mSize == size) && !(iter->second->mDatap == datap))
        {
            LL_ERRS() << "Packet ALREADY in delayed packet map!" << LL_ENDL;
        }
	}
#endif

	mDelayedPacketMap[packet_id] = tpp;
	return true;
}


LLTransferTarget* LLTransferTarget::createTarget(
	LLTransferTargetType type,
	const LLUUID& id,
	LLTransferSourceType source_type)
{
	switch (type)
	{
	case LLTTT_FILE:
		return new LLTransferTargetFile(id, source_type);
	case LLTTT_VFILE:
		return new LLTransferTargetVFile(id, source_type);
	default:
		LL_WARNS() << "Unknown transfer target type: " << type << LL_ENDL;
		return NULL;
	}
}


LLTransferSourceParamsInvItem::LLTransferSourceParamsInvItem() : LLTransferSourceParams(LLTST_SIM_INV_ITEM), mAssetType(LLAssetType::AT_NONE)
{
}


void LLTransferSourceParamsInvItem::setAgentSession(const LLUUID &agent_id, const LLUUID &session_id)
{
	mAgentID = agent_id;
	mSessionID = session_id;
}


void LLTransferSourceParamsInvItem::setInvItem(const LLUUID &owner_id, const LLUUID &task_id, const LLUUID &item_id)
{
	mOwnerID = owner_id;
	mTaskID = task_id;
	mItemID = item_id;
}


void LLTransferSourceParamsInvItem::setAsset(const LLUUID &asset_id, const LLAssetType::EType asset_type)
{
	mAssetID = asset_id;
	mAssetType = asset_type;
}


void LLTransferSourceParamsInvItem::packParams(LLDataPacker &dp) const
{
	LL_DEBUGS() << "LLTransferSourceParamsInvItem::packParams()" << LL_ENDL;
	dp.packUUID(mAgentID, "AgentID");
	dp.packUUID(mSessionID, "SessionID");
	dp.packUUID(mOwnerID, "OwnerID");
	dp.packUUID(mTaskID, "TaskID");
	dp.packUUID(mItemID, "ItemID");
	dp.packUUID(mAssetID, "AssetID");
	dp.packS32(mAssetType, "AssetType");
}


BOOL LLTransferSourceParamsInvItem::unpackParams(LLDataPacker &dp)
{
	S32 tmp_at;

	dp.unpackUUID(mAgentID, "AgentID");
	dp.unpackUUID(mSessionID, "SessionID");
	dp.unpackUUID(mOwnerID, "OwnerID");
	dp.unpackUUID(mTaskID, "TaskID");
	dp.unpackUUID(mItemID, "ItemID");
	dp.unpackUUID(mAssetID, "AssetID");
	dp.unpackS32(tmp_at, "AssetType");

	mAssetType = (LLAssetType::EType)tmp_at;

	return TRUE;
}

LLTransferSourceParamsEstate::LLTransferSourceParamsEstate() :
	LLTransferSourceParams(LLTST_SIM_ESTATE),
	mEstateAssetType(ET_NONE),
	mAssetType(LLAssetType::AT_NONE)
{
}

void LLTransferSourceParamsEstate::setAgentSession(const LLUUID &agent_id, const LLUUID &session_id)
{
	mAgentID = agent_id;
	mSessionID = session_id;
}

void LLTransferSourceParamsEstate::setEstateAssetType(const EstateAssetType etype)
{
	mEstateAssetType = etype;
}

void LLTransferSourceParamsEstate::setAsset(const LLUUID &asset_id, const LLAssetType::EType asset_type)
{
	mAssetID = asset_id;
	mAssetType = asset_type;
}

void LLTransferSourceParamsEstate::packParams(LLDataPacker &dp) const
{
	dp.packUUID(mAgentID, "AgentID");
	// *NOTE: We do not want to pass the session id from the server to
	// the client, but I am not sure if anyone expects this value to
	// be set on the client.
	dp.packUUID(mSessionID, "SessionID");
	dp.packS32(mEstateAssetType, "EstateAssetType");
}


BOOL LLTransferSourceParamsEstate::unpackParams(LLDataPacker &dp)
{
	S32 tmp_et;

	dp.unpackUUID(mAgentID, "AgentID");
	dp.unpackUUID(mSessionID, "SessionID");
	dp.unpackS32(tmp_et, "EstateAssetType");

	mEstateAssetType = (EstateAssetType)tmp_et;

	return TRUE;
}
