/*<<<***************************************************************************
The MIT License (MIT)

Copyright(C) 2013 - 2015 Marc Mirti Mancinelli, Christophe Lardau and Philippe Pamart (xdcube.org)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
////////////////////////////////////////////////////////////////////////////////
#Title: xbDnPlugin
#Project: xdcBase
////////////////////////////////////////////////////////////////////////////////
#Author: Marc MIRTI MANCINELLI
#History:
<15/09/2013: creation.
12/02/2014: this notice inserted.
***************************************************************************>>>*/
#include "stdafx.h"

#include "xbDnPlugin.h"

#include "xbSocketItf.h"

#include "_xbVer.h"

#ifdef _DEBUG
#	include "xbFileTools.h"
#endif

#pragma warning(push)
#pragma warning(disable: 4355) //'this' : used in base member initializer list
xbHotPlugType	 g_xbDnPlugType("DnCenter",XDCBASE_HSVER,"xdcBase DnCenter HotPlug",xbEngines(&g_xbSocketPlugType));

xbDnSession::xbDnSession(xbThreadEventObject *pEvent)
	:m_poDnPlugin(this)
{
	m_pEvent = NULL;
	m_bDestruction = false;
	m_bIsProtoPlug = false;
	m_bPlugClosed = false;
	m_bInPlugProtoUpdate = false;
}

void xbDnSession::onCreateDnPlug(IxbObject *pObj)
{
	xbRefQIPtr<IxbDnPlugFactory> prDnPlugFactory(pObj);

	xbRefPtr<IxbDnPlugFactory>	_prDnPlugFactory;
	xbRefPtr<IxbDnPlugProto>	_prDnPlugProto;
	{
		xbSectLock	lck(&m_sect);
		_prDnPlugFactory = m_prDnPlugFactory;
		_prDnPlugProto = m_prDnPlugProto;

		m_prDnPlugFactory = prDnPlugFactory;
		m_prDnPlugProto.Empty();
		m_bPlugClosed = false;
	}
/*	eProtectBegin();
	if (_prDnPlugProto.IsValid())
		_prDnPlugProto->askClose();
	eProtectEndTrace();*/
}

void xbDnSession::askCloseDnPlug(LPCSTR pReason,bool bForce)
{
	eProtectBegin();
	xbRefPtr<IxbDnPlugFactory>	prDnPlugFactory;
	xbRefPtr<IxbDnPlugProto>	prDnPlugProto;

	xbRefPtr<IxbDnObject> prCurObj;
	{
		xbSectLock lck(&m_sect);
		prDnPlugFactory = m_prDnPlugFactory;
		prDnPlugProto = m_prDnPlugProto;
		m_prDnPlugFactory.Empty();
		m_prDnPlugProto.Empty();
		m_bPlugClosed = true;
		if (bForce || !m_bInPlugProtoUpdate)
			prCurObj = m_prObj;
		if (pReason && m_closeReason.IsEmpty())
			m_closeReason = pReason;
		if (bForce)
			m_pEvent->StopProcess();
		else
			m_pEvent->StopTaskProcess();

	}
	
	if (prCurObj.IsValid())
		prCurObj->stopDnOp(pReason);

	if (prDnPlugProto.IsValid())
		prDnPlugProto->askClose(pReason);
	eProtectEndTrace();
}

void xbDnSession::Destroy(LPCSTR pReason)
{
	{
		xbSectLock lck(&m_sect);
		m_bDestruction = true;
	}
	m_poDnPlugin.Empty();
	askCloseDnPlug(pReason,true); //normally done by previous line (m_poDnPlugin.Empty()),
						// except for internal plugins.
}

void xbDnSession::Close(LPCSTR pReason)
{
	m_poDnPlugin.Empty();
	askCloseDnPlug(pReason,true); //normally done by previous line (m_poDnPlugin.Empty()),
						// except for internal plugins.
	{
		xbSectLock lck(&m_sect);
		m_bIsProtoPlug = false;
	}
}

bool xbDnSession::ReadUrl(xbDnData *pDnData,IxbDnObject *pObj,
	xbRefPtr<IxbInputStream> *ppStm)
{
	xbRefPtr<IxbDnPlugProto>	prProto;
	updateProto(pDnData,pObj,&prProto);

	if (pObj)
		pObj->setDnOpProto(prProto);

	return prProto->ReadUrl(pDnData,pObj,ppStm);
}

