1@echo off
2setlocal ENABLEDELAYEDEXPANSION
3
4rem Use real applications or testing stub for testing
5
6set MSIEXEC=msiexec
7set WMIC=wmic
8set CSCRIPT=cscript
9
10if not "%CFENGINE_MSIEXEC_TEST%"=="" (
11  set MSIEXEC=%CFENGINE_MSIEXEC_TEST% msiexec
12  set WMIC=%CFENGINE_MSIEXEC_TEST% wmic
13  set CSCRIPT=%CFENGINE_MSIEXEC_TEST% cscript
14)
15
16rem choose a function to execute
17
18if "%1"=="supports-api-version" echo 1
19if "%1"=="get-package-data"   goto :get_package_data_list
20if "%1"=="list-installed"     goto :list_installed
21if "%1"=="list-updates"       rem not implemented
22if "%1"=="list-updates-local" rem not implemented
23if "%1"=="repo-install"       rem not implemented
24if "%1"=="file-install"       goto :file_install_list
25if "%1"=="remove"             goto :remove_list
26
27goto :EOF
28
29
30
31rem Reads all stdin lines, for each line which starts with
32rem "File=" call the next function
33:get_package_data_list
34  for /F "tokens=*" %%a in ('more') do (
35    rem Assign for-loop %%a variable to "normal" %_q% variable to extract substrings
36    rem via %name:~begin,length% expansion (negative value means length of string - value)
37    set "_q=%%a"
38    rem * Use "Delayed Expansion" of variables (surround them with ! instead of %)
39    if "!_q:~0,5!"=="File=" call :get_package_data_one "!_q:~5!"
40  )
41goto :EOF
42
43
44rem Choose one of two following functions to call based on whether file exists or not
45:get_package_data_one
46  rem This function called with an argument in quotes, so:
47  rem use %1 when you need value in quotes,
48  rem use %~1 when you need without
49  if not exist %1 call :get_package_data_repo %1
50  if     exist %1 call :get_package_data_file %1
51goto :EOF
52
53
54rem Print package information for an existing file
55:get_package_data_file
56  echo PackageType=file
57  rem %~dp0 expands to drive and path of current script
58  rem TODO: if name is multi-line, print "Name=" only once
59  for /f "usebackq delims=" %%b in (`%CSCRIPT% /nologo "%~dp0\WiRunSQL.vbs" %1
60    "select Value from Property where Property = 'ProductName'"`
61  ) do echo Name=%%b
62  for /f "usebackq delims=" %%b in (`%CSCRIPT% /nologo "%~dp0\WiRunSQL.vbs" %1
63    "select Value from Property where Property = 'ProductVersion'"`
64  ) do echo Version=%%b
65goto :EOF
66
67
68rem If file does not exist - assume it's a repo
69:get_package_data_repo
70  echo PackageType=repo
71  echo Name=%~1
72goto :EOF
73
74
75
76rem Call vbs script to read installed software from registry
77:list_installed
78  %CSCRIPT% /nologo "%~dp0\msiexec-list.vbs"
79goto :EOF
80
81
82
83rem Reads all stdin lines, for each line which starts with "File=" call the next function
84:file_install_list
85  for /F "tokens=*" %%a in ('more') do (
86    set "_q=%%a"
87    if "!_q:~0,5!"=="File=" call :file_install_one "!_q:~5!"
88  )
89goto :EOF
90
91
92rem Install this file if it exists
93:file_install_one
94  if not exist %1 (
95    echo ErrorMessage=File %1 not found!
96    goto :EOF
97  )
98
99  set log_dir="\cfengine_package_logs\"
100  if not exist %log_dir% (
101    mkdir %log_dir%
102  )
103  for /F "delims=" %%i in (%1) do @set basename="%%~ni"
104  REM %log_dir:"=% replaces quotes with nothing, otherwise you get two double-quotes which causes failures
105  set log_file="%log_dir:"=%%basename:"=%_install.log"
106  %MSIEXEC% /quiet /passive /qn /norestart /l*vx %log_file% /i %1
107  if not errorlevel 0 (
108    echo ErrorMessage=msiexec.exe ErrorLevel was %ErrorLevel% for file %1 log at %log_file%
109  )
110goto :EOF
111
112
113
114rem Reads all stdin lines, calls next function for each of them
115:remove_list
116  for /F "tokens=*" %%a in ('more') do (
117    call :remove_line "%%a"
118  )
119  call :remove_one
120goto :EOF
121
122
123rem processes line of input, saves name and version, and calls
124rem next function before new block (which starts with "Name=" line)
125:remove_line
126  set "_q=%~1"
127  if "%_q:~0,5%"=="Name=" (
128    call :remove_one
129    set "_name=%_q:~5%"
130    set _ver=
131  )
132
133  if "%_q:~0,8%"=="Version=" (
134    set "_ver=%_q:~8%"
135  )
136goto :EOF
137
138
139rem Remove file or software stored in "%_name%" env variable, if it's set.
140rem If such file does not exist - remove an installed program with such name
141:remove_one
142  if "%_name%"=="" goto :EOF
143
144  if exist "%_name%" (
145    call :remove_file "%_name%"
146    goto :EOF
147  )
148
149  if "%_ver%"=="" (
150    set "_condition=name='%_name%'"
151  ) else (
152    set "_condition=name='%_name%' and version='%_ver%'"
153  )
154
155  rem Characters > and & chars in for expression must be escaped
156  for /f "delims=" %%a in (
157      '%WMIC% product where "%_condition%" get LocalPackage /value 2^>^&1'
158  ) do (
159    set "_q=%%a"
160    if "!_q:~0,13!"=="LocalPackage=" call :remove_file "!_q:~13!"
161  )
162goto :EOF
163
164
165rem Remove software from MSI package which name is passed as argument
166:remove_file
167  %MSIEXEC% /quiet /passive /qn /norestart /x %1
168  rem TODO options, error checking
169goto :EOF
170