Thursday, January 31, 2008

WiX Toolkit (Windows Installer) Custom actions and conditions

Normally, for setup projects I use InnoSetup from Jordan Russel which is, put simple, the best setup creator you will find.

However, for a project I was forced to produce a Windows Installer package and used the WiX toolkit for it. Basically, after several dead-ends I was finally completely stuck. I simply wanted to have Windows Installer executing a custom action on every install of the application, regardless if it's already installed or not. If the application is being removed, my custom action shouldn't be executed.

By default, you would simply use the condition "NOT Installed" which means: If the application is not installed, execute it. Else, leave it alone. But as I said I wanted it to be execute every time somebody issues a command like MSIEXEX /I MyProduct.msi. The default condition would evaluate to FALSE (since the application is already installed) and thus, the custom action isn't started. Of course, you could pass /fa to MSIEXEC.exe but I feared this would be forgotten and thus I would get more "Nothing is working here calls".

After searching and testing for nearly two hours (and found an excellent post about custom actions and properties from Jeff Wharton) I was able to find the correct solution:

<custom action="LaunchExe" after="InstallInitialize">NOT (REMOVE="ALL")</custom>

This simply means: If the application is NOT being removed, execute the custom action.

If you know how, it's simple.

Thursday, January 24, 2008

Using caspol.exe to change .NET security policy - done right

Maybe you know CASPOL.exe to modify or add your own security policy for the .NET Framework.

Most examples you will find on the internet will simply add a code group to the configuration and most people use it this way: Upon each installation, CASPOL.EXE is exeucted.

What most people not realize is that CASPOL does not remove the old group when adding a new one with the same name. See this screenshot:

This is no broken installation, for .NET everything is fine even if 100+ groups with the same name would exist. However, for the user this looks like a bug so he will call support. To make things even worse: When you have changed the group membership of your custom group later on you will end up with two groups with completely different membership conditions.

To avoid this, I developed the following batch file that you can use and customize. The basic trick is to first list all available groups and if the script finds one that has the same name, it will be deleted.

For more information about CasPol.exe, see MSDN.

Enjoy!


@echo off
Rem .NET Framework 2.0 CasPol.exe batch by TeX HeX
Rem http://texhex.blogspot.com
Rem Version 1.0

Rem Set this to the name of the group you want to create
SET GROUP=Testing123
Rem Set this to the description your group should have
SET GROUPDESC=Just testing group

SET CASPOL=%WINDIR%\Microsoft.Net\Framework\v2.0.50727\caspol.exe
SET ERRLVL=9

echo ---- Setting prompt off ----
%caspol% -polchgprompt off

Rem Check if this group exists already
echo ---- Check group existence ----
%caspol% -m -ld|find /C /I "%GROUP%"
IF NOT ERRORLEVEL 1 GOTO DELETE_GROUP
GOTO CREATE_GROUP


Rem Deleting old group (two times to make sure that we do not have one left over)
:delete_group
echo ---- Removing old group ----
CASPOL% -m -remgroup "%GROUP%"
CASPOL% -m -remgroup "%GROUP%" >NUL


:create_group
echo ---- Creating group ----
REM %CASPOL% -m -addgroup All_Code -url "\*" -zone MyComputer FullTrust -name "%GROUP%"
REM %CASPOL% -m -addgroup All_Code -strong -file c:\arg.dll -noname -noversion FullTrust -name "%GROUP%" -description "%GROUPDESC%"

%CASPOL% -m -addgroup All_Code -zone MyComputer FullTrust -name "%GROUP%" -description "%GROUPDESC%"
SET ERRLVL=%ERRORLEVEL%

echo Result is %ERRLVL%

Rem Patch prompting again
echo ---- Setting prompt on ----
%caspol% -polchgprompt on



Rem Now check the result
IF %ERRLVL% EQU 0 (
echo "All fine!"
exit 0
) ELSE (
echo "Error!"
exit -1
)




Monday, January 21, 2008

New Snom Auto update script

Since Snom does now offer the v7 firmware officially, it was time to update my Snom update script. That's because the transition to v7 must be done if a very special way (first firmware 6.5.15, then linux 3.38 and then the special update firmware).

Before you can use this script, you need to create several folders to include the parts you need to do the update:
  • /v6: includes the 6.5.15 firmware which is needed to update to v7 (snom3X0-6.5.15-SIP-j.bin)
  • /v6ux: includes the 3.38 Linux system also needed for v7 but can only be installed once the firmware is 6.5.15 (snom3X0-3.38-l.bin)
  • v6to7: this folder includes the special firmware that will update v6 to v7 while keeping all settings (snom3X0-update6to7-7.1.30-bf.bin)
  • v7: the normal firmware files for phones that already have v7 installed
All these files can be downloaded from http://wiki.snom.com/Firmware/V7/Update_Description.

To test it, you can simply use an URL like .../snom-firmware.asp?UA=Mozilla/4.0+(compatible;+snom320-SIP+6.5.15;+snom320+jffs2+v3.36;+snom320+linux+3.38) which will tell the script you would like to use the User agent of a Snom 320.