bool xbDnSession::WriteUrl(xbDnData *pDnData,IxbDnObject *pObj,
	xbRefPtr<IxbOutputStream> *ppStm)
{
	xbRefPtr<IxbDnPlugProto>	prProto;
	updateProto(pDnData,pObj,&prProto);

	if (pObj)
		pObj->setDnOpProto(prProto);
	
	return prProto->WriteUrl(pDnData,pObj,ppStm);
}

void xbDnSession::updateProto(xbDnData *pDnData,IxbDnObject *pObj,xbRefPtr<IxbDnPlugProto> *ppProto)
{
	m_bInPlugProtoUpdate=true;
	try
	{
		xbRefPtr<IxbDnPlugProto>	prProto;

		{
			xbRefQIPtr<IxbDnObjectProto> prObjProto(pObj,xbOp_Optional);
			if (prObjProto.IsValid() 
				&& prObjProto->ProtoCreate(pDnData->m_op,pDnData->m_Url,&prProto))
			{
				{
					xbSectLock lck(&m_sect);
					if (m_bDestruction)
						xbThrow xbEdnUser("Url '"+pDnData->m_Url+"' download cancelled, download center stopping...","xbDnSession::updateProto()");
					m_prDnPlugProto = prProto;
					m_bIsProtoPlug = false;
				}
				*ppProto = prProto;
				m_bInPlugProtoUpdate=false;
				return;
			}
		}

		bool bIsProtoPlug;

		xbRefPtr<IxbDnPlugFactory>	prProtoFact;
		xbRefPtr<IxbHotPlugin>		prPlugin;
		{
			xbSectLock lck(&m_sect);
			if (m_bDestruction)
				xbThrow xbEdnUser("Url '"+pDnData->m_Url+"' download cancelled, download center stopping...","xbDnSession::updateProto(2)");
			prProto = m_prDnPlugProto;
			prProtoFact = m_prDnPlugFactory;
			bIsProtoPlug = m_bIsProtoPlug;
		}
		m_poDnPlugin.GetPtr(&prPlugin);

		xbString proto(xbGetProtocol(pDnData->m_Url));
		if (proto.IsEmpty()) 
			proto = "file";

		if (bIsProtoPlug && prPlugin.IsValid() && !prPlugin->IsInDestruction()
			&& prProto.IsValid() && !prProto->IsInDestruction())
		{
			for(int i = 0;i < 2; i++)
			{
				xbString protoname(proto);
				if (i == 0) protoname += (pDnData->m_op == xbDnLoad?".r":".w");

				if (g_xbDnPlugType.CanWorkWith(prPlugin,protoname)
					&& prProto->CanWorkWithProto(pDnData,pObj,protoname))
				{
					*ppProto = prProto;
					m_bInPlugProtoUpdate=false;
					return;
				}

			}
		}
		else if (!bIsProtoPlug && prProto.IsValid() && !prProto->IsInDestruction())
		{
			for(int i = 0;i < 2; i++)
			{
				xbString protoname(proto);
				if (i == 0) protoname += (pDnData->m_op == xbDnLoad?".r":".w");

				if (!g_xbDnPlugType.CanWorkWith(NULL,protoname)
					&& prProto->CanWorkWithProto(pDnData,pObj,protoname))
				{
					*ppProto = prProto;
					m_bInPlugProtoUpdate=false;
					return;
				}

			}
		}
		{
			xbSectLock lck(&m_sect);
			if (m_bDestruction)
				xbThrow xbEdnUser("Url '"+pDnData->m_Url+"' download cancelled, download center stopping...","xbDnSession::updateProto(4)");
			m_prDnPlugProto.Empty();
		}
		prProto.Empty();

		if (prPlugin.IsValid() && !prPlugin->IsInDestruction() && prProtoFact.IsValid() && !prProtoFact->IsInDestruction())
		{
			int i;
			for(i = 0;i < 2; i++)
			{
				xbString protoname(proto);
				if (i == 0) protoname += (pDnData->m_op == xbDnLoad?".r":".w");

				if (g_xbDnPlugType.CanWorkWith(prPlugin,protoname)
					&& prProtoFact->CanWorkWithProto(pDnData,pObj,protoname))
					break;
			}
			if (i >= 2)
			{
				{
					xbSectLock lck(&m_sect);
					if (m_bDestruction)
						xbThrow xbEdnUser("Url '"+pDnData->m_Url+"' download cancelled, download center stopping...","xbDnSession::updateProto(5)");
					m_prDnPlugFactory.Empty();
					m_bIsProtoPlug = false;
				}
				prProtoFact.Empty();
			}
		}
		else
		{
			{
				xbSectLock lck(&m_sect);
				m_prDnPlugFactory.Empty();
			}
			prProtoFact.Empty();
			m_poDnPlugin.Empty();
		}

		if (prProtoFact.IsEmpty())
		{
			bool bPluginInUpdate = g_xbDnPlugType.IsInUpdate();
			int i;
			for(i = 0;i < 2; i++)
			{
				xbString protoname(proto);
				if (i == 0) protoname += (pDnData->m_op == xbDnLoad?".r":".w");

				if (g_xbDnPlugType.CreatePluggedObject(&m_poDnPlugin,protoname))
				{
					xbSectLock lck(&m_sect);
					m_bIsProtoPlug = true;
					prProtoFact = m_prDnPlugFactory;
					break;
				}
			}
			if (i >= 2)
			{
				if (!getBuiltinProto(proto,pDnData->m_op,&prProto))
				{
					if (!bPluginInUpdate && !g_xbDnPlugType.IsInUpdate())
					{
						if (pDnData->m_op == xbDnLoad) 
							xbThrow xbEdnUser("Can't load URL '"+pDnData->m_Url+"', using protocol '"+proto+"' !\nReason : invalid or not supported protocol");
						xbThrow xbEdnUser("Can't write to URL '"+pDnData->m_Url+"', using protocol '"+proto+"' !\nReason : invalid or not supported protocol");
					}
					if (pDnData->m_op == xbDnLoad) 
						xbThrow xbEhotPlug("Can't load URL '"+pDnData->m_Url+"', using protocol '"+proto+"' !\nReason : protocol plugin not ready or bad protocol");
					xbThrow xbEhotPlug("Can't write to URL '"+pDnData->m_Url+"', using protocol '"+proto+"' !\nReason : protocol plugin not ready or bad protocol");
				}

				{
					xbSectLock lck(&m_sect);
					if (m_bDestruction)
						xbThrow xbEdnUser("Url '"+pDnData->m_Url+"' download cancelled, download center stopping...","xbDnSession::updateProto(3)");
					m_prDnPlugProto = prProto;
					m_bIsProtoPlug = false;
				}
				*ppProto = prProto;
				m_bInPlugProtoUpdate=false;
				return;
			}
		}

		prProtoFact->ProtoCreate(pObj,proto,xbDnLoad,&prProto);
		{
			xbSectLock lck(&m_sect);
			if (m_bDestruction)
				xbThrow xbEdnUser("Url '"+pDnData->m_Url+"' download cancelled, download center stopping...","xbDnSession::updateProto(4)");
			m_prDnPlugProto = prProto;
			m_bIsProtoPlug = true;
		}
		*ppProto = prProto;
	}
	catch(...)
	{
		m_bInPlugProtoUpdate=false;
		throw;
	}
	m_bInPlugProtoUpdate=false;
}

