#INCLUDE WCONNECT.H

*** The following PRG contains:
*** A self loading stub program that can be used to run
*** as the code 'hot swapper' after the main EXe has unloaded
***
*** Following the stub is the actual CodeUpdate class
************************************************************************
* CodeUpdate
****************************************
***  Function: Hot swaps an EXE file by running a Zip Exe file that
***            unzips the update files.
***    Assume: The original application has quit and is no longer
***            loaded and running.
***      Pass: lcExeFile     -   The main application EXE file to run
***                              (wwReader70.exe)
***            lcUpdateFile  -   The updated EXE(Auto-Zip) file (full path)
***                              that contains the update files
***                              (d:\temp\updates\wwReaderUpdate.exe)
***    Return: nothing
************************************************************************
LPARAMETER lcExeFile, lcUpdateFile, lcApplicationName

SET PROCEDURE TO wwCodeUpdate ADDIT
SET PROCEDURE TO wwUtils ADDIT
SET PROCEDURE TO wwHTTP ADDIT
SET CLASSLIB TO wwDialogs ADDIT

*** Default - just load classes and exit
IF EMPTY(lcExeFile)
* WAIT WINDOW TIMEOUT 5 "No Exe file specified on command line..."
	RETURN
ENDIF
IF EMPTY(lcUpdateFile) OR !FILE(lcUpdateFile)
	WAIT WINDOW TIMEOUT 5 "Invalid Update File Path specified on command line..."
ENDIF

loVersion = CREATEOBJECT("wwCodeUpdate")
loVersion.cExeFile = lcExeFile

IF !EMPTY(lcApplicationName)
	loVersion.cApplicationName = lcApplicationName
ENDIF

loVersion.SwapExes(lcUpdateFile)

RETURN


*** Main Application Sample code
#IF .F.
	#DEFINE APP_VERSION 1.0
*** Test Code
	MESSAGEBOX("West Wind Technologies presents:" + CHR(13)+CHR(13) + ;
		CHR(9) + "GREAT BIG, BAD APPLICATION" + CHR(13) + ;
		CHR(9) + "Version " + TRANS(APP_VERSION,"99.99") + CHR(13),;
		64,"West Wind Technologies")


	o=CREATEOBJECT("wwCodeUpdate")
	o.cVersionUrl = "http://localhost/codeupdate/update.xml"
	o.cVersionType = "N"
	o.cExeFile = "test.exe"

	lnVersion = o.GetVersionInfo()
	IF lnVersion > APP_VERSION
		IF MESSAGEBOX("Version " + TRANS(lnVersion,"99.99") + " of GREAT BIG APP" + CHR(13) +;
				"is available online now." + CHR(13) + CHR(13) +;
				"Would you like to download it now?",32+4,;
				"West Wind Technologies") = 6
			o.DownloadUpdate()
			o.UpdateCode("cu_Update.exe")
		ENDIF
	ENDIF

	MESSAGEBOX("your Great Big Application starts here")

	RETURN
#ENDIF

*************************************************************
DEFINE CLASS wwCodeUpdate AS RELATION
*************************************************************
*: Author: Rick Strahl
*:         (c) West Wind Technologies, 2000
*:Contact: http://www.west-wind.com
*************************************************************
	#IF .F.
*:Help Documentation
*:Topic:
		wwServer::GetProcessID

*:Description:

*:Example:

*:Remarks:
		SERVER UPDATE FILE looks AS follows:
		<?XML VERSION="1.0"?>
		<codeupdate>
		<VERSION>2.55</VERSION>
		<minversion>2.45</minversion>
		<fileurl>http://www.west-WIND.com/FILES/UPDATES/wwhelp_update.EXE</fileurl>
		<filesize>444</filesize>
		<commandline>wwhelp.EXE</commandline>
		</codeupdate>

		The ONLY required KEYS are <VERSION> AND <fileurl> WITH The others optional.

*:SeeAlso:


*:ENDHELP
	#ENDIF

	cVersionUrl = ""
	cVersionType = "C"
	cExeFile = ""
	cCommandLineParameters = ""
	cApplicationName = "the application"
	cDownloadPath = ".\CodeUpdate\"

	lUnZipFile = .F.
	cErrorMsg = ""
	lError = .F.

*** Downloaded values from XML file
	vOnlineVersion = ""
	cOnlineFileUrl = ""
	nFileSize = 0
	vOnlineMinVersion = ""
	cUserMessage = ""
	cNewsMessage = ""

	lShowDialog = .T.
	nConnectTimeout = 5000