Once all these files are in place, you might use the following ASP script:




# Auto Update Skript
# Coypright (C) 2007-2008 TeX HeX
# http://texhex.blogspot.com
# All Rights Reserved

<%
'Example URLs (for testing):
'.../snom-firmware.asp?UA=Mozilla/4.0+(compatible;+snom320-SIP+6.5.10;+snom320+jffs2+v3.36;+snom320+linux+3.25)
'.../snom-firmware.asp?UA=Mozilla/4.0+(compatible;+snom320-SIP+6.5.15;+snom320+jffs2+v3.36;+snom320+linux+3.38)
'../snom-firmware.asp?UA=Mozilla/4.0+(compatible;+snom320-SIP+7.1.30)
'Definition of download URLs
URL_BASE="http://my-server.internal.company.com/snom"

URL_BASE_V6=URL_BASE & "/v6"
URL_BASE_V6_UX=URL_BASE & "/v6ux"
URL_BASE_V6TO7=URL_BASE & "/v6to7"
URL_BASE_V7=URL_BASE & "/v7"

URL_V6_FW_300=URL_BASE_V6 & "/snom300-6.5.15-SIP-j.bin"
URL_V6_FW_320=URL_BASE_V6 & "/snom320-6.5.15-SIP-j.bin"
URL_V6_FW_360=URL_BASE_V6 & "/snom360-6.5.15-SIP-j.bin"

URL_V6_UX_300=URL_BASE_V6_UX & "/snom300-3.38-l.bin"
URL_V6_UX_320=URL_BASE_V6_UX & "/snom320-3.38-l.bin"
URL_V6_UX_360=URL_BASE_V6_UX & "/snom360-3.38-l.bin"

URL_V6TO7_FW_300=URL_BASE_V6TO7 & "/snom300-from6to7-7.1.30-bf.bin"
URL_V6TO7_FW_320=URL_BASE_V6TO7 & "/snom320-from6to7-7.1.30-bf.bin"
URL_V6TO7_FW_360=URL_BASE_V6TO7 & "/snom360-from6to7-7.1.30-bf.bin"