bool	xbDnSession::DoDnOp(xbDnData *pCallerData,xbDnOp op,const xbString &url,IxbDnObject *pObj)
{
	xbRefPtr<IxbDnObject> prPrevObj;
	{
		xbSectLock lck(&m_sect);
		prPrevObj = m_prObj;
		if (pObj)
			m_prObj = pObj;
	}

	if (pObj)
		pObj->onDoDnOpStart(this,m_pEvent);

	bool ret = true;
	try
	{
		for(;;)
		{
			ResetStatus();
			try
			{
				xbDnData	data(pCallerData,op,url,pObj);
				data.CheckDnOp();

				doDnOp(&data,pObj);

				if (data.HasDnTask())
				{
					xbDnOp						op;
					xbString					suburl;
					xbRefPtr<IxbDnObject>		subobj;

					while(data.GetDnTask(&op,&suburl,&subobj))
					{
						DoDnOp(&data,op,suburl,subobj);
					}
				}
			}
			catch(xbEdnRetryNow *e)
			{
				try
				{
					if (!pObj->setDnOpRetryNow(e))
						xbThrow xbEdn(xbHR_TIMEOUT,"Too many tries for url '"+url+"'",xbGetMsgFull("raw",e));
					::xbSleep(300);
				}
				catch(...)
				{
					xbTraceException(e,true,__FILE__,__LINE__);
					throw;
				}
				xbDbgTraceException(e,true,__FILE__,__LINE__);
				continue;
			}
			catch(xbEhotPlug *e)
			{
				xbRdbgTraceException(e,false,__FILE__,__LINE__);
				throw;
			}
			catch(xbEdnRetryLater *e)
			{
				xbRdbgTraceException(e,false,__FILE__,__LINE__);
				throw;
			}
			catch(xbEdnRetrySession *e)
			{
				xbRdbgTraceException(e,false,__FILE__,__LINE__);
				throw;
			}
			catch(xbEbase *e)
			{
				pObj->SetError(e);
				xbRdbgTraceException(e,true,__FILE__,__LINE__);
				ret = false;
			}
			catch(...)
			{
				xbTraceException(NULL,true,__FILE__,__LINE__);
				pObj->SetError(E_FAIL,"Unknown exception","xbDownloadCenter::directDownload()");
				ret = false;
			}
			break;
		}
	}
	catch(...)
	{
		if (pObj)
		{
			eProtectBegin();
			pObj->onDoDnOpEnd(this);
			eProtectEndTrace();
		}
		eProtectBegin();
		xbSectLock lck(&m_sect);
		m_prObj = prPrevObj;
		eProtectEndTrace();
		throw;
	}
	if (pObj)
	{
		eProtectBegin();
		pObj->onDoDnOpEnd(this);
		eProtectEndTrace();
	}

	{
		xbSectLock lck(&m_sect);
		m_prObj = prPrevObj;
	}
	return ret;
}

