Inventory script

This Autoit script can be used to read the per client XML files that get copied to a network share once wpkg.js has finished running (I use the 'Execute after' option) [1]

To have the latest version of wpkg.xml copied to the server, you can either:

    use WPKG client, and copy this file as "Execute after" action,
    in config.xml, configure wpkg.js to save this file on the server. 

The script also reads in the 'master' package XML files. It then compares the 'revision=' of the master to the clients 'revision=' and flags up any differences that can be emailed to whoever using the third party tool mailsend.exe [2]. It also creates a per client pc CSV file in a subdirectory on the server share where the client XML files are saved (\invent\) and concats all the CSV files into a single all.csv file.

The output of a client invent XML files looks like:

PC NAME,DATE OF XML FILE,DESCRIPTION,PACKAGE ID NAME,PRIORITY,INSTALLED REVISION,MASTER XML REVISION,UPGRADE/DOWNGRADE/BLANK=NOTHING REQUIRED
RECOVERY,09/05/2011_15:37,7-Zip,7zip,10,920,920,
RECOVERY,09/05/2011_15:37,Activation MS Office 2010,msoffice2010-activation,71,1,1,
RECOVERY,09/05/2011_15:37,Adobe Acrobat 9,acrobat9,17,944,944,
RECOVERY,09/05/2011_15:37,Adobe Shockwave Player 11,shockwave,50,11.5.7.601,11.5.7.609,Upgrade required

Autoit script

Compile and cmd exe

#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_Outfile=wpkg_invent_chk.exe
#AutoIt3Wrapper_Change2CUI=y
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****
#include <File.au3>
#include <Array.au3>
#include <Date.au3>
#include <Constants.au3>

; ***********************************************************************************************************************
;
; Script to look at the WPKG generated XML files that get copied to the \win-info\wpkg-xml\ share after WPKG has run on
; each pc and compare the installed packages to the master set of SML files on \wpkg\server\WPKG-1.1.2\packages\.  A per
; pc csv file is created showing what is installed on that pc.  Another csv file is made concatting all the indervidual
; csv files.  This 'all' csv file is then read in and all pakcages that require up or downgrading are noted and if required,
; can be emailed to whoever is specified as the first argument to the exe of this script.
;
;C. Mortimer May 2011
;
; ***********************************************************************************************************************


Dim $master_or_computer_path, $master_or_computer_invent_array, $invent

$pc_xml_path = EnvGet("LOGONSERVER") & "\win-info\wpkg-xml"
$master_xml_path = EnvGet("LOGONSERVER") & "\wpkg\server\WPKG-1.1.2\packages"

If @Compiled = 1 Then
	If $CmdLine[0] > 0 Then
		If ($CmdLine[1] = "/?") OR ($CmdLine[1] = "?") Then
			ConsoleWrite("Usage" & @CRLF & @CRLF & "Program to produce a report file per machine name showing the installed WPKG packages as stored in the auto created WPKG files in" & _
					" " & $pc_xml_path & "\[machine name].xml.  These files are copied from \\[machine name]\c$\windows\temp\wpkg.xml to " & $pc_xml_path & " when ever the wpkg.js scipt has " & _
					"finished running.  The report files are stored in " & $pc_xml_path & "\invent\.  " & $pc_xml_path & "\invent\all.csv is a concat of all the csv files.  If this script is " & _
					"is passed an argument of an email address, a report of any machines with packages out of sync with the master WPKG XML file (" & $master_xml_path & "\ can be emailed to whoever." & _
					@CRLF & @CRLF & "C. Mortimer May 2011" & @CRLF)
			Exit
		ElseIf StringInStr($CmdLine[1], "@") Then
			$email_address = $CmdLine[1]
		Else
			$email_address = ""
		EndIf
	Else
		$email_address = ""
	EndIf
Else
	$email_address = "foo@gmail.com"
EndIf

$csv_title_txt = "PC Name,Date of XML File,Description,Package ID Name,Priority,Installed Revision,Master XML Revision,Upgrade/Downgrade/blank=Nothing Required"
$downgrade_string = "Downgrade required"
$upgrade_string = "Upgrade required"
$mailsend = EnvGet("LOGONSERVER") & "\netlogon\bin\mailsend.exe"
$s_domain = "somewhere"
$s_SmtpServer = "smtp.somewhere.cam.ac.uk"
$s_FromName = ""
$s_FromAddress = "wibble@gmail.com"
$s_ToAddress = "-t " & $email_address
$s_Subject = "PC's with missmatched WPKG programs"

If FileExists($pc_xml_path) = 0 Then
	ConsoleWrite("Error. Unable to read files in " & $pc_xml_path & ", exiting" & @CRLF)
	Exit