URL_V7_FW_300=URL_BASE_V7 & "/snom300-7.1.30-SIP-f.bin"
URL_V7_FW_320=URL_BASE_V7 & "/snom320-7.1.30-SIP-f.bin"
URL_V7_FW_360=URL_BASE_V7 & "/snom360-7.1.30-SIP-f.bin"
'''URL_V7_FW_370=URL_BASE_V7 & "/snom370-7.1.30-SIP-f.bin"

'----------STOP EDITING-------------------
Dim CRLF
CRLF=chr(13) + chr(10)

Function Log(Text)
Response.Write("# " & Text)
Response.Write(CRLF)
End function

Function SendOutFirmware_V6
Log("Sending v6 Firmware")
Call SendOutFirmware(URL_V6_FW_300,URL_V6_FW_320,URL_V6_FW_360)
End Function

Function SendOutLinux_V6
Log("Sending v6 Linux")
Call SendOutFirmware(URL_V6_UX_300,URL_V6_UX_320,URL_V6_UX_360)
End Function

Function SendOutFirmware_V6to7
Log("Sending v6 to v7 Firmware")
Call SendOutFirmware(URL_V6TO7_FW_300,URL_V6TO7_FW_320,URL_V6TO7_FW_360)
End Function

Function SendOutFirmware_V7
Log("Sending v7 Firmware")
Call SendOutFirmware(URL_V7_FW_300,URL_V7_FW_320,URL_V7_FW_360)
End Function


Function SendOutFirmware(url300, url320, url360)
'Only write out "firmware:"" tag if it's in the night (21:00 = 9 PM, 05:00 = 5 AM)
if ( (hour(now())>=21 or hour(now())<=5) or sNow="1" ) then
'if true then
Dim sURL

pos=InStr(sUA,"snom300")
if pos>0 then
sURL=url300
end if

pos=InStr(sUA,"snom320")
if pos>0 then
sURL=url320
end if

pos=InStr(sUA,"snom360")
if pos>0 then
sURL=url360
end if

if len(sURL)>0 then
Response.Write("firmware: " + sURL)
Response.Write(CRLF)
else
Log("Unable to get download URL!")
end if
else
'Debug output, I don't think Snoms can actually undestand this :)
Log("Firmware download disabled: It's not in the night! We have now: " + cstr(now()) )
end if
End Function


Dim sUA
sUA=Request.QueryString("UA")
if len(sUA)=0 then
Log("Parameter UA is empty, defaulting to HTTP_USER_AGENT")
sUA=Request.ServerVariables("HTTP_USER_AGENT")
Response.Write(CRLF)
end if

Dim sNow
sNow=Request.QueryString("Now")

'Debug output, just for reference
Log("Parameter [USER AGENT] is --> " + sUA)

Log("Parameter [Now] (direct update) is --> " + sNow)


'Try to find out the current firmware version
Dim iPosStart,iPosEnd,sTmp
iPosStart=0
iPosEnd=0
sTmp=""

'Start searching
Log("Searching firmware version")
iPosStart=InStr(sUA,"-SIP")
if iPosStart>0 then
sTmp=Right(sUA,len(sUA)- (iPosStart+4)) '+4 to cut the "SIP " part
Call Log("Version sniffing - part 1: " + sTmp)

'now search for next ;
iPosEnd=InStr(sTmp,";")
if iPosEnd>0 then
sTmp=left(sTmp,iPosEnd-1)
Call Log("Version: " + sTmp)
else
'maybe a new 7.x phone - search for ")"
iPosEnd=InStr(sTmp,")")
if iPosEnd>0 then
sTmp=left(sTmp,iPosEnd-1)
Call Log("Version: " + sTmp)
end if
end if
end if

'Log sTmp

Dim sCurVersion
sCurVersion=""

'Now check if the version can be found!
if iPosStart<=0 or iPosEnd<=0 then
'No version info found, send to last v6 release!
Call Log("No version info found!")
Call SendOutFirmware_V6()
else
'Okay, we have a version! If this is 6.5.15 we need to send out the newest linux!
sCurVersion=sTmp
Call Log("Found version: " & sCurVersion)

if CInt(left(sCurVersion,1))<=6 and sCurVersion<>"6.5.15" then
Call Log("Version is 6 or below but not 6.5.15!")
Call SendOutFirmware_V6()
else
'Is this 6.5.15?
If sCurVersion="6.5.15" then
'Do we need to do a linux update?
iPosStart=0
iPosEnd=0
sTmp=""

Log("Searching linux version")
iPosStart=InStr(sUA," linux")
if iPosStart>0 then
sTmp=Right(sUA,len(sUA)- (iPosStart+6)) '+4 to cut the " linux" part
Call Log("Version sniffing - part 1: " + sTmp)

'now search for )
iPosEnd=InStr(sTmp,")")
if iPosEnd>0 then
sTmp=left(sTmp,iPosEnd-1)
Call Log("Version: " + sTmp)
end if
end if

if iPosStart<=0 or iPosEnd<=0 then
Log("Unable to find linux version!")
else
if sTmp<>"3.38" then
Log("Version to old, sending new linux!")
Call SendOutLinux_V6()
else
'Firmware is 6.5.15 and linux version okay!
'-> Update to 7.1.30!
Call SendOutFirmware_V6to7()
end if
end if
else
'Firmware is not 6.5.15/UX 3.38, more like 7+
Call SendOutFirmware_V7()
end if
end if
end if



Log("Done!")

%>





Monday, January 14, 2008

SOME of your network connections are down

SOME of your network connections are down...

Wednesday, January 9, 2008

Lost in translation: ASC16-UTF8-1252

As soon as you start to deal with any string data, you need to make sure of which character encoding type this string is. The most commonly used are ASCII, ISO-8859-1, Windows-1252 and the two Unicode encodings UTF-8 and UTF-16.

Normally, you can fully ignore which character encoding you are using but as soon as you need to communicate with an external source, or need to make sure in which format an external data source is, you need to know the encoding.

Here is the list of the most commonly used encodings:

ASCII (http://www.asciitable.com/) is very old but still very widely used. Every character consists of one byte but only the values 0 – 127 are defined which makes it 7 bit. The characters between 128 and 255 (8 bit) are so called "Extended ASCII" and which characters they map can be defined in a codepage. This means: If you are using data that contains ASCII characters about 127, you need to make sure that sender and receiver use the same codepage.

ISO-8859-1 Latin-1 (http://en.wikipedia.org/wiki/ISO/IEC_8859-1) is very widely used in the internet since it's the default encoding for the "text/" MIME type. Basically it's a codepage that maps 0 – 127 to the same characters like ASCII, 128 and above to several characters of the Latin alphabet.

Windows-1252 (http://en.wikipedia.org/wiki/Windows-1252) is based on ISO-8859-1 and is still the most used codepage for Windows. It differs in the 0x80 to 0x9F range which contains non-printable characters in ISO-8859-1 but printable characters on Windows-1252.

UTF-8 (http://en.wikipedia.org/wiki/Utf-8) is a Unicode character encoding that again maps 0 – 127 like ASCII but can also display all Unicode characters. UTF-8 is a variable-length encoding which means the values 0-255 is mapped to one byte, like in "Extended ASCII", but UTF-8 can use two, three or even four bytes per character when needed. Because of this variable-length encoding and the backward compatibility with ASCII, it is the encoding you should use.

UTF-16 (http://en.wikipedia.org/wiki/UTF-16) is also a Unicode character encoding but maps 0 – 127 differently from ASCII and uses only two or four bytes per character. Because of this it requires more data space and should only be used if you need to use. Use UTF-8 in any other case.