void xbDnSession::doDnOp(xbDnData *pDnData,IxbDnObject *pObj)
{
	pObj->ClearError();

	if (pObj->DoDnOp(pDnData))
	{
		xbDbgOutput("xbDnSession::doDnOp("+pDnData->m_Url+") : download done by object");
		return;
	}

	if (pDnData->UrlOpen())
	{
		xbDbgOutput("xbDnSession::doDnOp("+pDnData->m_Url+") : download done by protocol");
		return;
	}

	try
	{
		if (pDnData->m_op == xbDnLoad)
			doDnLoad(pDnData,pObj);
		else
			doDnWrite(pDnData,pObj);
	}
	catch(...)
	{
		pDnData->UrlClose("dnSession terminates download operation on error");
		throw;
	}
	pDnData->UrlClose("dnSession terminates download operation");
}

#define xbDnSession_BUFSZ1 (8*1024)
#define xbDnSession_BUFSZ2 (1024*1024)
void xbDnSession::doDnLoad(xbDnData *pDnData,IxbDnObject *pObj)
{
	int64	fileSz = pDnData->UrlGetSize();

	pObj->LoadingStart(pDnData,fileSz);

	try
	{
		xbData dta;
		dta.Length = xbDnSession_BUFSZ1;
		//BYTE	buf[8192];
		for(;;)
		{
			pDnData->CheckStop();

			ulong rdCnt = pDnData->UrlRead((void*)(const void*)dta,dta.Length);

			if (!rdCnt) 
				break;

			if (!pObj->LoadingAddBuffer(pDnData,(const uchar*)(const void*)dta,rdCnt)) 
				break;

			dta.Length = xbDnSession_BUFSZ2;
		}
	}
	catch(...)
	{
		pObj->LoadingEnd(pDnData,false);
		throw;
	}

	pObj->LoadingEnd(pDnData,true);
	xbDbgOutput("xbDnSession::doDnLoad("+pDnData->m_Url+") : download using protocol");
}

