[Win]Online Update for your apps.

Share your advanced PureBasic knowledge/code with the community.
User avatar
HeX0R
Addict
Addict
Posts: 1189
Joined: Mon Sep 20, 2004 7:12 am
Location: Hell

[Win]Online Update for your apps.

Post by HeX0R »

I were in need of a good and easy Online-Update-Function, to be able to keep some of my programs up to date.

There are allready a few solutions available, but none of them do fit my needs:

1.) I don't want to have an extra update.exe or something like this
2.) The checking and downloading should also work through a proxy and even redirected urls should be catched.
3.) Should be easy and short.
4.) threadsafe without threadsafe ;)
5.) Should work with unicode also.

Because of point 2 and 3 i decided very early to go the api-way, so this is win-only.

This means, the user only has to make sure, that his/her internet explorer is configured correctly (with proxy settings or whatever) and the code should work as expected.

Here comes the main include:

Code: Select all

;/-----------------
;|
;| CheckForUpdates.pbi
;| V1.05 [01.06.2010]
;|
;| ©HeX0R 2009/2010
;|
;| Small include, which should make it
;| very easy, to give your applications
;| the power of an
;| Online Update
;|
;| for PB >= 4.0 (no DEMO)
;|
;| Needs the freely available
;| Online-Update-Creat0r
;| to create the online-info-files
;| (Just run example to get it)
;|
;| New since 1.04:
;| Error handling
;| (and also auto error logging)
;/-----------------
#CFU_IDENT = $6f3bc31a

Structure _CFU_VERSION_INFO_
	Name.s           ;Name of your Program
	Version.s        ;Version as String (for example "1.05.12")
	URL.s            ;Link, where Patch can be downloaded
	FileSize.q       ;Filesize of the patch
	ReleaseDate.l    ;Releasedate of the patch (unix timestamp)
	MD5.s            ;MD5 FileFingerprint of Patchfile
	ServerFileName.s ;Real name of the Patch (if it is redirected for example)
	InfoLink.s       ;Link, to get further information
	Text.s[3]        ;Optional Texts, you maybe need
	Number.l[3]      ;Optional Long-Variables (for example used as Flags)
	*Description     ;Description of this update
EndStructure

Structure _CFU_THREAD_VALUES_
	*URL
	AuthMode.i
	BlockSize.i
	WindowHandle.i
	Message.i
	FileHandle.i
	FileSize.q
	StatusGadget.i
	*Result
EndStructure

Structure _CFU_CallBack_Cookie_
	*Buffer
	Pointer.i
EndStructure

#CFU_NO_AUTH           = $00
#CFU_VERSIONINFO_AUTH  = $01
#CFU_FILEDOWNLOAD_AUTH = $02

Enumeration
	#CFU_ERROR_NO_ERROR
	#CFU_ERROR_CANT_CREATE_FILE
	#CFU_ERROR_CANT_CREATE_THREAD
	#CFU_ERROR_NO_MEMORY
	#CFU_ERROR_NO_INTERNET
	#CFU_ERROR_UNABLE_TO_OPEN_URL
	#CFU_ERROR_UNABLE_TO_READ_ONLINEFILE
	#CFU_ERROR_EMPTY_INFOFILE
	#CFU_ERROR_NO_INFOFILE
EndEnumeration