*** Allows access configuration of HTTP
*** settings. Call CreateHTTPClient to
*** create an instance of this object to
*** manipulate
	oHTTP = NULL

	PROTECTED cXML
	cXML = ""
	cAppStartpath = ""

************************************************************************
* wwCodeUpdate :: Init
*********************************
***  Function:
***    Assume:
***      Pass:
***    Return:
************************************************************************
	FUNCTION INIT

		THIS.cAppStartpath = GetAppStartPath()
		
	ENDFUNC
* wwCodeUpdate :: Init

************************************************************************
* wwCodeUpdate :: GetVersionInfo
*********************************
***  Function: Retrieves online XML file and parses the Version and
***            download URL from the result
***    Assume: Sets these properties from XML retrieved:
***            vOnlineVersion
***            cOnlineFileUrl
***      Pass: lnVersion   -   Current Version Number/or string
***    Return: Numeric: Online Version number or 0 on failure
************************************************************************
	FUNCTION GetVersionInfo
		LOCAL loIP, lcVersionType, lcXML, lnSize, lcVersion

		THIS.SetError()

		lcVersionType = THIS.cVersionType

		IF ISNULL(THIS.oHTTP)
*   THIS.CreateHTTPClient()
			THIS.oHTTP = CREATEOBJECT("cu_wwHTTP")
		ENDIF

		THIS.oHTTP.lShowDialog = .F.

		lcXML = THIS.oHTTP.HTTPGet(THIS.cVersionUrl)

		IF THIS.oHTTP.nError # 0
			THIS.SetError(THIS.oHTTP.cErrorMsg)
			RETURN IIF(lcVersionType = "C","",0)
		ENDIF

		IF LEFT(lcXML,5) <> "<?xml"
			THIS.SetError("Missing or invalid XML returned from server")
			RETURN IIF(lcVersionType = "C","",0)
		ENDIF

		lcVersion = Extract(lcXML,"<version>","</version>")
		IF EMPTY(lcVersion)
			THIS.SetError("No version number found in XML")
			RETURN IIF(lcVersionType = "C","",0)
		ENDIF

		THIS.vOnlineVersion = IIF(lcVersionType="C",lcVersion,VAL(lcVersion))

		lcVersion = Extract(lcXML,"<minversion>","</minversion>")
		IF !EMPTY(lcVersion)
			THIS.vOnlineMinVersion = IIF(lcVersionType="C",lcVersion,VAL(lcVersion))
		ENDIF
***!!!!!!!!!
*---------------------------
		*THIS.cOnlineFileUrl = Extract(lcXML,"<fileurl>","</fileurl>")
		THIS.cOnlineFileUrl = JUSTPATH(THIS.cVersionUrl) + "/" + Extract(lcXML,"<fileurl>","</fileurl>")
*======================================
***!!!!!!!!!!!!!!
		THIS.nFileSize = VAL( Extract(lcXML,"<filesize>","</filesize>") )
		THIS.cUserMessage = Extract(lcXML,"<usermessage>","</usermessage>")
		THIS.cNewsMessage = Extract(lcXML,"<newsmessage>","</newsmessage>")

*** Save the XML just in case
		THIS.cXML = lcXML

*!*			IF lcVersionType = "C"
*!*				RETURN lcVersion
*!*			ENDIF

		RETURN THIS.vOnlineVersion
	ENDFUNC
* wwCodeUpdate :: GetVersion

************************************************************************
* wwCodeUpdate :: CheckVersionAndUpdate
***************************************
***  Function:
***    Assume:
***      Pass:
***    Return:
************************************************************************
	FUNCTION CheckVersionAndUpdate
		LPARAMETERS lvVersion

		lvNewVersion = THIS.GetVersion()

		IF lvNewVersion > lvVersion
			THIS.DownloadUpdate()
			THIS.UpdateCode()
		ENDIF

	ENDFUNC
* wwCodeUpdate :: CheckVersionAndUpdate

************************************************************************
* wwCodeUpdate :: DownloadUpdate
*********************************
***  Function: Downloads the actual file from the Web site.
***    Assume:
***      Pass: llCheckForExistingVersion
***    Return: .T. or .F.
************************************************************************
	FUNCTION DownloadUpdate
		LPARAMETER llCheckforExistingVersion, tcFileVersionXML
		LOCAL loIP, lcData, lnSize, loUrl

		IF !EMPTY(tcFileVersionXML)
			lcFileVersionXML = tcFileVersionXML
		ELSE
			lcFileVersionXML = "fileversion.xml"	
		ENDIF
		 
		IF llCheckforExistingVersion
			lcFile = File2Var(THIS.cDownloadPath + lcFileVersionXML)
			lcVersion = Extract(lcFile,"<version>","</version>")
			IF IIF(THIS.cVersionType="C",lcVersion,VAL(lcVersion)) = THIS.vOnlineVersion
				RETURN .T. && File was already downloaded
			ENDIF
		ENDIF

		IF ISNULL(THIS.oHTTP)
			loIP = THIS.CreateHTTPClient()
		ELSE
			loIP = THIS.oHTTP
		ENDIF

		loIP.lShowDialog = THIS.lShowDialog