void xbDnSession::doDnWrite(xbDnData *pDnData,IxbDnObject *pObj)
{
	xbRefPtr<IxbOutputStream> prStm;
	try
	{
		pDnData->UrlGetWriteStm(&prStm);

		pObj->WriteToStream(pDnData,prStm);
	}
	catch(...)
	{
		eProtectTrace(pDnData->UrlWritingEnd(false));
		eProtectTrace(pObj->WritingEnd(pDnData,false));
		throw;
	}
	try
	{
		pDnData->UrlWritingEnd(true);
	}
	catch(...)
	{
		eProtectTrace(pObj->WritingEnd(pDnData,false));
		throw;
	}
	pObj->WritingEnd(pDnData,true);
	xbDbgOutput("xbDnSession::doDnWrite("+pDnData->m_Url+") : write using protocol");
}

bool xbDnSession::getBuiltinProto(const xbString &proto,xbDnOp op,xbRefPtr<IxbDnPlugProto> *ppProto)
{
	if (op != xbDnLoad) return false;

	if (proto != "file" && proto != "res" && proto != "data")
		return false;

	xbDnBuiltinPlugin *pProtoObj = xbDnew xbDnBuiltinPlugin();
	try
	{
		pProtoObj->Create();
		
		*ppProto = pProtoObj;
		
		pProtoObj->Release();
	}
	catch(...)
	{
		pProtoObj->Release();
		throw;
	}

	return true;
}

bool xbDnBuiltinPlugin::CanWorkWithProto(xbDnData *pDnData,IxbDnObject *pObj,const xbString &szProto) const
{
	if (szProto == "file.r" || szProto == "res.r" || szProto == "data.r")
		return true;
	return false;
}

bool xbDnBuiltinPlugin::WriteUrl(xbDnData *pDnData,IxbDnObject *pObj,
		xbRefPtr<IxbOutputStream> *ppStm)
{
	xbThrow xbEsys(E_NOTIMPL,"xbDnBuiltinPlugin::WriteUrl() : not implemented");
	return false;
}

bool xbDnBuiltinPlugin::ReadUrl(xbDnData *pDnData,IxbDnObject *pObj,
		xbRefPtr<IxbInputStream> *ppStm)
{
	xbAssert(pDnData->m_op == xbDnLoad);

	if (tryResDownload(pDnData,pObj,ppStm))
		return true;
	if (tryDataDownload(pDnData,pObj,ppStm))
		return true;

	xbString	file;
	if (!::xbGetFileUrl(pDnData->m_Url,&file)) 
		xbThrow xbEdn("Can't load url '"+pDnData->m_Url+"' : bad plugin loaded !","xbDnBuiltinPlugin::ReadUrl()");

	if (pObj->LoadFromFile(pDnData,file)) return true;

	xbFileStream *pStm = xbDnew xbFileStream();
	try
	{
		pStm->Open(file,xbOpen,xbAccessR,xbWorkSharedR);
		
		*ppStm = pStm;
		
		pStm->Release();
	}
	catch(...)
	{
		pStm->Release();
		throw;
	}

	pObj->setProtoInfo("xdcBase Builtin file downloader",0);

	return false;
}