ElseIf FileExists($master_xml_path) = 0 Then
	ConsoleWrite("Error. Unable to read files in " & $master_xml_path & ", exiting" & @CRLF)
	Exit
EndIf

If Not FileExists($pc_xml_path & "\invent\*") Then DirCreate($pc_xml_path & "\invent\")

; Lets work out the current master revision numbers for each id
$invent = ""
_make_xml_array($master_xml_path)

; Now work out the list of installed packages for each machine generated xml file...
$filedelete = FileDelete($pc_xml_path & "\invent\*.csv")
;~ ConsoleWrite ("deleted files = " & $filedelete & @CRLF)
_make_xml_array($pc_xml_path)

;Write to the console a list of csv files in $pc_xml_path & "\invent\
$csv_files = _FileListToArray($pc_xml_path & "\invent\", "*.csv", 1)
ConsoleWrite(@CRLF)
For $r = 1 To $csv_files[0] Step 1
	ConsoleWrite(StringUpper($csv_files[$r]) & @CRLF)
Next
ConsoleWrite(@CRLF & "The list of CSV files above are the files in " & $pc_xml_path & "\invent\ showing the inventory of the WPKG programs installed.  " & _
		"'ALL.CSV' is a concatination of all the indervidual CSV files." & @CRLF & @CRLF)

; Now sort out the mailing of the machines needing up/downgrading
$error_list = $csv_title_txt & @CRLF
If FileExists($pc_xml_path & "\invent\all.csv") Then

	Dim $all_csv_file
	_FileReadToArray($pc_xml_path & "\invent\all.csv", $all_csv_file)
	$all_error_occurance_id = _ArrayFindAll($all_csv_file, "grade required", "", "", "", 1)
	If @error < 1 Then
		For $o = 0 To _ArrayMaxIndex($all_error_occurance_id) Step 1
			$error_list = $error_list & $all_csv_file[$all_error_occurance_id[$o]] & @CRLF
		Next
		ConsoleWrite("Machines and packages that differ from the Master XML files = " & @CRLF & @CRLF & $error_list & @CRLF)

		If StringLen($email_address) > 0 Then
			If FileExists($mailsend) Then

				$wpkg_invent_email_file = EnvGet("temp") & "\wpkg_invent_email.txt"
				$write2file = FileOpen($wpkg_invent_email_file, 2)
				FileWriteLine($write2file, $error_list)
				FileClose($write2file)

				$sendmail = $mailsend & " -v -d " & $s_domain & " -smtp " & $s_SmtpServer & " " & $s_ToAddress & " -f " & $s_FromAddress & _
						" -sub """ & $s_Subject & """ +cc +bc -m """ & $wpkg_invent_email_file & """,text/plain,i"
				$std_run_reg = Run($sendmail, "", "", $STDERR_CHILD + $STDOUT_CHILD)

				$q = 1
				$timewatch = TimerInit() ; Make a note of the current time
				While $q = 1

					$std_out_read = StdoutRead($std_run_reg)
					$std_err_read = StderrRead($std_run_reg)
					If (StringLen($std_out_read) > 0) OR (StringLen($std_err_read) > 0) Then
						ConsoleWrite(@CRLF & @CRLF & "Email sent" & @CRLF)
						$q = 2
					EndIf
					If TimerDiff($timewatch) > 5000 Then
						$q = 2
						ConsoleWrite(@CRLF & @CRLF & "Error sending email :(" & @CRLF)
					EndIf
				WEnd
				Sleep(1000)
				FileDelete($wpkg_invent_email_file)
			Else
				ConsoleWrite("The file '" & EnvGet("LOGONSERVER") & "\netlogon\bin\mailsend.exe' is missing, unable to send any email" & @CRLF)
			EndIf
		EndIf
	Else
		ConsoleWrite("All of the pcs are up to date with their WPKG packages" & @CRLF)
	EndIf
Else
	ConsoleWrite("The file '" & $pc_xml_path & "\invent\all.csv' is missing, unable to send any email or write to STDOUT the list of out of date pcs" & @CRLF)
EndIf

; =======================================================================================================================

Func _make_xml_array($master_or_computer_path)

	$xml_files = _FileListToArray($master_or_computer_path, "*.xml", 1) ; make an array listing each master xml file
;~ 	_ArrayDisplay ($xml_files)

	For $p = 1 To $xml_files[0] Step 1 ; for each xml file...
		If $master_or_computer_path = $pc_xml_path Then
;~ 			ConsoleWrite ("pc = " & StringReplace($xml_files[$p], ".xml", "") & @CRLF)
			$invent = ""
		EndIf

		Dim $xml_file_contents
		_FileReadToArray($master_or_computer_path & "\" & $xml_files[$p], $xml_file_contents) ; create an array, $xml_file_contents listing its contents
;~ 		_ArrayDisplay ($xml_file_contents)
		$occurance_id = _ArrayFindAll($xml_file_contents, " revision=", "", "", "", 1) ; in the array $xml_file_contents, return all the indices (lines) containing ' revision='
;~ 		_ArrayDisplay ($occurance_id)
		For $o = 0 To _ArrayMaxIndex($occurance_id) Step 1
			$id_line = FileReadLine($master_or_computer_path & "\" & $xml_files[$p], $occurance_id[$o])

			Do ; replace tabs with a single space
				$id_line = StringReplace($id_line, "	", " ")
			Until @extended = 0

			Do ; replace double space with a single space
				$id_line = StringReplace($id_line, "  ", " ")
			Until @extended = 0

			Do ; replace a space at the start of the line with null
				If StringLeft($id_line, 1) = " " Then $id_line = StringTrimLeft($id_line, 1)
			Until StringLeft($id_line, 1) <> " "

			Do ; replace <wpkg> at the start of the line with null
				If StringLeft($id_line, 6) = "<wpkg>" Then $id_line = StringTrimLeft($id_line, 6)
			Until StringLeft($id_line, 6) <> "<wpkg>"
;~ 			ConsoleWrite ("$id_line = " & $id_line & @CRLF)

			; Now make an array and run a For loop to replace the %version%/%shortversion%/%subversion% int he XML file with it's variable value
			Dim $_aArray[3]

			$_aArray[0] = "shortversion"
			$_aArray[1] = "version"
			$_aArray[2] = "subversion"

			For $ver In $_aArray
				If StringInStr($id_line, "%" & $ver & "%") Then
;~ 						ConsoleWrite("line number = " & $occurance_id[0] & "(" & $ver & ")" & @CRLF)
					$doloop = 1
					$doloop_line_no = $occurance_id[$o]
					Do
						$find_version = FileReadLine($master_or_computer_path & "\" & $xml_files[$p], $doloop_line_no + 1) ; Read the line 1 line after the line containing ' revision=' in the xml file
						;ConsoleWrite("$find_version = " & $find_version & " on line " & $doloop_line_no & @CRLF)
						If StringInStr($find_version, "<variable name=""" & $ver & """") Then ; If this '1 line after revision=' contains $ver (%version%, %shortversion% or %subversion%...
							$find_version_split = StringSplit($find_version, '""') ; make an array of this line...
							If @error = 1 Then $find_version_split = StringSplit($find_version, '''') ; if no double quotes are used, split using single quotes.
							$find_version_value = $find_version_split[_ArraySearch($find_version_split, "value=", "", "", "", 1) + 1] ; and note the value set in the array indice one after 'value='
							;ConsoleWrite("$find_version_value = " & $find_version_value & @CRLF)
							$doloop = 2
							;ConsoleWrite($id_line & @CRLF)
							;_ArrayDisplay($find_version_split)
						EndIf
						$doloop_line_no = $doloop_line_no + 1 ; increment $doloop_line_no by one and run the Do loop again, this time looking for the next line down.
					Until $doloop = 2
					$id_line = StringReplace($id_line, "%" & $ver & "%", $find_version_value)
;~ 			ConsoleWrite("$id_line = " & $id_line & @CRLF)
				EndIf
			Next
;~ 			ConsoleWrite("$id_line = " & $id_line & @CRLF)

			If $master_or_computer_path = $master_xml_path Then
				$id_line_split = StringSplit($id_line, '""') ; split the line with double quotes as the deliminator.
				If @error = 1 Then $id_line_split = StringSplit($id_line, '''') ; if no double quotes are used, split using single quotes.
				$invent = $invent & "," & $id_line_split[_ArraySearch($id_line_split, "id=", "", "", "", 1) + 1] ;	Create a string $invent contianing the id name...
				$invent = $invent & "," & $id_line_split[_ArraySearch($id_line_split, "revision=", "", "", "", 1) + 1] ; and the revision number.
				If StringLeft($invent, 1) = "," Then $invent = StringTrimLeft($invent, 1) ; If the first character on the left if a ',' remove it.
;~ 				ConsoleWrite("$invent after reading master files = " & $invent & @CRLF)
				$master_or_computer_invent_array = StringSplit($invent, ",") ; Turn the string $invent into an array.
			ElseIf $master_or_computer_path = $pc_xml_path Then
				$id_line_split = StringSplit($id_line, '""')
;~ 				_ArrayDisplay ($id_line_split)
				$invent = $invent & StringReplace($xml_files[$p], ".xml", "") ; start creating the indevidual package csv line by obtaining the pc name...
				$time = FileGetTime($master_or_computer_path & "\" & $xml_files[$p], 0, 0) ; then the date and time the pc's xml file was created
				$invent = $invent & "," & $time[2] & "/" & $time[1] & "/" & $time[0] & "_" & $time[3] & ":" & $time[4]
				$invent = $invent & "," & $id_line_split[_ArraySearch($id_line_split, "name=", "", "", "", 1) + 1] ; then add the 'name' text...
				$id = _ArraySearch($id_line_split, "id=", "", "", "", 1)
				$invent = $invent & "," & $id_line_split[$id + 1] ; then the 'id' for the package...
				$invent = $invent & "," & $id_line_split[_ArraySearch($id_line_split, "priority=", "", "", "", 1) + 1]
				$revision = _ArraySearch($id_line_split, "revision=", "", "", "", 1)
				$invent = $invent & "," & $id_line_split[$revision + 1] ; then the revision number...
				$master_revision = $master_or_computer_invent_array[_ArraySearch($master_or_computer_invent_array, $id_line_split[$id + 1], "", "", "", 0) + 1] ; now add the revision number for the same id but from the master xml files
				$invent = $invent & "," & $master_revision

				If $id_line_split[$revision + 1] = $master_revision Then
					$invent = $invent & "," & ""
				ElseIf $id_line_split[$revision + 1] > $master_revision Then
					$invent = $invent & "," & $downgrade_string
				ElseIf $id_line_split[$revision + 1] < $master_revision Then
					$invent = $invent & "," & $upgrade_string
				EndIf

				$invent = $invent & @CRLF
			EndIf
		Next

		; Now save each output to a per machine name file and concat all of the output into one file

		If $master_or_computer_path = $pc_xml_path Then
			ConsoleWrite("Inventory of installed packages on " & StringUpper ($xml_files[$p]) & " = " & @CRLF & @CRLF & $csv_title_txt & @CRLF & $invent & @CRLF)

;~ 			ConsoleWrite ("FileReadLine (" & $pc_xml_path & "\invent\" & StringReplace($xml_files[$p], ".xml", "") & ".csv, 1)  = " & FileReadLine ($pc_xml_path & "\invent\" & StringReplace($xml_files[$p], ".xml", "") & ".csv", 1) & @CRLF)
			If FileReadLine($pc_xml_path & "\invent\" & StringReplace($xml_files[$p], ".xml", "") & ".csv", 1) = "" Then
				$save_csv = FileOpen($pc_xml_path & "\invent\" & StringReplace($xml_files[$p], ".xml", "") & ".csv", 1)
				FileWrite($save_csv, StringUpper($csv_title_txt) & @CRLF)
				FileClose($save_csv)
;~ 				ConsoleWrite ("writing title to " & $pc_xml_path & "\invent\" & StringReplace($xml_files[$p], ".xml", "") & ".csv" &  "(" & FileReadLine ($pc_xml_path & "\invent\" & StringReplace($xml_files[$p], ".xml", "") & ".csv", 1) & ")" & @CRLF)
			EndIf

			$save_csv = FileOpen($pc_xml_path & "\invent\" & StringReplace($xml_files[$p], ".xml", "") & ".csv", 1)
;~ 			ConsoleWrite ("now saving $invent to " & StringReplace($xml_files[$p], ".xml", "") & ".csv" & @CRLF)
			FileWrite($save_csv, $invent)
			FileClose($save_csv)

;~ 			ConsoleWrite ("FileReadLine (" & $pc_xml_path & "\invent\all.csv, 1) = " & FileReadLine ($pc_xml_path & "\invent\all.csv", 1) & @CRLF)
			If FileReadLine($pc_xml_path & "\invent\all.csv", 1) = "" Then
				$save_csv_all = FileOpen($pc_xml_path & "\invent\all.csv", 1)
				FileWrite($save_csv_all, StringUpper($csv_title_txt) & @CRLF)
				FileClose($save_csv_all)
;~ 				ConsoleWrite ("writing title to " & $pc_xml_path & "\invent\all.csv" & "(" & FileReadLine ($pc_xml_path & "\invent\all.csv", 1) & ")" & @CRLF)
			EndIf

			$save_csv_all = FileOpen($pc_xml_path & "\invent\all.csv", 1)
;~ 			ConsoleWrite ("now saving $invent to " & $pc_xml_path & "\invent\all.csv" & @CRLF)
			FileWrite($save_csv_all, $invent)
			FileClose($save_csv_all)
		EndIf
	Next
EndFunc   ;==>_make_xml_array