CompilerIf Defined(CFU_LOG_ERROR, #PB_Constant) = 0
#CFU_LOG_ERROR = #True
CompilerEndIf

Prototype CFU_ErrorHandlerPrototype(ErrorNr)

Global *CFU_AuthBuffer
Global CFU_AuthBufferLength
Global CFU_AuthMode
Global CFU_LastError
Global CFU_ErrorHandler.CFU_ErrorHandlerPrototype = #False

Procedure CFU_InitErrorHandler(*Proc)
	CFU_ErrorHandler = *Proc
EndProcedure

Procedure CFU_GetLastError()
	Protected A = #CFU_ERROR_NO_ERROR

	Swap A, CFU_LastError

	ProcedureReturn A
EndProcedure

CompilerIf Defined(CFU_GetErrorMessage, #PB_Procedure) = 0
Procedure.s CFU_GetErrorMessage(Error = #PB_Default)
	Protected Result.s = "No Error", Msg.s

	If Error = #PB_Default
		Error = CFU_GetLastError()
	EndIf

	Msg = Space(1024)

	Select Error
		Case #CFU_ERROR_CANT_CREATE_FILE
			Result = "Unable to create File"
		Case #CFU_ERROR_CANT_CREATE_THREAD
			Result = "Unable to create Thread"
		Case #CFU_ERROR_NO_MEMORY
			Result = "Not enough Memory"
		Case #CFU_ERROR_NO_INTERNET
			Result = "WinINet-Init failed"
			If FormatMessage_(#FORMAT_MESSAGE_FROM_SYSTEM, 0, GetLastError_(), 0, @Msg, 1024, 0)
				Result + #CRLF$ + Msg
			EndIf
		Case #CFU_ERROR_UNABLE_TO_OPEN_URL
			Result = "Unable to connect to URL"
			If FormatMessage_(#FORMAT_MESSAGE_FROM_SYSTEM, 0, GetLastError_(), 0, @Msg, 1024, 0)
				Result + #CRLF$ + Msg
			EndIf
		Case #CFU_ERROR_UNABLE_TO_READ_ONLINEFILE
			Result = "Unable to open File on Server"
			If FormatMessage_(#FORMAT_MESSAGE_FROM_SYSTEM, 0, GetLastError_(), 0, @Msg, 1024, 0)
				Result + #CRLF$ + Msg
			EndIf
		Case #CFU_ERROR_EMPTY_INFOFILE
			Result = "This seems to be an empty infofile"
		Case #CFU_ERROR_NO_INFOFILE
			Result = "The infofile doesn't exist on server!"
	EndSelect

	ProcedureReturn Result
EndProcedure
CompilerEndIf

Procedure CFU_LogError()
	Protected FID, Error

	If #CFU_LOG_ERROR
		Error = CFU_GetLastError()
		If Error <> #CFU_ERROR_NO_ERROR
			If CFU_ErrorHandler
				CFU_ErrorHandler(Error)
			Else
				If FileSize(GetEnvironmentVariable("APPDATA") + "\C4U\") <> -2
					CreateDirectory(GetEnvironmentVariable("APPDATA") + "\C4U\")
				EndIf
				FID = OpenFile(#PB_Any, GetEnvironmentVariable("APPDATA") + "\C4U\ErrorLog.txt")
				If FID
					FileSeek(FID, Lof(FID))
					WriteStringN(FID, FormatDate("%dd.%mm.%yyyy %hh:%ii:%ss ", Date()) + CFU_GetErrorMessage(Error))
					CloseFile(FID)
				EndIf
			EndIf
		EndIf
	EndIf

EndProcedure

Procedure CFU_Editor_StreamCallback(*dwCookie._CFU_CallBack_Cookie_, *pbBuff, cb, *pcb.LONG)
	Protected Length
	;/------
	;| Callback used for CFU_Editor_Load()
	;/------

	Length = MemorySize(*dwCookie\Buffer) - *dwCookie\Pointer
	If Length > cb
		CopyMemory(*dwCookie\Buffer + *dwCookie\Pointer, *pbBuff, cb)
		*pcb\l = cb
	Else
		If Length
			CopyMemory(*dwCookie\Buffer + *dwCookie\Pointer, *pbBuff, Length)
		EndIf
		*pcb\l = Length
	EndIf

	*dwCookie\Pointer + *pcb\l

	ProcedureReturn 0
EndProcedure

Procedure CFU_Editor_Load(Gadget, *Buffer)
	Protected Stream.EDITSTREAM, C._CFU_CallBack_Cookie_
	;/------
	;| Procedure to get rtf-Data into an EditorGadget
	;/------

	If *Buffer And MemorySize(*Buffer)
		C\Pointer          = 0
		C\Buffer           = *Buffer
		Stream\dwCookie    = @C
		Stream\pfnCallback = @CFU_Editor_StreamCallback()
		SendMessage_(GadgetID(Gadget), #EM_STREAMIN, #SF_RTF, @Stream)
	EndIf

EndProcedure

Procedure CFU_GetHTTPFile(*B._CFU_THREAD_VALUES_)
	Protected hINet, hData, Bytes, Size, DownloadedBytes, f.f
	;/------
	;| Procedure to download something from the internet.
	;| We use the WinAPI, because it makes sure
	;| it will also work, when the user is behind a proxy.
	;| (and he/she configures his/her IE correctly)
	;| and even redirected files will be catched.
	;|
	;| This procedure is called as thread, to make sure
	;| there won't be any lags, when the host is not reachable
	;| or something like this.
	;|
	;| This procedure can download directly into memory or in a file.
	;| See CFU_LoadInternetFile (in file) and CFU_LoadVersionInfo (in memory)
	;/-------

	*B\Result = 0
	Size      = 0
	hINet     = InternetOpen_(?GVI_Agent, 0, 0, 0, 0)

	If Not hInet
		CFU_LastError = #CFU_ERROR_NO_INTERNET
	Else
		;INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_PRAGMA_NOCACHE
		If *B\AuthMode And *CFU_AuthBuffer And CFU_AuthBufferLength
			hData = InternetOpenUrl_(hINet, *B\URL, *CFU_AuthBuffer, CFU_AuthBufferLength, $4000100, 0)
		Else
			hData = InternetOpenUrl_(hINet, *B\URL, 0, 0, $4000100, 0)
		EndIf
		If Not hData
			CFU_LastError = #CFU_ERROR_UNABLE_TO_OPEN_URL
		Else
			If *B\BlockSize = 0
				*B\BlockSize = 8192
			EndIf
			*B\Result = AllocateMemory(*B\BlockSize)
			If Not *B\Result
				CFU_LastError = #CFU_ERROR_NO_MEMORY
			Else
				Repeat
					If InternetReadFile_(hData, *B\Result + Size, *B\BlockSize, @Bytes) = #False
						FreeMemory(*B\Result)
						*B\Result     = 0
						CFU_LastError = #CFU_ERROR_UNABLE_TO_READ_ONLINEFILE
						Break
					ElseIf *B\FileHandle = -1
						Size + Bytes
						*B\Result = ReAllocateMemory(*B\Result, Size + *B\BlockSize)
						If Not *B\Result
							CFU_LastError = #CFU_ERROR_NO_MEMORY
							Break
						EndIf
					Else
						If *B\StatusGadget <> -1 And IsGadget(*B\StatusGadget)
							f = ((DownloadedBytes + Bytes) / *B\FileSize) * 100
							SetGadgetState(*B\StatusGadget, f)
						EndIf
						If Bytes
							WriteData(*B\FileHandle, *B\Result, Bytes)
							DownloadedBytes + Bytes
						EndIf
					EndIf
				Until Bytes = 0
			EndIf
			If *B\Result And *B\AuthMode And *CFU_AuthBuffer And CFU_AuthBufferLength
				If CFU_AuthMode = #CFU_VERSIONINFO_AUTH Or (CFU_AuthMode & #CFU_FILEDOWNLOAD_AUTH And *B\AuthMode = #CFU_FILEDOWNLOAD_AUTH)
					FreeMemory(*CFU_AuthBuffer)
					*CFU_AuthBuffer      = 0
					CFU_AuthBufferLength = 0
					CFU_AuthMode         = #CFU_NO_AUTH
				EndIf
			EndIf
			InternetCloseHandle_(hData)
		EndIf
		InternetCloseHandle_(hINet)
	EndIf

	If *B\FileHandle <> -1
		CloseFile(*B\FileHandle)
	EndIf
	If *B\WindowHandle And *B\Message And IsWindow_(*B\WindowHandle)
		PostMessage_(*B\WindowHandle, *B\Message, *B\Result, #Null)
	EndIf
	If *B\FileHandle <> -1 And *B\Result
		FreeMemory(*B\Result)
	EndIf

	FreeMemory(*B\URL)
	FreeMemory(*B)
	CFU_LogError()
EndProcedure

Procedure CFU_CheckVersionInfo(*R._CFU_VERSION_INFO_, wparam, delete = #True)
	Protected Size, i
	;/------
	;| After Versioninfo has been downloaded (CFU_LoadVersionInfo), you can feed
	;| this procedure with the wParam-Value of the Event
	;| If anything is correct, the CFU_VERSION_INFO-Structure will be filled
	;| with the values.
	;/------

	If *R
		If Not wparam
			;No Info there, maybe an empty file?
			CFU_LastError = #CFU_ERROR_EMPTY_INFOFILE
			*R            = 0
		Else
			If PeekL(wparam) <> #CFU_IDENT
				;This is no correct Infofile!
				CFU_LastError = #CFU_ERROR_NO_INFOFILE
				*R            = 0
			Else
				;Ok, lets fill the VERSION_INFO-Structure
				Size + SizeOf(LONG)
				*R\Name           = PeekS(wparam + Size, -1, #PB_UTF8)
				Size + MemoryStringLength(wparam + Size, #PB_UTF8) + 1
				*R\Version        = PeekS(wparam + Size, -1, #PB_UTF8)
				Size + MemoryStringLength(wparam + Size, #PB_UTF8) + 1
				*R\URL            = PeekS(wparam + Size, -1, #PB_UTF8)
				Size + MemoryStringLength(wparam + Size, #PB_UTF8) + 1
				*R\FileSize       = PeekQ(wparam + Size)
				Size + SizeOf(QUAD)
				*R\ReleaseDate    = PeekL(wparam + Size)
				Size + SizeOf(LONG)
				*R\MD5            = PeekS(wparam + Size, -1, #PB_UTF8)
				Size + MemoryStringLength(wparam + Size, #PB_UTF8) + 1
				*R\ServerFileName = PeekS(wparam + Size, -1, #PB_UTF8)
				Size + MemoryStringLength(wparam + Size, #PB_UTF8) + 1
				*R\InfoLink       = PeekS(wparam + Size, -1, #PB_UTF8)
				Size + MemoryStringLength(wparam + Size, #PB_UTF8) + 1
				For i = 1 To SizeOf(_CFU_VERSION_INFO_\Text) / SizeOf(STRING)
					*R\Text[i - 1] = PeekS(wparam + Size, -1, #PB_UTF8)
					Size + MemoryStringLength(wparam + Size, #PB_UTF8) + 1
				Next i
				For i = 1 To SizeOf(_CFU_VERSION_INFO_\Number) / SizeOf(LONG)
					*R\Number[i - 1] = PeekL(wparam + Size)
					Size + SizeOf(LONG)
				Next i
				;Check, if there is a description
				If MemorySize(wparam) - Size > 0
					*R\Description    = AllocateMemory(MemorySize(wparam) - Size)
					CopyMemory(wparam + Size, *R\Description, MemorySize(wparam) - Size)
				Else
					*R\Description = 0
				EndIf
			EndIf
		EndIf
	EndIf

	If wparam And delete
		;Normaly delete here, maybe someone wants to delete it later
		FreeMemory(wparam)
	EndIf
	CFU_LogError()

	ProcedureReturn *R
EndProcedure

Procedure CFU_LoadVersionInfo(Url.s, WindowHandle, Message, UserName.s = "", Password.s = "", AuthMode = #CFU_NO_AUTH)
	Protected T, i, a$, b$, *B
	Protected *TV._CFU_THREAD_VALUES_ = AllocateMemory(SizeOf(_CFU_THREAD_VALUES_))
	;/------
	;| Through CFU_LoadVersionInfo, you start the thread, which tries to
	;| load the online versioninfo.
	;| When the thread has finished, it will send the Message [Message] to [WindowHandle]
	;| with wparam as Buffer.
	;| This Buffer should then be sent to CFU_CheckVersionInfo(), to get the correct values.
	;|
	;| New since V1.03:
	;| Username and Password for basic htaccess secured sites.
	;| AuthMode is a combination of:
	;| AuthMode = #CFU_NO_AUTH:           Don't use Authorization
	;| AuthMode = #CFU_VERSIONINFO_AUTH:  Use Authorization when loading versioninfo
	;| AuthMode = #CFU_FILEDOWNLOAD_AUTH: Use Authorization when loading patchfile
	;/------

	*TV\URL = AllocateMemory(StringByteLength(Url) + SizeOf(CHARACTER))
	PokeS(*TV\URL, Url)
	CFU_AuthBufferLength = 0
	If *CFU_AuthBuffer
		FreeMemory(*CFU_AuthBuffer)
	EndIf
	If AuthMode > #CFU_NO_AUTH And UserName
		CFU_AuthMode = AuthMode
		*B           = AllocateMemory(StringByteLength(UserName + ":" + Password) + 1)
		PokeS(*B, UserName + ":" + Password, -1, #PB_Ascii)
		i  = (Len(UserName) + Len(Password)) * 2
		b$ = Space(i)
		Base64Encoder(*B, MemorySize(*B), @b$, i * SizeOf(CHARACTER))
		a$ = "Authorization: Basic " + PeekS(@b$, -1, #PB_Ascii)
		FreeMemory(*B)
		*CFU_AuthBuffer = AllocateMemory(StringByteLength(a$) + SizeOf(CHARACTER))
		PokeS(*CFU_AuthBuffer, a$)
		CFU_AuthBufferLength = Len(a$)
	Else
		CFU_AuthMode = #CFU_NO_AUTH
	EndIf
	*TV\WindowHandle = WindowHandle
	*TV\Message      = Message
	*TV\BlockSize    = 0
	*TV\AuthMode     = CFU_AuthMode & #CFU_VERSIONINFO_AUTH
	*TV\FileHandle   = -1
	*TV\StatusGadget = -1
	T                = CreateThread(@CFU_GetHTTPFile(), *TV)
	If Not T
		FreeMemory(*TV\URL)
		FreeMemory(*TV)
		CFU_LastError = #CFU_ERROR_CANT_CREATE_THREAD
	EndIf
	CFU_LogError()

	ProcedureReturn T
EndProcedure

Procedure CFU_LoadInternetFile(Url.s, DestinationFile.s, WindowHandle, Message, FileSize, StatusbarGadget = -1, BlockSize = $10000)
	Protected T, FID
	Protected *TV._CFU_THREAD_VALUES_
	;/------
	;| This procedure will download the patchfile to the destination
	;/------

	FID = CreateFile(#PB_Any, DestinationFile)
	If Not FID
		CFU_LastError = #CFU_ERROR_CANT_CREATE_FILE
	Else
		*TV              = AllocateMemory(SizeOf(_CFU_THREAD_VALUES_))
		*TV\URL          = AllocateMemory(StringByteLength(Url) + SizeOf(CHARACTER))
		PokeS(*TV\URL, Url)
		*TV\WindowHandle = WindowHandle
		*TV\Message      = Message
		*TV\BlockSize    = BlockSize
		*TV\AuthMode     = CFU_AuthMode & #CFU_FILEDOWNLOAD_AUTH
		*TV\FileHandle   = FID
		*TV\FileSize     = FileSize
		*TV\StatusGadget = StatusbarGadget
		T                = CreateThread(@CFU_GetHTTPFile(), *TV)
		If Not T
			FreeMemory(*TV\URL)
			FreeMemory(*TV)
			CloseFile(FID)
			CFU_LastError = #CFU_ERROR_CANT_CREATE_THREAD
		EndIf
	EndIf
	CFU_LogError()

	ProcedureReturn T
EndProcedure

DataSection
	;Mozilla/4.0 (compatible; ST)
	GVI_Agent:
	Data.l $697A6F4D, $2F616C6C, $20302E34, $6D6F6328, $69746170, $3B656C62, $29545320
	Data.b 0
EndDataSection
And here an example:

Code: Select all

;/-------------
;| Example for the
;| CheckForUpdates.pbi
;|
;| Should be easy to understand
;| and also easy to integrate
;| in own applications.
;|
;| PB >= 4.0 (no DEMO)
;|
;| ©HeX0R 2009
;/-------------

XIncludeFile "CheckForUpdates.pbi"

InitNetwork()

#URL_OF_INFOFILE = "http://h3x0r.ath.cx/Sonstiges/example.dat"

;Our VersionInfo Variable
Global VI._CFU_VERSION_INFO_

;Two new Events
Enumeration #WM_USER + 2
	#EVENT_VERSION_INFO_RECEIVED
	#EVENT_DOWNLOAD_FINISHED
EndEnumeration

Enumeration
	#Window_Main
	;
	;If you want to use the OpenWindow_NewUpdate-Procedure in your own prog
	;you have to integrate the following Windowconstant into your main window enumeration:
	#Window_NewUpdate
EndEnumeration

Enumeration
	#Button_Start_Check
	#Editor_0
	;
	;If you want to use the OpenWindow_NewUpdate-Procedure in your own prog
	;you have to integrate the following Gadgetconstants into your main gadget enumeration:
	#Button_Update_Load
	#Button_Update_Link
	#Button_Update_Close
	#ProgressBar_Update
EndEnumeration

Procedure.s ByteRechner(dSize.d)
	Protected Namen = $4B4D4754, i = 24, C.c, Result.s = "0"

	If dSize > 0.0
		While dSize > 1024 And i >= 0
			dSize / 1024
			C = (Namen >> i) & $FF
			i - 8
		Wend
		Result = StrD(dSize, 2) + " " + Chr(C) + "Byte"
	EndIf
	ProcedureReturn Result
EndProcedure

;/ New Update found Window Example
Procedure OpenWindow_NewUpdate(Parent = 0)
	Protected Gadget

	OpenWindow(#Window_NewUpdate, 0, 0, 300, 350, "New update found!", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_WindowCentered, Parent)
	CompilerIf #PB_Compiler_Version < 430
	CreateGadgetList(WindowID(#Window_NewUpdate))
	CompilerEndIf
	TextGadget(#PB_Any, 5, 5, 100, 20, "Name:")
	StringGadget(#PB_Any, 105, 3, 190, 22, VI\Name, #PB_String_ReadOnly)
	TextGadget(#PB_Any, 5, 30, 100, 20, "Version:")
	StringGadget(#PB_Any, 105, 28, 190, 22, VI\Version, #PB_String_ReadOnly)
	TextGadget(#PB_Any, 5, 55, 100, 20, "ReleaseDate:")
	StringGadget(#PB_Any, 105, 53, 190, 22, FormatDate("%dd.%mm.%yyyy %hh:%ii", VI\ReleaseDate), #PB_String_ReadOnly)
	TextGadget(#PB_Any, 5, 80, 100, 20, "FileSize:")
	StringGadget(#PB_Any, 105, 78, 190, 22, ByteRechner(VI\FileSize), #PB_String_ReadOnly)
	TextGadget(#PB_Any, 5, 105, 100, 20, "Info:")
	Gadget = EditorGadget(#PB_Any, 5, 130, 290, 165, #PB_Editor_ReadOnly)
	ButtonGadget(#Button_Update_Load, 5, 300, 70, 22, "Download", #PB_Button_Default)
	If Left(LCase(VI\InfoLink), 7) = "http://"
		ButtonGadget(#Button_Update_Link, 115, 300, 70, 22, "Info")
	EndIf
	ButtonGadget(#Button_Update_Close, 225, 300, 70, 22, "Close")
	ProgressBarGadget(#ProgressBar_Update, 5, 325, 290, 20, 0, 100)

	;Activate WordWrap in EditorGadget
	SendMessage_(GadgetID(Gadget), #EM_SETTARGETDEVICE, 0, 0)
	;Check, if there is any Description
	If VI\Description
		CFU_Editor_Load(Gadget, VI\Description)
		FreeMemory(VI\Description)
	EndIf
EndProcedure

Procedure MyCallBack(Window, Msg, wparam, lparam)
	Protected Result = #PB_ProcessPureBasicEvents

	;Here are our two new Window-events
	;those Messages get sent through PostMessage, so you will see
	;them also in your mainloop.
	;But i decided to do it inside the callback because of two reasons:
	;
	; 1.) EventwParam() will be obsolete and removed someday (maybe)
	; 2.) There is a bug, when user moves the window:
	;     http://www.purebasic.fr/english/viewtopic.php?t=36027
	;     So it is possible, that messages never will arrive
	
	Select Msg

		Case #EVENT_VERSION_INFO_RECEIVED
			If CFU_CheckVersionInfo(@VI, wParam)
				;Ok, we have some info!
				;Now we should check if MV\Version is newer then our Version
				;But as long as this is just an example, we go on and show the window

				OpenWindow_NewUpdate(WindowID(#Window_Main))
			EndIf

		Case #EVENT_DOWNLOAD_FINISHED
			If wParam
				AddGadgetItem(#Editor_0, -1, "Download finished!, File should be here:")
				AddGadgetItem(#Editor_0, -1, GetTemporaryDirectory() + VI\ServerFileName)
				;Check integrity of downloaded file
				If MD5FileFingerprint(GetTemporaryDirectory() + VI\ServerFileName) = VI\MD5
					;Now run Patch (well, this time, this is no patch)
					RunProgram(GetTemporaryDirectory() + VI\ServerFileName)
					;Send close message to own Program
					SendMessage_(WindowID(#Window_Main), #WM_CLOSE, #Null, #Null)
				Else
					AddGadgetItem(#Editor_0, -1, "File seems to be corrupted!")
				EndIf
			Else
				AddGadgetItem(#Editor_0, -1, "Something went wrong with the download!")
			EndIf
			DisableGadget(#Button_Update_Load, 0)

	EndSelect

	ProcedureReturn Result
EndProcedure

Procedure main()

	OpenWindow(#Window_Main, 0, 0, 400, 400, "VersionCheck-Test", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_ScreenCentered)
	CompilerIf #PB_Compiler_Version < 430
	CreateGadgetList(WindowID(#Window_Main))
	CompilerEndIf
	SetWindowCallback(@MyCallBack(), #Window_Main)
	ButtonGadget(#Button_Start_Check, 140, 5, 120, 22, "Start Check Now")
	EditorGadget(#Editor_0, 5, 35, 390, 360)

	;Main Loop
	Repeat
		Select WaitWindowEvent()
			Case #PB_Event_CloseWindow

				Select EventWindow()
					Case #Window_Main
						Break
					Case #Window_NewUpdate
						CloseWindow(#Window_NewUpdate)
				EndSelect

			Case #PB_Event_Gadget

				Select EventGadget()
					Case #Button_Start_Check
						;Start the Check-Thread
						CFU_LoadVersionInfo(#URL_OF_INFOFILE, WindowID(#Window_Main), #EVENT_VERSION_INFO_RECEIVED)
						;We will get informed through a #EVENT_VERSION_INFO_RECEIVED Event, when info download is finished
						
						;Integrate those three buttons into your main eventloop:
					Case #Button_Update_Link
						RunProgram(VI\InfoLink)
					Case #Button_Update_Close
						CloseWindow(#Window_NewUpdate)
					Case #Button_Update_Load
						;Start the download
						If CFU_LoadInternetFile(VI\URL, GetTemporaryDirectory() + VI\ServerFileName, WindowID(#Window_Main), #EVENT_DOWNLOAD_FINISHED, VI\FileSize, #ProgressBar_Update)
							;We will get informed through a #EVENT_DOWNLOAD_FINISHED Event, when file download is finished
							DisableGadget(#Button_Update_Load, 1)
						EndIf
				EndSelect

		EndSelect

	ForEver

EndProcedure

main()
End

If you start the example and click the download-button, you will receive the UpdateInfo-Creat0r-Tool.
With this you can create your own info-files and can upload them via ftp (and also the patchfile).

Image

It should be selfexplaining.

Have Fun
Last edited by HeX0R on Tue Jun 01, 2010 8:32 pm, edited 4 times in total.
User avatar
idle
Always Here
Always Here
Posts: 5844
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Post by idle »

looks good
User avatar
HeX0R
Addict
Addict
Posts: 1189
Joined: Mon Sep 20, 2004 7:12 am
Location: Hell

Post by HeX0R »

Small Update of the include (and also the Creat0r)
User avatar
HeX0R
Addict
Addict
Posts: 1189
Joined: Mon Sep 20, 2004 7:12 am
Location: Hell

Post by HeX0R »

Here is a Little Helper, which should make it really easy to use the include:

[Edit]
Removed Helper, i think the callback-method is the cleaner way.
Last edited by HeX0R on Tue Jun 01, 2010 8:34 pm, edited 3 times in total.
SFSxOI
Addict
Addict
Posts: 2970
Joined: Sat Dec 31, 2005 5:24 pm
Location: Where ya would never look.....

Post by SFSxOI »

looks great! Thank You :)

can it be used with a log in and password?
User avatar
HeX0R
Addict
Addict
Posts: 1189
Joined: Mon Sep 20, 2004 7:12 am
Location: Hell

Post by HeX0R »

Hmm... what would be the sense of reaching an update-info-file and/or patch only via a secured site?
Thalius
Enthusiast
Enthusiast
Posts: 711
Joined: Thu Jul 17, 2003 4:15 pm
Contact:

Post by Thalius »

to have a legit reaosn to nag the user with a password requester ofc ! :lol:
"In 3D there is never enough Time to do Things right,
but there's always enough Time to make them *look* right."
"psssst! i steal signatures... don't tell anyone! ;)"
rsts
Addict
Addict
Posts: 2736
Joined: Wed Aug 24, 2005 8:39 am
Location: Southwest OH - USA

Post by rsts »

Maybe like for an update to PureBasic?

cheers
SFSxOI
Addict
Addict
Posts: 2970
Joined: Sat Dec 31, 2005 5:24 pm
Location: Where ya would never look.....

Post by SFSxOI »

HeX0R wrote:Hmm... what would be the sense of reaching an update-info-file and/or patch only via a secured site?
Oh, a few things come to mind; To make sure a user is who they say they are and are entitled to the software? Like maybe for a commercial product that one would purchase? Or for restricting or limiting distribution to a certain group or groups like in beta testing multiple products that are served from the same server but are intended for different groups depending on which product they are testing? Maybe ?

Good work though. I like it. Thank You :)
User avatar
HeX0R
Addict
Addict
Posts: 1189
Joined: Mon Sep 20, 2004 7:12 am
Location: Hell

Post by HeX0R »

O.k., that seems to make sense, so i tried to implement it.

Now you can use it also to get through htaccess-secured sites.

Here is an example (http://h3x0r.ath.cx/hidden/example.dat is only available through authorization):

Code: Select all

;/-------------
;| Example for the
;| CheckForUpdates.pbi
;|
;| Should be easy to understand
;| and also easy to integrate
;| in own applications.
;|
;| PB >= 4.0 (no DEMO)
;|
;| ©HeX0R 2009
;|
;| UPDATE !!21.05.2009!!
;| Now with auto url-detection
;| in Update-Description
;|
;| UPDATE !!17.09.2009!!
;| Updatewindow has its own
;| Callback
;|
;/-------------

XIncludeFile "..\CheckForUpdates.pbi"

InitNetwork()

#URL_OF_INFOFILE = "http://h3x0r.ath.cx/hidden/example.dat"

;Our VersionInfo Variable
;-Integrate (Add Global variable)
Global VI._CFU_VERSION_INFO_

;-Integrate (Add new Events)
Enumeration #WM_APP + 2
	#EVENT_VERSION_INFO_RECEIVED
	#EVENT_DOWNLOAD_FINISHED
EndEnumeration

Enumeration
	#Window_Main
	;
	;-Integrate (Add Window Constant)
	;If you want to use the OpenWindow_NewUpdate-Procedure in your own prog
	;you have to integrate the following Windowconstant into your main window enumeration:
	#Window_NewUpdate
EndEnumeration

Enumeration
	#Button_Start_Check
	#Editor_0
	;
	;-Integrate (Add Gadget Constants)
	;If you want to use the OpenWindow_NewUpdate-Procedure in your own prog
	;you have to integrate the following Gadgetconstants into your main gadget enumeration:
	#Button_Update_Load
	#Button_Update_Link
	#Button_Update_Close
	#Editor_Update_Description
	#ProgressBar_Update
EndEnumeration

;-Integrate following two Procedures
Procedure.s ByteRechner(dSize.d)
	Protected Namen = $4B4D4754, i = 24, C.c, Result.s = "0"

	If dSize > 0.0
		While dSize > 1024 And i >= 0
			dSize / 1024
			C = (Namen >> i) & $FF
			i - 8
		Wend
		Result = StrD(dSize, 2) + " " + Chr(C) + "Byte"
	EndIf
	ProcedureReturn Result
EndProcedure

Procedure NewUpdateWindow_CallBack(Window, Msg, wparam, lparam)
	Protected StringBuffer.s, *el.ENLINK, txt.TEXTRANGE, w, h

	Select Msg
		Case #WM_NOTIFY
			*el = lParam
			If *el\nmhdr\idFrom = #Editor_Update_Description
				If *el\nmhdr\code = #EN_LINK
					If *el\msg = #WM_LBUTTONDOWN
						StringBuffer   = Space(1024)
						txt\chrg\cpMin = *el\chrg\cpMin
						txt\chrg\cpMax = *el\chrg\cpMax
						txt\lpstrText  = @StringBuffer
						SendMessage_(*el\nmhdr\hwndFrom, #EM_GETTEXTRANGE, 0, txt)
						If StringBuffer
							If Left(LCase(StringBuffer), 3) = "www"
								StringBuffer = "http://" + StringBuffer
							EndIf
							RunProgram(StringBuffer)
						EndIf
					EndIf
				EndIf
			EndIf
		
	EndSelect
	
	ProcedureReturn #PB_ProcessPureBasicEvents
EndProcedure

;/ New Update found Window Example
Procedure OpenWindow_NewUpdate(Parent = 0)
	Protected i

	OpenWindow(#Window_NewUpdate, 0, 0, 300, 350, "New update found!", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_WindowCentered, Parent)
	CompilerIf #PB_Compiler_Version < 430
	CreateGadgetList(WindowID(#Window_NewUpdate))
	CompilerEndIf
	SetWindowCallback(@NewUpdateWindow_CallBack(), #Window_NewUpdate)
	TextGadget(#PB_Any, 5, 5, 100, 20, "Name:")
	StringGadget(#PB_Any, 105, 3, 190, 22, VI\Name, #PB_String_ReadOnly)
	TextGadget(#PB_Any, 5, 30, 100, 20, "Version:")
	StringGadget(#PB_Any, 105, 28, 190, 22, VI\Version, #PB_String_ReadOnly)
	TextGadget(#PB_Any, 5, 55, 100, 20, "ReleaseDate:")
	StringGadget(#PB_Any, 105, 53, 190, 22, FormatDate("%dd.%mm.%yyyy %hh:%ii", VI\ReleaseDate), #PB_String_ReadOnly)
	TextGadget(#PB_Any, 5, 80, 100, 20, "FileSize:")
	StringGadget(#PB_Any, 105, 78, 190, 22, ByteRechner(VI\FileSize), #PB_String_ReadOnly)
	TextGadget(#PB_Any, 5, 105, 100, 20, "Info:")
	EditorGadget(#Editor_Update_Description, 5, 130, 290, 165, #PB_Editor_ReadOnly)
	ButtonGadget(#Button_Update_Load, 5, 300, 70, 22, "Download", #PB_Button_Default)
	If Left(LCase(VI\InfoLink), 7) = "http://"
		ButtonGadget(#Button_Update_Link, 115, 300, 70, 22, "Info")
	EndIf
	ButtonGadget(#Button_Update_Close, 225, 300, 70, 22, "Close")
	ProgressBarGadget(#ProgressBar_Update, 5, 325, 290, 20, 0, 100)

	;Activate WordWrap in EditorGadget
	SendMessage_(GadgetID(#Editor_Update_Description), #EM_SETTARGETDEVICE, 0, 0)
	;Enable Auto URL-Detection
	i = SendMessage_(GadgetID(#Editor_Update_Description), #EM_GETEVENTMASK, 0, 0)
	SendMessage_(GadgetID(#Editor_Update_Description), #EM_SETEVENTMASK, 0, i | #ENM_LINK)
	SendMessage_(GadgetID(#Editor_Update_Description), #EM_AUTOURLDETECT, #True, 0)
	;Check, if there is any Description
	If VI\Description
		CFU_Editor_Load(#Editor_Update_Description, VI\Description)
		FreeMemory(VI\Description)
	EndIf
EndProcedure

Procedure MyCallBack(Window, Msg, wparam, lparam)
	Protected Result = #PB_ProcessPureBasicEvents, StringBuffer.s, *el.ENLINK, txt.TEXTRANGE

	;Here are our two new Window-events
	;those Messages get sent through PostMessage, so you will see
	;them also in your mainloop.
	;But i decided to do it inside the callback because of two reasons:
	;
	; 1.) EventwParam() will be obsolete and removed someday (maybe)
	; 2.) There is a bug, when user moves the window:
	;     http://www.purebasic.fr/english/viewtopic.php?t=36027
	;     So it is possible, that messages never will arrive

	Select Msg
		;-Integrate (Callback)

		Case #EVENT_VERSION_INFO_RECEIVED
			If CFU_CheckVersionInfo(@VI, wParam)
				;Ok, we have some info!
				;Now we should check if MV\Version is newer then our Version
				;But as long as this is just an example, we go on and show the window

				OpenWindow_NewUpdate(WindowID(#Window_Main))
			EndIf

		Case #EVENT_DOWNLOAD_FINISHED
			If wParam
				AddGadgetItem(#Editor_0, -1, "Download finished!, File should be here:")
				AddGadgetItem(#Editor_0, -1, GetTemporaryDirectory() + VI\ServerFileName)
				;Check integrity of downloaded file
				If MD5FileFingerprint(GetTemporaryDirectory() + VI\ServerFileName) = VI\MD5
					;Now run Patch (well, this time, this is no patch)
					RunProgram(GetTemporaryDirectory() + VI\ServerFileName)
					;Send close message to own Program
					SendMessage_(WindowID(#Window_Main), #WM_CLOSE, #Null, #Null)
				Else
					AddGadgetItem(#Editor_0, -1, "File seems to be corrupted!")
				EndIf
			Else
				AddGadgetItem(#Editor_0, -1, "Something went wrong with the download!")
			EndIf
			DisableGadget(#Button_Update_Load, 0)

	EndSelect

	ProcedureReturn Result
EndProcedure

Procedure main()

	OpenWindow(#Window_Main, 0, 0, 400, 400, "VersionCheck-Test", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_ScreenCentered)
	CompilerIf #PB_Compiler_Version < 430
	CreateGadgetList(WindowID(#Window_Main))
	CompilerEndIf
	SetWindowCallback(@MyCallBack(), #Window_Main)
	ButtonGadget(#Button_Start_Check, 140, 5, 120, 22, "Start Check Now")
	EditorGadget(#Editor_0, 5, 35, 390, 360)

	;Main Loop
	Repeat
		Select WaitWindowEvent()
			Case #PB_Event_CloseWindow

				Select EventWindow()
					Case #Window_Main
						Break
						;-Integrate (CloseWindow)
					Case #Window_NewUpdate
						CloseWindow(#Window_NewUpdate)
				EndSelect

			Case #PB_Event_Gadget

				Select EventGadget()
					Case #Button_Start_Check
						;Start the Check-Thread
						CFU_LoadVersionInfo(#URL_OF_INFOFILE, WindowID(#Window_Main), #EVENT_VERSION_INFO_RECEIVED, "affen", "kopf", #CFU_VERSIONINFO_AUTH)
						;We will get informed through a #EVENT_VERSION_INFO_RECEIVED Event, when info download is finished

						;-Integrate (EventGadget)
						;Integrate those three buttons into your main eventloop:
					Case #Button_Update_Link
						RunProgram(VI\InfoLink)
					Case #Button_Update_Close
						CloseWindow(#Window_NewUpdate)
					Case #Button_Update_Load
						;Start the download
						If CFU_LoadInternetFile(VI\URL, GetTemporaryDirectory() + VI\ServerFileName, WindowID(#Window_Main), #EVENT_DOWNLOAD_FINISHED, VI\FileSize, #ProgressBar_Update)
							;We will get informed through a #EVENT_DOWNLOAD_FINISHED Event, when file download is finished
							DisableGadget(#Button_Update_Load, 1)
						EndIf
				EndSelect

		EndSelect

	ForEver

EndProcedure

main()
End
User avatar
HeX0R
Addict
Addict
Posts: 1189
Joined: Mon Sep 20, 2004 7:12 am
Location: Hell

Re: [Win]Online Update for your apps.

Post by HeX0R »

There were a small long time overseen bug in the main include,
which could lead to missing data when receiving the updateinfo (not the updatefile itself).

Normally won't happen (only if your updateinfo file is really huge), but i posted it anyway.
SFSxOI
Addict
Addict
Posts: 2970
Joined: Sat Dec 31, 2005 5:24 pm
Location: Where ya would never look.....

Re: [Win]Online Update for your apps.

Post by SFSxOI »

Thanks HeXOR,

I been using the heck out of it, didn't encounter that bug tho as the file is pretty small so that might be he reason. Thanks for the update. :)
The advantage of a 64 bit operating system over a 32 bit operating system comes down to only being twice the headache.
Post Reply