bool xbDnBuiltinPlugin::tryResDownload(xbDnData *pDnData,IxbDnObject *pObj,xbRefPtr<IxbInputStream> *ppStm)
{
	xbString	dll,res,type,ifNotUrl;
	LPCSTR		resPtr,typePtr;
	if (!::xbGetResUrl(pDnData->m_Url,&dll,&res,&resPtr,&type,&typePtr,&ifNotUrl))
	{
		if (ifNotUrl.IsEmpty())
			return false;

		pDnData->SubDnOp(xbDnLoad,ifNotUrl,pObj);

		return true;
	}
	HMODULE	hModLoad = NULL;
	bool bhModToFree = false;
	if (dll.IsValid())
	{
/*		bool bClsid = false;
		if (dll.First() == '{')
		{
			CLSID clsid(::xbStrToGuid(dll));

			dll = xbGetModulePath(clsid);
		}*/
#ifndef XDCBASE_EMBED
//		if (!bClsid)
		{
			xbXdcLibEnum libenum;
			hModLoad = libenum.FindHlib(dll);
		}
		if (!hModLoad)
#endif
		{
			hModLoad = ::xbLoadLibraryAsData(dll);
//			if (!hModLoad)
//				hModLoad = ::xbLoadLibrary(dll);
//			if (!hModLoad) xbThrow TExcept(xbHR_LAST,"xbDownloadCenter::tryResDownload("+pDnData->m_Url+") : dll '"+dll+"' not loaded");
			bhModToFree = true;
		}
	}

	try
	{
#ifndef XDCBASE_EMBED
		xbXdcLibEnum libenum;
#endif
		int cnt = hModLoad?-1:0;
		bool ok = false;
		for(;;)
		{
			HMODULE hMod;
			cnt++;
			if (cnt == 0) hMod = hModLoad;
			else if (cnt == 1) 
			{
				hMod = ::xbGetInstanceHandle();
				if (!hMod) cnt = 2;
			}
			else if (cnt == 2) hMod = NULL;
			else 
			{
#ifndef XDCBASE_EMBED
				hMod = libenum.NextHlib();
				if (!hMod)
#endif
					 break;
			}
			xbDbgOutput("xbDnBuiltinPlugin::tryResDownload("+pDnData->m_Url+") : "+xbString(cnt)+" : searching in "+::xbGetFilePath(hMod));

			if (!typePtr) typePtr = pObj->GetDefaultResType();

			if (res.GetLength()>1 && res.First() == '-')
			{
				int ix = -::xbStrToInt(res);
				if (ix > 0)
				{
					xbString ret = xbGetResNo(ix,typePtr,hMod);
					if (ret.IsValid())
					{
						if (ret.First() == '#')
						{
							int id = xbStrToInt(ret.Mid(1));
							xbRdbgAssert(id > 0);

							resPtr = (LPCSTR)MAKEINTRESOURCE(id);
							res.Empty();
						}
						else
						{
							res = ret;
							res.OwnData();
							resPtr = res;
						}
					}
				}
			}

			if (pObj->LoadFromRes(pDnData,resPtr,(type.IsEmpty()?NULL:(LPCSTR)type),hMod)) 
			{
				ok = true;
				break;
			}

			HRSRC hrSrc = ::FindResource(hMod,resPtr,typePtr/*,MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)*/);
			if (!hrSrc) 
			{
				continue;
			}

			HGLOBAL hGlob = ::LoadResource(hMod,hrSrc);
			if (!hGlob) continue;

			LPVOID ptr = ::LockResource(hGlob);
			if (!ptr) continue;

			DWORD		size = ::SizeofResource(hMod,hrSrc);
			if (!size) continue;

			pObj->LoadingStart(pDnData,size);
			try
			{
				pObj->LoadingAddBuffer(pDnData,(BYTE*)ptr,size);
			}
			catch(...)
			{
				pObj->LoadingEnd(pDnData,false);
				throw;
			}
			pObj->LoadingEnd(pDnData,true);

			ok = true;
			
			break;
		}
		if (!ok) 
			xbThrow xbEdnUser(xbHR_LAST,"Can't load resource file '"+pDnData->m_Url+"'","xbDownloadCenter::tryResDownload()");
	}
	catch(...)
	{
		if (bhModToFree && hModLoad) 
			xbFreeLibrary(hModLoad);
		throw;
	}
	if (bhModToFree && hModLoad) 
		xbFreeLibrary(hModLoad);
	pObj->setProtoInfo("xdcBase Builtin resource downloader",0);
	return true;
}

bool	xbDnBuiltinPlugin::tryDataDownload(xbDnData *pDnData,IxbDnObject *pObj,xbRefPtr<IxbInputStream> *ppStm)
{
	xbString data;
	if (!::xbGetDataUrl(pDnData->m_Url,&data)) return false;

	pObj->LoadingStart(pDnData,data.GetLength());
	try
	{
		pObj->LoadingAddBuffer(pDnData,(const BYTE*)(LPCSTR)data,data.GetLength());
	}
	catch(...)
	{
		pObj->LoadingEnd(pDnData,false);
		throw;
	}
	pObj->LoadingEnd(pDnData,true);

	pObj->setProtoInfo("xdcBase Builtin data downloader",0);

	return true;
}

#pragma warning(pop)