*** Break down the URL into its components
		loUrl = loIP.InternetCrackUrl(THIS.cOnlineFileUrl)
		IF ISNULL(loUrl)
			RETURN .F.
		ENDIF
loIP.nhttpport=VAL(loUrl.cPort)
		IF loIP.HTTPConnect(loUrl.cServer,"","",IIF(LOWER(loUrl.cProtocol)="https",.T.,.F.)) # 0
			THIS.SetError(loIP.cErrorMsg)
			RETURN .F.
		ENDIF

*** Create a temporary directory if it doesn't exist
		IF !ISDIR(THIS.cDownloadPath)
			MD (THIS.cDownloadPath)
		ENDIF

		lcTFile = THIS.cDownloadPath + JUSTFNAME(STRTRAN(THIS.cOnlineFileUrl,"/","\"))
		lcData = ""
		lnSize = 0


		IF loIP.HTTPGetEx( TRIM(loUrl.cPath),@lcData,@lnSize,,lcTFile) # 0
			THIS.SetError(loIP.cErrorMsg)
			RETURN .F.
		ENDIF

		File2Var(THIS.cDownloadPath + lcFileVersionXML,;
			[<?xml version="1.0"?><version>]+ TRANS(THIS.vOnlineVersion) +[</version>])

		loIP.HTTPClose()

	ENDFUNC
* wwCodeUpdate :: DownloadUpdate


************************************************************************
* wwCodeUpdate :: SwapExes
****************************************
***  Function: Hot swaps an EXE file by running a Zip Exe file that
***            unzips the update files.
***    Assume: The original application has quit and is no longer
***            loaded and running.
***            Meant to be run as a mainline method
***      Pass: lcUpdateFile  -   The updated EXE(Auto-Zip) file (full path)
***                              that contains the update files
***    Return: nothing
************************************************************************
	FUNCTION SwapExes
		LPARAMETER lcUpdateFile
		LOCAL loSafety, OP

		loSafety = CREATEOBJECT("wwEnv","SAFETY","OFF")

*_screen.visible = .F.

		IF EMPTY(lcUpdateFile)
			lcUpdateFile = SYS(5) + CURDIR() + [\codeupdate\Codeupdate.exe /auto ] +;
				SYS(5) + CURDIR()
		ENDIF

*** Configure the notification dialog
		OP = CREATEOBJECT("wwProgressForm")
		OP.WIDTH = 350
		OP.SetCaption("File Update")
		OP.SetDescription("Updating files for " + THIS.cApplicationName + "...")
		OP.HideProgressBar()
		OP.SHOW()

*** Force the form to update
		DOEVENTS


*** Allow the application some time to go away
		INKEY(5,"HM")

*** Execute the RUN command and wait
		lcParms = [RUN /n7] + lcUpdateFile
		&lcParms

*** Wait for 5 seconds to allow unzipping to complete
*** This should be plenty of time
		OP.SetDescription([Getting ready to restart ] +;
			THIS.cApplicationName + [...])
		INKEY(5,"HM")

*** Make sure the new Exe exists - if not wait longer
		IF !FILE(THIS.cExeFile)
*** Wait 5 more seconds
			INKEY(5,"HM")
		ENDIF

*** And start up the EXE
		lcParms = [RUN /n1 ] + THIS.cExeFile + ;
			IIF(!EMPTY(THIS.cCommandLineParameters)," " + THIS.cCommandLineParameters,"")
		&lcParms

		RETURN

		RETURN
*  wwCodeUpdate :: UpdateExe

************************************************************************
* wwCodeUpdate :: RunUpdateExe
*********************************
***  Function:
***    Assume:
***      Pass:
***    Return:
************************************************************************
	FUNCTION RunUpdateExe
		LPARAMETER lcUpdateExe
				
*** Run external program to copy in the files
		lcParms = [RUN /n1 ] + lcUpdateExe
*Messagebox(lcParms)
		&lcParms

*** Required if READ EVENTS IS ACTIVE
*** otherwise EXE won't release
		IF RDLEVEL() > 0
			CLEAR EVENTS
		ENDIF

			ON ERROR *
			ON SHUTDOWN
*!*			CLEAR DLLS
*!*			RELEASE ALL
*!*			CLEAR ALL

		DOEVENTS

		QUIT

	ENDFUNC
* wwCodeUpdate :: RunUpdateExe

************************************************************************
* wwCodeUpdate :: CopyUpdate
*********************************
***  Function: Once downloaded copies the update
***    Assume:
***      Pass:
***    Return:
************************************************************************
	FUNCTION CopyUpdate

		OP = CREATEOBJECT("wwProgressForm")
		OP.SetCaption("File Update")
		OP.SetDescription("Updating file:" + THIS.cExeFile)
		OP.HideProgressBar()
		OP.SHOW()

		IF MESSAGEBOX("Ready to update your application",32+4,"Code Update") = 6
			COPY FILE (THIS.cExeFile) TO (".\codeupdate\" + THIS.cExeFile + "_bak")
			COPY FILE (".\codeupdate\" + THIS.cExeFile) TO (THIS.cExeFile)
		ENDIF

	ENDFUNC
* wwCodeUpdate :: CopyUpdate

************************************************************************
* wwCodeUpdate :: CreateHTTPClient
****************************************
***  Function:
***    Assume:
***      Pass:
***    Return:
************************************************************************
	FUNCTION CreateHTTPClient()
		THIS.oHTTP = CREATEOBJECT("CU_wwHTTP")
		THIS.oHTTP.nConnectTimeout = THIS.nConnectTimeout
		RETURN THIS.oHTTP
	ENDFUNC
*  wwCodeUpdate :: CreateHTTPClient

************************************************************************
* wwCodeUpdate :: SetError
*********************************
	PROTECTED FUNCTION SetError
		LPARAMETERS lcErrorMsg

		IF PCOUNT() = 0
			THIS.lError = ""
			THIS.cErrorMsg = ""
		ELSE
			THIS.lError = .T.
			THIS.cErrorMsg = lcErrorMsg
		ENDIF

	ENDFUNC
* wwCodeUpdate :: SetError

ENDDEFINE




*****************************************************
DEFINE CLASS CU_wwHTTP AS wwHTTP
**************************************

*** Custom properties dealing with display
*** of download information
	lShowDialog = .F.
	oProgressForm = .NULL.
	cCaption = "Se descarca actualizarea..."
	nContentSize = 0

************************************************************************
* CU_wwHTTP :: OnHTTPBufferUpdate
************************************
***  Function: HTTP Progress Event Handler
***    Assume: Relies on wwDialogs.vcx for Progress Form
***      Pass:
***    Return:
************************************************************************
	FUNCTION OnHTTPBufferUpdate
		LPARAMETERS lnbytes,lnbufferreads,lccurrentchunk

		DO CASE
*** If this is the 0 chunk it's HTTP Header
			CASE lnbufferreads = 0
				THIS.nContentSize = VAL( Extract(lccurrentchunk,CHR(13)+CHR(10) + "Content-length: ",CHR(13)) )
				DO CASE
					CASE THIS.nContentSize > 90000
						THIS.nHTTPWorkBufferSize = 16484
					CASE THIS.nContentSize > 40000
						THIS.nHTTPWorkBufferSize = 8182
				ENDCASE
				RETURN
			CASE lnbufferreads = -1
*** Done
				DOEVENTS
				THIS.oProgressForm = .F.
				THIS.oProgressForm = .NULL.
				RETURN
			OTHERWISE
				DOEVENTS
		ENDCASE

		IF THIS.lShowDialog
			IF lnbufferreads=1
				THIS.oProgressForm = CREATEOBJECT("wwProgressForm")
				THIS.oProgressForm.SetCaption(THIS.cCaption)
				THIS.oProgressForm.ShowCancelButton()
				THIS.oProgressForm.SHOW()
			ENDIF

			IF THIS.oProgressForm.lCancelled
				THIS.lHTTPCancelDownload = .T.
			ENDIF

			THIS.oProgressForm.SetDescription("S-au primit de la " + THIS.cServer + ":" +CHR(13) +;
				LTRIM( TRANSFORM(lnbytes,"999,999,999") ) + " din " +;
				LTRIM(TRANSFORM(THIS.nContentSize,"999,999,999"))+ " bytes")
			THIS.oProgressForm.SetProgress(lnbytes/THIS.nContentSize * 100)
		ENDIF

	ENDFUNC


ENDDEFINE
