'Check Dr.Backup compressed Storage Usage from Local Catalog 'Update version 6/10/2011 - MDR '6/10/2011 - bug fix on plugins '4/11/2012 - support long filenames '12/5/2012 - backup destination support '3/18/2013 - added statistics by extension '3/27/2013 - adapted to run the report on a per backupset basis to handle large catalog '3/29/2013 - added switch to write ONLY deletes to spreadsheet file and stats '3/29/2013 - added switch to mark files in catalog as delete for GUI removal '3/30/2013 - added FTP and other options set by configurable parameters - add a prompting UI '4/9/2013 - added workbench catalog option for databaseoverride '7/11/2013 - tweaked the display of stats when only deletes are required '4/22/2014 - standard pre-amble for handle any default script processor '8/4/2015 - file system object nulled out bug repair for FTP; specified location regsvr32.exe '10/20/2016 - handle on node long filenames (excluding those on a NAS) '10/28/2016 - if avaialble, use PSEXEC to run with SYSTEM credentials '1/7/2017 - enabled use of SQL Server catalog plus additional testing '2/27/2018 - moved file system object creation out of main loop to help with resource issues '11/15/2018 - increased maximum number of file extensions supported in summary table to 100,000 '02/28/2019 - option to limit the number of files for delete processing '04/03/2019 - optimized WMI object use '03/25/2020 - added catalogID to end of spreadsheet '08/14/2020 - updated long file legacy handling and suppressed statistics for vhd and vhdx image files '08/15/2020 - do not mark files in \Rsplit\ as deleted. '02/19/2021 - Changes to support new delete ui '05/10/2021 - Changes to support new restore ui - restoring DELETE files CONST RBSProvider = "Dr.Backup" CONST Version = "(v3.0.6)" Const adUseServer = 2 CONST MAXDeletedR = -1 'maximum number of remote files to market for deletion before aborting CE (-1 is umlimited) CONST MAXVAL = 100000 'maximum number of extension types tracked CONST LongFileLength = 255 'reports files in catalog with more than this number of characters CONST SHOWDELETEDMIRROR = FALSE 'display delete statistics for mirror drive [FALSE] CONST SHOWDELETEDCLOUD = TRUE 'display delete statistics for cloud storage [FALSE] CONST YESNO = 4 'Customizable features. defaults setup here '------------------------------------------------------------------------------------------------------------------ DIM FTPPORT 'global port definition for ftp WRITEDELETESONLY = FALSE 'Only write deleted files to spreadsheet [False] CHECKFORDELETES = TRUE 'Enumerates file system to detect deleted files and report [True] ShowDeletedExts = FALSE 'Show stats of files that we think are deleted? [False] MARKDELETESINCATALOG = FALSE 'mark deleted files in catalog so they can be removed with GUI [FALSE] ADDDELETECOLUMN = TRUE 'add a delete column to catalog table so we can handle delete marking and cloud storage reporting DaysBeforeDelete = 0 'delay from time backed up to time eligible to be marked for delete in catalog [0] SENDTOFTP = FALSE 'send the catalog and stats file via FTP to Dr.Backup for examination [FALSE] FTPPATH = "/CatalogExports" 'catalog ftp folder - upload only FTPHOST = "rbs1.drbackup.net" 'ftp host FTPPORT = 121 'ftp port FTPUSERNAME = "drbackup" 'ftp user FTPPASSWORD = "b@ckup!" 'ftp password DATABASEOVERRIDE = FALSE 'use temp location for database DIM PSEXEC : PSEXEC = FALSE 'run using SYSTEM credentials DIM MyDrive DIM MyPath DIM NULLSub 'special query conditional for MSAccess or SQL db query string DIM objUtilFSO '------------------------------------------------------------------------------------------------------------------ 'statistics counters REDIM extstr(maxval) 'file extension string array REDIM extcount(maxval) 'number of times extension string appears REDIM extbytes(maxval) 'uncompressed bytes running counter REDIM extcbytes(maxval) 'compressed bytes running counter DIM extlist 'string of previously seen extensions and index DIM NextExt 'next free slot in array NextExt = 1 'initialize for extension tracking string extlist = ";NONE=0;" 'string formatted list of extensions seen and index into array for data storage extstr(0) = "NONE" 'display text string of extension found extcount(0) = 0 'number of times extension seen extbytes(0) = 0 'number of raw bytes in files with this extension extcbytes(0) = 0 'number of compressed bytes in files with this extension FirstPass = True 'logical to tell us to write header to output file Dim FullFilename 'filename being inspected. could be longer than 255 characters LongFiles = 0 'counter to track number of long files in catalog for warning message. CatalogRecords = 0 'total records in the catalog. sum of all backupset records. Set objUtilFSO = CreateObject( "Scripting.FileSystemObject" ) 'allocate file object for main loop '** standard pre-amble for all interactive scripts ** 'get OS volume Set OSobj = CreateObject("Scripting.FileSystemObject") OSfolder = OSobj.getspecialfolder(0) objStartFolder = Left(OSfolder, 3) 'root of search file tree OSVolume = Left(OSfolder, 1) '** added to elevate privs - initial call will have zero arguments ** If WScript.Arguments.count = 0 AND NewOS() Then Set objShell = CreateObject("Shell.Application") 'Pass a bogus argument with leading blank space, say [ uac] objShell.ShellExecute "wscript.exe", Chr(34) & _ WScript.ScriptFullName & Chr(34) & " uac", "", "runas", 1 Wscript.Quit End If 'find location of 32-bit script processing program - in syswow64 on 64-bit machines 'define location of regsvrexe here as well ScriptEXE = OSFolder & "\SYSTEM32\Cscript.exe" RegsvrEXE = OSFolder & "\SYSTEM32\Regsvr32.exe" Set objFSOexe = CreateObject("Scripting.FileSystemObject") If objFSOexe.FileExists(OSFolder & "\SYSWOW64\Cscript.exe") Then '64-bit system found. switch to 32-bit cscript/regsvr32 ScriptEXE = OSFolder & "\SYSWOW64\Cscript.exe" RegsvrEXE = OSFolder & "\SYSWOW64\Regsvr32.exe" End If '** if we have UAC escalated count will be 1, otherwise 0 on legacy machines ** '** force use of cscript so we get console display ** If Wscript.Arguments.Count < 2 Then strPath = Wscript.ScriptFullName 'use psexec? Response = Msgbox("Use SYSTEM account? (PSEXEC) ", YESNO + 256, "PSEXEC-SYSTEM") If Response = VbYes Then PSEXEC = TRUE If Instr(ScriptEXE, "SYSWOW64") <> 0 Then '64-bit machine If objFSOexe.FileExists(OSVolume & ":\Program Files (x86)\Remote Backup\Custom\psexec.exe") Then ScriptEXE = """" & OSVolume & ":\Program Files (x86)\Remote Backup\Custom\psexec.exe"" -sid " & ScriptEXE & "" Else PSEXEC = False End If Else '32-bit machine If objFSOexe.FileExists(OSVolume & ":\Program Files\Remote Backup\Custom\psexec.exe") Then ScriptEXE = """" & OSVolume & ":\Program Files\Remote Backup\Custom\psexec.exe"" -sid " & ScriptEXE & " " Else PSEXEC = False End If End If Else PSEXEC = FALSE End If If PSEXEC Then strCommand = "%comspec% /k " & """" & ScriptEXE & " //nologo """ & strPath & """" & " 1 2" & """" Else strCommand = "%comspec% /k " & ScriptEXE & " //nologo """ & strPath & """" & " 1 2" End If 'Wscript.Echo "strCommand = " & strCommand Set objShell = CreateObject("Wscript.Shell") objShell.Run(strCommand), 1, True Wscript.Quit End If 'we need to trap errors and continue due to SQL query try-retry logic problems 'unfrotunately, sometimes this can cause trouble On Error Goto 0 Err.Clear '** Let's get started ** Wscript.StdOut.WriteLine(RBSProvider & " - Export Catalog Analysis " & Version & VbCrLF) Wscript.StdOut.WriteLine("Start : " & Now() & VbCr) On Error Goto 0 Err.Clear 'Get catalog location from Registry Const HKEY_LOCAL_MACHINE = &H80000002 strComputer = "." strKeyPath = "SOFTWARE\Quantum Tech, Inc.\Remote Backup\Settings" strEntryName = "ClientDB" Set objReg = GetObject("winmgmts:" _ & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\default:StdRegProv") status = objReg.GetStringValue( HKEY_LOCAL_MACHINE, strKeyPath, strEntryName, strValue) 'Sometimes security products block access to the registry. If so, lookup will fail. 'Give the user an opportunity to specify location of backup catalog manually rather than hard fail. If (status <> 0) or (Err.Number <> 0) Then strValue = InputBox("Unable to lookup database location in registry." & VbCrLf & "Please enter path below: ","Locate Backup Catalog", "C:\Program Files\Remote Backup") If Len(strValue) = 0 Then Wscript.StdOut.WriteLine("Cancelled. Script aborted." & VbCr) Wscript.Quit End If Err.Clear End If 'Assume Access database - generally in C:\Program Files (x86)\Remote Backup\backup.mdb DBPath = strValue Database = strValue & "\" & "backup.mdb" 'INITIALIZE DATABASE 'grab Database type field from registry strEntryName = "DBType" err.clear Set objReg = GetObject("winmgmts:" _ & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\default:StdRegProv") status = objReg.GetStringValue( HKEY_LOCAL_MACHINE, strKeyPath, strEntryName, strValue) If (status = 0) and (Err.Number = 0) Then 'dbtype field found. DBType = strValue Else DBType = "0" 'default to MS Access db End If If DBType <> "0" Then 'Get SQL Server from registry strEntryName = "DBServer" Err.Clear Set objReg = GetObject("winmgmts:" _ & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\default:StdRegProv") status = objReg.GetStringValue( HKEY_LOCAL_MACHINE, strKeyPath, strEntryName, strValue) If (status = 0) and (Err.Number = 0) Then DBServer = strValue WScript.StdOut.Writeline (VbCrLf& "Processing SQL Server Database: " & DBServer & " (RBBACKUP)" & VbCrLf) SQLDb = True NullSub = "IsNull([LongPath],[Filename])" 'complex query string for MSSQL Else WScript.StdOut.Writeline ("Registry error: " & err.description & " Error Code: " & err.number & VbCr) WScript.StdOut.Writeline ("Unable to access SQL Server in registry. Quitting." & VbCr) Wscript.Quit End If Else 'Setup for MS Access database WScript.StdOut.Writeline (VbCrLf & "Processing Access Database: " & Database & VbCrLf) SQLDb = False NULLSub = "IIf(IsNull([LongPath]),[Filename],[LongPath])" 'complex query string for MS Access End If 'End setup and database initialization - begin script function here 'Catalog export using disk restore utility (on workbench) overrides default catalog location CONST DISKRESTOREDEFAULTLOC = "C:\Documents and Settings\Administrator\Local Settings\Temp" If DATABASEOVERRIDE = TRUE Then Database = DISKRESTOREDEFAULTLOC & "\" & "backup.mdb" strValue = DISKRESTOREDEFAULTLOC End If 'Setup to permit FTP transfer of catalog using ftp library FTPDLL = DBPath & "\Custom\" & "ChilkatFTP.dll" 'troubleshooting prompt to dislay working database location 'Wscript.StdOut.WriteLine("Processing Database: " & Database) 'FTP option enabled? 'Let's prompt for configurable parameters Response = Msgbox("Send report to " & RBSProvider & " for analysis (FTP)?", YESNO + 256, "Send report") If Response = VbYes Then SENDTOFTP = TRUE FTPPASSWORD = InputBox("FTP Password? : ","Access Permission", FTPPASSWORD) Else SENDTOFTP = FALSE End If 'Disk cleanup mode active? Response = Msgbox("Deleted file cleanup?", YESNO + 256, "Catalog maintenance") If Response = VbYes Then 'various options we need to promtp for here: 'Let's prompt for delete status only Response = Msgbox("Log only deleted file stats?", YESNO, "Catalog maintenance") If Response = VbYes Then WRITEDELETESONLY = TRUE ShowDeletedExts = TRUE 'added this logic in to force display of deleted stats if only doing deleted files Else WRITEDELETESONLY = FALSE End If 'Let's prompt for marking files in catalog for gui delete Response = Msgbox("Mark deleted files for removal?", YESNO, "Catalog maintenance") If Response = VbYes Then MARKDELETESINCATALOG = TRUE PromptDay = InputBox("Delay days? (0-3660): ","Delete File Restrictions", 0) If CInt(PromptDay) > 3660 or CInt(PromptDay) < 0 Then DaysBeforeDelete = 3660 Wscript.StdOut.Writeline("Files deleted less than " & DaySBeforeDelete & " will be skipped.") Else DaysBeforeDelete = Cint(PromptDay) End If Else MARKDELETESINCATALOG = FALSE End If End If 'initialize stats counters CompressedSizeR = 0 OriginalSizeR = 0 FileCountR = 0 CompressedSizeM = 0 OriginalSizeM = 0 FileCountM = 0 CompressedSizeC = 0 OriginalSizeC = 0 FileCountC = 0 CompressedSizeP = 0 OriginalSizeP = 0 FileCountP = 0 CompressedSizeT = 0 OriginalSizeT = 0 FileCountT = 0 'Master record count with sum of all backup sets RecordCount = 0 'Deleted data stats FilesDeletedR = 0 FilesDeletedSizeR = 0 FilesDeleteOriginalSizeR = 0 FilesDeletedM = 0 FilesDeletedSizeM = 0 FilesDeleteOriginalSizeM = 0 FilesDeletedC = 0 FilesDeletedSizeC = 0 FilesDeleteOriginalSizeC = 0 FilesDeletedP = 0 FilesDeletedSizeP = 0 FilesDeleteOriginalSizeP = 0 strValue = DBPATH 'this is needed due to poor coding in original script. ForWriting = 2 'setup output file for catalog spreadsheet CatalogFile = strValue & "\Custom\catalog.csv" Wscript.StdOut.Writeline("Catalog Export = " & CatalogFile) Set objFSO = CreateObject("Scripting.FileSystemObject") Set objFile = objFSO.CreateTextFile(CatalogFile, ForWriting) 'CONNECT TO DATABASE (Main Connection) If SQLDb = FALSE Then Err.Clear Set objConn = CreateObject("ADODB.Connection") strConnect = "Provider=MSDASQL; DRIVER={Microsoft Access Driver (*.mdb)};DBQ=" & DataBase & ";UID=admin;PWD=;" objConn.Open strConnect If Err.Number <> 0 Then Wscript.StdOut.WriteLine(VbCrLf & "Unable to Open Access Database.") Wscript.StdOut.WriteLine(VbCrLf & "Can't continue." & VbCr) Wscript.Quit End If Else Err.Clear 'SQL database Open goes here Set objConn = CreateObject("ADODB.Connection") strConnect = "Provider=SQLOLEDB;Data Source=" & DBSERVER & ";Trusted_Connection=Yes;" & _ "Initial Catalog=RBBACKUP;" objConn.CommandTimeout = 0 objConn.Open strConnect If Err.Number <> 0 Then Wscript.StdOut.WriteLine(VbCrLf & "Unable to Open SQL Database.") Wscript.StdOut.WriteLine(VbCrLf & "Can't continue." & VbCr) Wscript.Quit End If End If 'string holding storage locations - support for mirror and cloud storage StorageLoc = "" 'In order to handle catalogs with ultra large record counts, we are going to do a catalog 'export based on backupset, then total everything up. The output catalog won't be sorted, but 'that can be done in excel. 'We will do the export 1 backup set at a time for performance reasons 'Conditionally setup connect string if SQL or MS Access 'Create recordset for backupset enumeration If SQLDb = False Then 'MS access connection strConnectSetname = "Provider=MSDASQL; DRIVER={Microsoft Access Driver (*.mdb)};DBQ=" & DataBase & ";UID=admin;PWD=;" Else 'SQL connection strConnectSetname = "Provider=SQLOLEDB;Data Source=" & DBSERVER & ";Trusted_Connection=Yes;" & _ "Initial Catalog=RBBACKUP;" End If '2/19/2021 - handle delete UI changes If MARKDELETESINCATALOG = True Then On Error Resume Next Err.Clear Set conn = CreateObject("ADODB.Connection") Set backupset = CreateObject("ADODB.Recordset") conn.CommandTimeout = 0 conn.Open strConnectSetname If Err.Number <> 0 Then Wscript.StdOut.WriteLine(VbCrLf & "Unable to Open SQL Database.") Wscript.StdOut.WriteLine(VbCrLf & "Can't continue." & VbCr) Wscript.Quit End If sqlStmt = "UPDATE [SetNames] SET [DStatus] = 1, [RStatus] = 1 " backupset.open sqlStmt, conn, 3, 3 conn.Close End If Set rsSetname = CreateObject ("ADODB.Recordset") rsSetname.ActiveConnection = strConnectSetname rsSetname.CursorLocation = 3 rsSetname.CursorType = adUseServer 'Perform backup set name lookup On Error Resume Next Err.Clear sqlSetname = "Select * from SETNAMES" rsSetname.open sqlSetname If Err.Number <> 0 Then Wscript.StdOut.WriteLine(VbCrLf & "Can't get Backupset Names." & VbCr) Wscript.Quit End If On Error Goto 0 'enumerate through all backup sets in catalog Do Until rsSetname.EOF 'Wscript.Stdout.Writeline("SETID = " & rsSetname("ID")) 'Creates a resultset against established main connection Set rsFileEntry = CreateObject("ADODB.Recordset") If SQLDb = True Then '8/13/2017 - let the server do the heavy lifting on this one rsFileEntry.Cursorlocation = adUseServer End If 'Get advanced SQL query - requires BackupLocation field support sqlStmt = GetSQLQuery(2, rsSetname("ID")) 'Wscript.StdOut.Writeline("SQL: " & sqlStmt) On Error Resume Next Err.clear 'Full feature support assumed Supports117 = TRUE rsFileEntry.open sqlStmt, objConn, 3, 3 If Err.Number <> 0 Then If FirstPass = TRUE then Wscript.StdOut.Writeline("WARN: Old catalog format detected.") End If Err.clear Supports117 = FALSE sqlStmt = GetSQLQuery(1, rsSetname("ID")) 'Retry with a query that will supoprt a slightly earlier version of catalog rsFileEntry.open sqlStmt, ObjConn, 3, 3 End If 'If non-complex query fails, there is probably catalog trouble, or too many entries causing memory overflow If Err.Number <> 0 Then Wscript.StdOut.Writeline(VbCrLf & "Catalog query FAILED.") wscript.StdOut.Writeline(err.description & " (" & err.number & ")") Wscript.StdOut.WriteLine(VbCrLf & "Script aborted." & VbCr) Wscript.Quit End If On Error GoTo 0 If FirstPass = TRUE Then LocationStr = "" If Supports117 = True Then LocationStr = ",""Location""" End If objFile.Writeline """Filename"",""CompressedSize"",""OrigSize"",""DateStamp"",""SetNameID"",""SubFolder"",""FullPFolder"",""Extension"",""Deleted""" & LocationStr & ",""CatalogID""" FirstPass = False End If 'keep running counter of records in all backup sets CatalogRecords = CatalogRecords + rsFileentry.recordcount 'Wscript.StdOut.Writeline("Backupset Records: " & rsFileentry.recordcount) 'enumerate through all catalog entries for this backup set and compute stats Do Until rsFileEntry.EOF If RsFileEntry("PrepResult") = "0" Then 'Get Backuplocation(s) so we can handle separately for stats If Supports117 = True Then StorageLoc = GetBackupLocation(RsFileEntry("BackupLocation")) 'Wscript.StdOut.Writeline("StorageLoc = " & StorageLoc) Else 'if not supported in database, assume Remote server backup StorageLoc = "R" End If 'Check to see if filename exists in catalog table CleanUpLongFile = False FullFilename = "" OriginalFullFilename = "" If Len(RsFileEntry("Filename") & " ") = 1 Then 'Null filename field means this is a longpath file. Get upto 512 chars from longpath table FullFilename = RsFileEntry("Longpath").Getchunk(512) Else 'Wscript.Echo "RsFileEntry(""LPathID"") = " & RsFileEntry("LPathID") If RsFileEntry("LPathID") = "0" OR Len(RsFileEntry("LpathID") & " ") = 1 Then 'Just a simple file. Load into Fullfilename field FullFilename = RsFileEntry("Filename") OriginalFullFilename = FullFileName Else 'open up a connection to database, so we can read [LongPaths256] SET objLongConn = CreateObject("ADODB.Connection") If SQLDb = False Then 'MS access connection objLongConn.Open "Provider=MSDASQL; DRIVER={Microsoft Access Driver (*.mdb)};DBQ=" & DataBase & ";UID=admin;PWD=;" Else 'SQL connection objLongConn.Open "Provider=SQLOLEDB;Data Source=" & DBSERVER & ";Trusted_Connection=Yes;" & _ "Initial Catalog=RBBACKUP;" End If Set rsLongFileEntry = CreateObject("ADODB.Recordset") sqlLongStmt = "Select SourceFolder, SourceFile from [LongPaths256] WHERE LPathID = " & RsFileEntry("LpathID") rsLongFileEntry.open sqlLongStmt, objLongConn, 3, 3 If rsLongFileEntry.EOF = FALSE Then 'Found the record. Process it here. MyDrive = FreeDrive() 'Wscript.Echo "MyDrive = " & MyDrive MyPC = MyComputerName() 'Wscript.Echo "MyComputerName = " & MyPC MySourceFolder = rsLongFileEntry("SourceFolder") If Len(MySourceFolder) = 2 Then 'root of drive, ignore share mapping. use actual file. FullFilename = MysourceFolder & "\" & rsLongFileEntry("SourceFile") '8/14/2020 - don't forget to intialize so we dont think old style longfilename OriginalFullFilename = FullFilename Else MakeShare MySourceFolder, "DRBACKUP" 'Wscript.Echo "SourceFolder = " & rsLongFileEntry("SourceFolder") MapStatus = MapDrive(MyDrive, "\\" & MyPC & "\" & "DRBACKUP") 'Wscript.Echo "MapStatus = " & MapStatus FullFilename = MyDrive & ":\" & rsLongFileEntry("SourceFile") OriginalFullFilename = rsLongFileEntry("SourceFolder") & "\" & rsLongFileEntry("SourceFile") 'Wscript.Echo "FullFileName (Long) = " & FullFilename CleanupLongFile = TRUE End If rsLongFileEntry.Close 'rsLongFileEntry = Nothing Else Wscript.Echo "Failed to lookup long filename - some sort of database corruption/mismatch likely" 'error looking up longfilename rcord End If objLongConn.Close 'objLongConn = Nothing End If End If 'check if file exists on disk If Len(FullFilename & " ") <> 1 Then 'filename in catalog. Check disk If CHECKFORDELETES = FALSE Then 'We don't need to do any delete checks Deleted = "No" '8/15/2020 - do not include rsplit files in deleted processing. ElseIf (objUtilFSO.FileExists(Fullfilename) = TRUE) OR (Instr(Fullfilename, "\RSplit\") <> 0) Then 'Simple case. file on disk. nothing to do. Deleted = "No" Else 'Wscript.Echo "Filename does not exist. Size(" & len(Fullfilename) & ") File: " & FullFilename 'Wscript.Echo "OriginalFullFilename - Size (" & Len(OriginalFullFilename) & ") File: " & OriginalFullFilename 'Pause "Type any key to resume" 'If this is NOT a plugin, (empty or NULL string) then it is a deleted file If Len(RsFileEntry("Plugin") & " ") = 1 Then 'THIS IS NOT A PLUGIN. Plugins are skipped 'added special handling here for old style longfilenames. We cannot reliably detect file in ROOT of 'drive or share if filename length is >254 characters (total 254+3 = 257 for total path) 'so we will NOT marked as deleted in database or spreadsheet. Rather, we will mark as LONG255 If OriginalFullFilename <> "" THEN 'not an old style long filename Deleted = "Yes" '3/29/2013 - File is deleted. Let's see if we need to mark this in catalog for deletion? If MARKDELETESINCATALOG = TRUE Then Deleted = "Yes-Pending" If datediff("d", RsFileEntry("Datestamp"), date) > DaysBeforeDelete Then Deleted = "Yes" 'Mark file for subsequent delete in catalog If Supports117 = True Then 'new catalog format. use delete field. RsFileEntry("Delete") = TRUE RsFileEntry("Restore") = TRUE 'Wscript.StdOut.Writeline("Deleted - Mark Delete field: " & fullfilename) Else 'old catalog format. use restore field. RsFileEntry("Restore") = TRUE 'Wscript.StdOut.Writeline("Deleted - Mark (old) Restore field: " & fullfilename) End If RsFileEntry.Update End If End If Else 'special handling required for old style long file -> long255 Deleted = "Long255" End If 'Wscript.StdOut.Writeline("Deleted: " & fullfilename) 'update deleted statistics 'Remote Server 'Wscript.StdOut.Writeline("StorageLoc=" & StorageLoc) If Instr(StorageLoc, "R") > 0 AND Deleted <> "Long255" Then 'Remote server is a location. update deleted stats FilesDeletedR = FilesDeletedR + 1 FilesDeletedSizeR = FilesDeletedSizeR + CDbl(RsFileEntry("PrepSize")) FilesDeletedOriginalSizeR = FilesDeletedOriginalSizeR + CDbl(RsFileEntry("OrigSize")) 'abort catalog export of maximum number of remote files deleted exceeds specified limit If FilesDeletedR = MaxDeletedR Then Wscript.StdOut.Writeline(VbCrLf & VbCrLf & "Catalog Export Maintenance - ABORTED. Remote Files Marked for Delete Reached: " & FilesDeletedR & VbCrLf & "Run Delete from Server Now.") Wscript.StdOut.Writeline(VbCrLf & " Deleted File Storage (bytes) : " & FormatNumber(FilesDeletedSizeR, Default, Default, Default, GroupDigits)) Wscript.StdOut.Writeline(" Deleted File Count : " & FormatNumber(FilesDeletedR, Default, Default, Default, GroupDigits) & VbCrLf) Wscript.Quit End If End If 'Local Mirror If Instr(StorageLoc, "M") > 0 AND Deleted <> "Long255" Then 'Mirror server is a location. update deleted stats FilesDeletedM = FilesDeletedM + 1 FilesDeletedSizeM = FilesDeletedSizeM + CDbl(RsFileEntry("PrepSize")) FilesDeletedOriginalSizeM = FilesDeletedOriginalSizeM + CDbl(RsFileEntry("OrigSize")) End If 'Private Cloud Storage If Instr(StorageLoc, "C") > 0 AND Deleted <> "Long255" Then 'Cloud storage is a location. update deleted stats FilesDeletedC = FilesDeletedC + 1 FilesDeletedSizeC = FilesDeletedSizeC + CDbl(RsFileEntry("PrepSize")) FilesDeletedOriginalSizeC = FilesDeletedOriginalSizeC + CDbl(RsFileEntry("OrigSize")) End If 'Public Cloud Storage If Instr(StorageLoc, "P") > 0 AND Deleted <> "Long255" Then 'Cloud storage is a location. update deleted stats FilesDeletedP = FilesDeletedP + 1 FilesDeletedSizeP = FilesDeletedSizeP + CDbl(RsFileEntry("PrepSize")) FilesDeletedOriginalSizeP = FilesDeletedOriginalSizeP + CDbl(RsFileEntry("OrigSize")) End If Else 'This is a plugin. Ignore it as plugins are not ordinary user files. Deleted = "No" End If End If If IsObject(objUtilFSO) Then If Not objUtilFSO Is Nothing Then '''Set objUtilFSO = Nothing End If End If 'objUtilFSO = Nothing End If 'Update running stats - Maintain multiple sets of stats If Instr(StorageLoc, "R") > 0 Then 'Remote server is a location. update stats CompressedSizeR = CompressedSizeR + CDbl(rsFileEntry("PrepSize")) OriginalSizeR = OriginalSizeR + CDbl(RsFileEntry("OrigSize")) FileCountR = FileCountR + 1 End If If Instr(StorageLoc, "M") > 0 Then 'Local Mirror is a location. update stats CompressedSizeM = CompressedSizeM + CDbl(rsFileEntry("PrepSize")) OriginalSizeM = OriginalSizeM + CDbl(RsFileEntry("OrigSize")) FileCountM = FileCountM + 1 End If If Instr(StorageLoc, "C") > 0 Then 'Cloud storage is a location. update stats CompressedSizeC = CompressedSizeC + CDbl(rsFileEntry("PrepSize")) OriginalSizeC = OriginalSizeC + CDbl(RsFileEntry("OrigSize")) FileCountC = FileCountC + 1 End If If Instr(StorageLoc, "P") > 0 Then 'Public cloud storage is a location. update stats CompressedSizeP = CompressedSizeP + CDbl(rsFileEntry("PrepSize")) OriginalSizeP = OriginalSizeP + CDbl(RsFileEntry("OrigSize")) FileCountP = FileCountP + 1 End If 'summary totals updated CompressedSizeT = CompressedSizeT + CDbl(rsFileEntry("PrepSize")) OriginalSizeT = OriginalSizeT + CDbl(RsFileEntry("OrigSize")) FileCountT = FileCountT + 1 'Keep track if we have long filenames - may be troublesome to ultimately restore unless to orig path If Len(OriginalFullFilename) > LongFileLength OR OriginalFullFilename = "" Then LongFiles = Longfiles + 1 End If 'If version 11.7 or later, then display storage locations of data If Supports117 = True Then LocationStr = ",""" & StorageLoc & """" Else LocationStr = "" End If '3/29/2013 - Include processing to write ONLY deletes to the spreadsheet if requested If WRITEDELETESONLY = TRUE AND Ucase(Deleted) = "NO" Then 'file is not deleted, do not put into spreadsheet Else If OriginalFullFilename <> "" Then SpreadsheetView = OriginalFullFilename Else SpreadsheetView = FullFilename End If 'Wscript.Echo "Record Started = " & RecordCount On Error Resume Next Err.Clear objFile.Writeline """" & Spreadsheetview & """,""" & _ CDbl(RsFileEntry("PrepSize")) & """,""" & CDbl(RsFileentry("OrigSize")) & """,""" & _ RsFileEntry("DateStamp") & """,""" & RsFileEntry("SetnameID") & """,""" & _ RsFileEntry("SubFolder") & """,""" & RsFileEntry("FullPFolder") & """,""" & _ GetExtension(UnicodeToAscii(Spreadsheetview)) & """,""" & _ Deleted & """" & LocationStr & "," & RsFileEntry("ID") 'the writeline function won't handle unicode characters. Normalize them with call to unicodetoascii function if needed If Err.Number <> 0 Then objFile.Writeline """" & UnicodeToAscii(Spreadsheetview) & """,""" & _ CDbl(RsFileEntry("PrepSize")) & """,""" & CDbl(RsFileentry("OrigSize")) & """,""" & _ RsFileEntry("DateStamp") & """,""" & RsFileEntry("SetnameID") & """,""" & _ RsFileEntry("SubFolder") & """,""" & RsFileEntry("FullPFolder") & """,""" & _ GetExtension(UnicodeToAscii(Spreadsheetview)) & """,""" & _ Deleted & """" & LocationStr & "," & RsFileEntry("ID") End If On Error GoTo 0 'Wscript.Echo "Record Finished = " & RecordCount UpdateExtStats GetExtension(UnicodeToAscii(Spreadsheetview)), CDbl(RsFileentry("OrigSize")), CDbl(RsFileEntry("PrepSize")), Deleted End If Else 'Wscript.StdOut.Writeline("Plugin found (" & RsFileEntry("ID") & ") : " & fullfilename ) End If RecordCount = RecordCount + 1 'since catalog could be large, provide feedback here If (RecordCount Mod 100) = 0 Then Wscript.StdOut.Write("Processing : " & Recordcount & "/" & CatalogRecords & VbCr) End If 'Sanity check here. Let's clear out the Restore and Deleted fields if we are processing deletes files 'and the file is NOT deleted. i.e., some residual left over from a previous run? If MARKDELETESINCATALOG = TRUE Then If Ucase(Deleted) = "NO" Then If rsFileEntry("Restore") = TRUE Then RsFileEntry("Restore") = False RsFileEntry.Update End If If rsFileEntry("Delete") = TRUE Then RsFileEntry("Delete") = False RsFileEntry.Update End If End If End If If CleanUpLongFile = TRUE Then 'Free up drive letter & remove local share UnmapDrive(MyDrive) RemoveShare("DRBACKUP") End If On Error Resume Next rsFileEntry.MoveNext On Error GoTo 0 Loop 'files within backup set rsFileEntry.Close On Error Resume Next rsSetname.MoveNext On Error GoTo 0 Wscript.StdOut.Write("Processing : " & Recordcount & "/" & CatalogRecords & VbCr) Loop 'backupsetID 'done processing. wrap up and display stats. Wscript.StdOut.Write("Processing : " & Recordcount & "/" & CatalogRecords & VbCrLF) Wscript.StdOut.Write("End Processing : " & Now() & VbCrLF) GroupDigits = -1 Default = 0 'show report Wscript.StdOut.Writeline(" ") Wscript.StdOut.Writeline("Analysis - Findings" & VbCrLf) 'Report out for Remote server storage Wscript.StdOut.Writeline("Remote Offsite Storage - Maintained by " & RBSProvider) If FileCountR > 0 Then Wscript.StdOut.Writeline(" Compressed Storage (bytes) : " & FormatNumber(CompressedSizeR, Default, Default, Default, GroupDigits)) Wscript.StdOut.Writeline(" Raw Size on Disk (bytes) : " & FormatNumber(OriginalSizeR, Default, Default, Default, GroupDigits)) Wscript.StdOut.Writeline(" File Count : " & FormatNumber(FileCountR, Default, Default, Default, GroupDigits)) If FilesDeletedR > 0 Then Wscript.StdOut.Writeline(VbCrLf & " Deleted File Storage (bytes) : " & FormatNumber(FilesDeletedSizeR, Default, Default, Default, GroupDigits)) Wscript.StdOut.Writeline(" Deleted File Count : " & FormatNumber(FilesDeletedR, Default, Default, Default, GroupDigits) & VbCrLf) Else Wscript.StdOut.Writeline(VbCrLf & " No Deleted File Storage Reported." & VbCrLf) End If Else Wscript.StdOut.Writeline(VbCrLf & " No Offsite File Storage Reported." & VbCrLf) End If If FileCountM > 0 Then Wscript.StdOut.Writeline("Local Mirror Storage - Maintained by Client Locally") Wscript.StdOut.Writeline(" Compressed Storage (bytes) : " & FormatNumber(CompressedSizeM, Default, Default, Default, GroupDigits)) Wscript.StdOut.Writeline(" Raw Size on Disk (bytes) : " & FormatNumber(OriginalSizeM, Default, Default, Default, GroupDigits)) Wscript.StdOut.Writeline(" File Count : " & FormatNumber(FileCountM, Default, Default, Default, GroupDigits)) If SHOWDELETEDMIRROR = TRUE Then If FilesDeletedM > 0 Then Wscript.StdOut.Writeline(VbCrLf & " Deleted File Storage (bytes) : " & FormatNumber(FilesDeletedSizeM, Default, Default, Default, GroupDigits)) Wscript.StdOut.Writeline(" Deleted File Count : " & FormatNumber(FilesDeletedM, Default, Default, Default, GroupDigits) & VbCrLf) Else Wscript.StdOut.Writeline(VbCrLf & " No Deleted File Storage Detected." & VbCrLf) End If End If End If If FileCountC > 0 Then Wscript.StdOut.Writeline(" ") Wscript.StdOut.Writeline("Private Cloud Storage - Maintained by Client") Wscript.StdOut.Writeline(" Compressed Storage (bytes) : " & FormatNumber(CompressedSizeC, Default, Default, Default, GroupDigits)) Wscript.StdOut.Writeline(" Raw Size on Disk (bytes) : " & FormatNumber(OriginalSizeC, Default, Default, Default, GroupDigits)) Wscript.StdOut.Writeline(" File Count : " & FormatNumber(FileCountC, Default, Default, Default, GroupDigits)) If SHOWDELETEDCLOUD = TRUE Then If FilesDeletedC > 0 Then Wscript.StdOut.Writeline(VbCrLf & " Deleted File Storage (bytes) : " & FormatNumber(FilesDeletedSizeC, Default, Default, Default, GroupDigits)) Wscript.StdOut.Writeline(" Deleted File Count : " & FormatNumber(FilesDeletedC, Default, Default, Default, GroupDigits) & VbCrLf) Else Wscript.StdOut.Writeline(VbCrLf & " No Deleted File Storage Detected." & VbCrLf) End If End If End If If FileCountP > 0 Then Wscript.StdOut.Writeline(" ") Wscript.StdOut.Writeline("Public Cloud Storage - Puchased from Public Storage Provider*") Wscript.StdOut.Writeline(" Compressed Storage (bytes) : " & FormatNumber(CompressedSizeP, Default, Default, Default, GroupDigits)) Wscript.StdOut.Writeline(" Raw Size on Disk (bytes) : " & FormatNumber(OriginalSizeP, Default, Default, Default, GroupDigits)) Wscript.StdOut.Writeline(" File Count : " & FormatNumber(FileCountP, Default, Default, Default, GroupDigits)) If SHOWDELETEDCLOUD = TRUE Then If FilesDeletedP > 0 Then Wscript.StdOut.Writeline(VbCrLf & " Deleted File Storage (bytes) : " & FormatNumber(FilesDeletedSizeP, Default, Default, Default, GroupDigits)) Wscript.StdOut.Writeline(" Deleted File Count : " & FormatNumber(FilesDeletedP, Default, Default, Default, GroupDigits) & VbCrLf) Else Wscript.StdOut.Writeline(VbCrLf & " No Deleted File Storage Detected.") End If End If Wscript.StdOut.Writeline(" Note: Public Cloud Storage statistics deemed reliable but not guaranteed." & VbCrLf) End If If (FileCountT = 0) OR (FileCountT <> FileCountR) Then Wscript.StdOut.Writeline(VbCrLf & "Catalog Export Summary - All Backup Destinations") Wscript.StdOut.Writeline(" Compressed Storage (bytes) : " & FormatNumber(CompressedSizeT, Default, Default, Default, GroupDigits)) Wscript.StdOut.Writeline(" Raw Size on Disk (bytes) : " & FormatNumber(OriginalSizeT, Default, Default, Default, GroupDigits)) Wscript.StdOut.Writeline(" Total (Unique) File Count : " & FormatNumber(FileCountT, Default, Default, Default, GroupDigits) & VbCrLf) End If If LongFiles > 0 Then Wscript.StdOut.Writeline("WARN: Extremely long filenames detected. (" & LongFiles & ")") End If If CatalogRecords <> FileCountT Then Wscript.StdOut.Writeline("WARN: Locked or untransferred files detected. (" & CatalogRecords - FileCountT & ")") End If 'all done 'open file to write out .csv of statistics statslogFile = strValue & "\Custom\catalog_stats.csv" Set objFSOStats = CreateObject("Scripting.FileSystemObject") Set objstatsFile = objFSOStats.CreateTextFile(StatsLogFile, ForWriting) 'write out extended statistics on extensions (summary) MaxDisplay = 20 Wscript.StdOut.Writeline(VbCrLf & "Catalog Content Statistic Summary (by file extension)" & vbcrlf) Wscript.Echo LeftJustify("Ext",6) & space(2) & RightJustify("Count",7) & Space(2) & RightJustify("Compressed",15) & Space(2) & RightJustify("Bytes",15) Wscript.Echo LeftJustify("---",6) & space(2) & RightJustify("-----",7) & Space(2) & RightJustify("----------",15) & Space(2) & RightJustify("-----",15) 'header for .csv file objstatsFile.Writeline """Extension"",""Count"",""CompressedSize"",""OrigSize""" 'let's sort the statistics by compressed bytes, decending sort() 'pause "after sorting extensions" & "extensions = " & NextExt For i = 0 to NextExt -1 If i <= maxdisplay -1 then 'Wscript.StdOut.Writeline "item = " & i & "value = " & extstr(i) 'Pause "Before screen Item = " & i Wscript.StdOut.Writeline(LeftJustify(UnicodeToAscii(extstr(i)),6) & space(2) & RightJustify(FormatNumber(extcount(i),0,,,-1),7) & space(2) & RightJustify(FormatNumber(extcbytes(i),0,,,-1),15) & space(2) & RightJustify(FormatNumber(extbytes(i),0,,,-1),15)) 'Pause "After screen Item = " & i End If objstatsFile.Writeline """" & UnicodeToAscii(extstr(i)) & """,""" & _ extcount(i) & """,""" & extcbytes(i) & """,""" & _ extbytes(i) & """" 'Pause "After File Item = " & i Next 'pause "after writing extensions" objstatsfile.close 'pause "after objstatsfile close" Wscript.StdOut.Writeline("") If ShowDeletedExts = FALSE Then Wscript.StdOut.Writeline("Note: Deleted file extension statistics are suppressed.") End If Wscript.StdOut.Writeline("") 'all done Wscript.StdOut.Writeline("Catalog Export = " & CatalogFile) Wscript.StdOut.Writeline("Catalog Export Stats = " & StatsLogFile & VbCrLf) If WRITEDELETESONLY = TRUE Then Wscript.StdOut.Writeline("Note: Stats and Catalog report only for deleted files." & VbCrLf) End If objFile.Close 'let's send the file via FTP for analysis 'first, let's prepend the machine name and datestamp so its somewhat unique DateStamp = FormatDateTime(now(),0) DateStamp = Replace(DateStamp, "/", "-") DateStamp = Replace(DateStamp, ":", "_") DateStamp = Replace(DateStamp, " ", "_") NewCatalogFile = strValue & "\Custom\" & GetMachineNameString() & "_" & DateStamp & "_catalog.csv" objFSO.MoveFile CatalogFile, NewCatalogFile NewStatsLogFile = strValue & "\Custom\" & GetMachineNameString() & "_" & DateStamp & "_catalog_stats.csv" objFSO.MoveFile StatsLogFile, NewStatsLogFile 'null out file system object to unlock access to files 'objFSO = Nothing 'let's try to provide some information here if ftp fails. On Error GoTo 0 'pause "before send to ftp" If SENDTOFTP = TRUE Then If objFSOexe.FileExists(FTPDLL) Then 'Wscript.StdOut.Writeline("Registering FTP library for use." ) 'let's register the ChilkatFTP.dll here for use Set WshShell = WScript.CreateObject( "WScript.Shell" ) strcmd = RegsvrEXE & " /s " & """" & FTPDLL & """" WshShell.Run strcmd, 1, TRUE Wscript.StdOut.Writeline(VbCfLf & "Sending Catalog file to FTP: " & NewCatalogFile & " to " & RBSProvider & " for analysis." ) Wscript.StdOut.Writeline(FTPUpload(NewCatalogFile, FTPPATH, FTPHOST, FTPUSERNAME, FTPPASSWORD, FALSE) ) 'send up the stats file too Wscript.StdOut.Writeline(VbCfLf & "Sending Stats file to FTP: " & NewStatsLogFile & " to " & RBSProvider & " for analysis." ) Wscript.StdOut.Writeline(FTPUpload(NewStatsLogFile, FTPPATH, FTPHOST, FTPUSERNAME, FTPPASSWORD, FALSE) ) Else Wscript.StdOut.Writeline(VbCrLf & "Cannot locate FTP library." ) Wscript.StdOut.Writeline("Files not transfered." ) End If End If Wscript.StdOut.WriteLine(VbCrLf & VbCRLf& "Catalog Export Analysis Completed: " & Now() & VbCrLf) 'pause "psexec status = " & PSEXEC 'If PSEXEC = TRUE Then Pause "Pressing Enter closes this command box..." 'End If WScript.Quit Function GetExtension(MyFilename) 'extract file extension to report out for sorting purposes GetExtension = "NONE" 'default to None 'empty filename means long file. not supported here. If Len(MyFilename & " ") = 1 then GetExtension = "Long Filenames Not Supported" Exit Function End If 'extract filename from path to file strFilename = Right(MyFilename, Len(MyFilename) - InstrRev(MyFilename,"\")) 'if no period, then no possible extension If Instr(strFilename, ".") = 0 Then Exit Function End If 'if last character is period, then no extension If Right(strFilename, 1) = "." Then Exit Function End If 'now we know there is a period and at least one character after it. grab extension. GetExtension = UCase(Right(strFilename, Len(strFilename) - InstrRev(strFilename,"."))) Exit Function End Function Function NewOS() On Error Resume Next strComputer = "." OScaption = "" NewOS = False Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2") Set colOperatingSystems = objWMIService.ExecQuery _ ("Select * from Win32_OperatingSystem") For Each objOperatingSystem in colOperatingSystems OScaption = objOperatingSystem.Caption Next If Instr(OScaption,"XP") > 0 OR _ Instr(OScaption, "2003") > 0 OR _ instr(OScaption, "2000") > 0 OR _ len(OScaption) = 0 Then NewOS = False Else NewOS = True End If On Error GoTo 0 Exit Function End Function Function GetSQLQuery(CatalogLevel, setID) '12/5/2012 - Format an SQL query string specific to level of software '3/28/2013 - delete field doesn't appear until user attempts to use delete I/F. removed from Case 2 ' Default = Generic, works with all catalogs (All) ' 1 = Longfilename support upto 259 characters (11.5.8) ' 2 = Delete and BackupLocation Support (11.7.x) ' Select Case CatalogLevel Case 1 GetSQLQuery = "SELECT Catalog.ID, Catalog.DateStamp, Catalog.SetNameID, Catalog.Extension, " & _ "Catalog.[Restore], Catalog.Status, Catalog.[Filename], " & _ "Catalog.StoreMethod, Catalog.LongPathID, Catalog.OrigSize, " & _ "Catalog.PrepSize, Catalog.TrfResult, Catalog.PrepResult, Catalog.Patch, " & _ "Catalog.FullPFolder, LongPaths.LongPath, " & NullSub & " " & _ "AS OrigName, SetNames.[Name] AS SetName, Catalog.SubFolder, " & _ "Catalog.EncryptMethod, Catalog.Plugin, Catalog.ModifiedDate, Catalog.PatchName, " & _ "Catalog.DependentFolder FROM SetNames INNER JOIN (LongPaths RIGHT JOIN [Catalog] " & _ "ON LongPaths.PathID = Catalog.LongPathID) ON SetNames.ID = Catalog.SetNameID WHERE SETNAMES.ID = " & SetID & ";" Case 2 GetSQLQuery = "SELECT Catalog.ID, Catalog.DateStamp, Catalog.SetNameID, Catalog.Extension, " & _ "Catalog.[Restore], Catalog.Status, Catalog.[Filename], " & _ "Catalog.StoreMethod, Catalog.LongPathID, Catalog.OrigSize, " & _ "Catalog.PrepSize, Catalog.TrfResult, Catalog.PrepResult, Catalog.Patch, " & _ "Catalog.FullPFolder, Catalog.LPathID, Catalog.BackupLocation, Catalog.[Delete], LongPaths.LongPath, " & NullSub & " " & _ "AS OrigName, SetNames.[Name] AS SetName, Catalog.SubFolder, " & _ "Catalog.EncryptMethod, Catalog.Plugin, Catalog.ModifiedDate, Catalog.PatchName, " & _ "Catalog.DependentFolder FROM SetNames INNER JOIN (LongPaths RIGHT JOIN [Catalog] " & _ "ON LongPaths.PathID = Catalog.LongPathID) ON SetNames.ID = Catalog.SetNameID WHERE SETNAMES.ID = " & SetID & ";" Case Else GetSQLQuery = "Select * FROM Catalog WHERE SETNAMES.ID = " & SetID & ";" End Select Exit Function End Function Function GetBackupLocation(Location) '12/5/2012 - translate bit string into readable string '1000 = (R)emote Server '0100 = Local (M)irror '0010 = (C)loud Storage '000 = Public Cloud (Any) '0001 = Future (unspecified - ignore for now) RemoteStr = "" MirrorStr = "" CloudStr = "" If Len(Location & " ") = 1 Then 'null string GetBackupLocation = "R" Exit Function End If If Mid(Location,1,1) = "1" Then RemoteStr = "R" End If If Mid(Location,2,1) = "1" Then MirrorStr = "M" End If If Mid(Location,3,1) = "1" Then CloudStr = "C" End If If Mid(Location,1,3) = "000" Then CloudStr = "P" End If GetBackupLocation = RemoteStr & MirrorStr & CloudStr Exit Function End Function Sub UpdateExtStats (ext, bytes, cbytes, deletes) 'update stats for extensions seen If (ShowDeletedExts = FALSE) AND ((Ucase(deletes) = "YES") OR (Ucase(deletes) = "YES-PENDING")) _ OR Ucase(ext) = "VHD" OR Ucase(ext) = "VHDX" _ Then 'deleted file which we don't want to include Else If Instr(extlist, ";" & Ucase(ext) & "=") > 0 Then 'we've seen this string before i = getidx(extlist, Ucase(ext)) extcount(i) = extcount(i) + 1 extbytes(i) = extbytes(i) + bytes extcbytes(i) = extcbytes(i) + cbytes 'Wscript.Echo "updating: " & ext & "[" & i & "]" & " #: " & extcount(i) Else 'we haven't seen this extension yet 'special case, test for no extension If Len(ext) = 0 Then extcount(0) = extcount(0) + 1 extbytes(0) = extbytes(0) + bytes extcbytes(0) = extcbytes(0) + cbytes Else extlist = extlist & Ucase(ext) & "=" & NextExt & ";" i = NextExt extstr(i) = Ucase(ext) extcount(i) = 1 extbytes(i) = bytes extcbytes(i) = cbytes NextExt = NextExt + 1 'Wscript.Echo "Added: " & ext & " as index: " & i End If End If End If End Sub Function Getidx(master, extstr) 'scan master extensionstring for extstr and grab array index 'syntax is ;extension=index; ' 'handle NONE p1 = Instr(master, ";" & extstr & "=") If P1 = 0 Then Getidx = 0 Else p2 = p1 + len(extstr) + 2 p3 = Instr( p2, Master, ";") Getidx = Clng(mid(master, p2, p3-p2)) End If 'wscript.echo Clng(mid(master, p2, p3-p2)) End Function Function RightJustify(string, size) 'right justify a string in a buffer of size 'size' If Len(string) >= size Then RightJustify = string Else RightJustify = Space(size - Len(string)) & string End If End Function Function LeftJustify(string, size) 'left justify a string in a buffer of size 'size' If Len(string) >= size Then LeftJustify = Mid(string, 1, size) Else LeftJustify = string & Space(size - Len(string)) End If End Function Sub Sort( ) 'sort global array by compressed bytecount in decending order via XOR 'This is a pretty poor excuse for a sort, but its here as a future placeholder for a real sort routine Dim i, j, strHolder, Holder For i = (NextExt -2) to 0 Step -1 For j= 0 to i If (extcbytes(j) > extcbytes(j+1)) XOR TRUE Then strholder = extstr(j+1) extstr(j+1) = extstr(j) extstr(j) = strholder holder = extcount(j+1) extcount(j+1) = extcount(j) extcount(j) = holder holder = extbytes(j+1) extbytes(j+1) = extbytes(j) extbytes(j) = holder holder = extcbytes(j+1) extcbytes(j+1) = extcbytes(j) extcbytes(j) = holder End If Next Next End Sub Function FTPUpload( locFile, targetDir, host, user, password, blnMkDir ) ' This function uses the free ChilkatFTP ActiveX component ' to upload a single file. ' The remote directory can be specified, but the remote ' file name will be the same as the local file name. ' The function is based on Chilkat's own sample for the ChilkatFTP2 component ' (which is not free): http://www.example-code.com/vbscript/ftpPutFile.asp ' ' Arguments: ' locFile [string] the (path and) file name of the file to be uploaded ' targetDir [string] the (relative) path of the remote target directory; ' if empty, the current remote directory will be used ' host [string] the remote host name (e.g. "ftp.mydomain.org") ' user [string] the login name for the remote host ' password [string] the password for the login account ' blnMkDir [boolean] if True, the remote directory will be created if it ' doesn't exist, otherwise the function will fail if ' the remote directory doesn't exist ' ' The ChilkatFTP ActiveX component can be downloaded from: ' http://www.chilkatsoft.com/download/FtpActiveX.msi ' Documentation can be found at: ' http://www.chilkatsoft.com/refdoc/xChilkatFtpRef.html ' ' Written by Rob van der Woude ' http://www.robvanderwoude.com ' Standard housekeeping Dim objFSO, objFTP, ok, strRemFile ' Extract the local file name and extension only from its path Set objFSO = CreateObject( "Scripting.FileSystemObject" ) With objFSO strRemFile = .BuildPath( targetDir, .GetFileName( locFile ) ) End With Set objFSO = Nothing ' Create a ChilkatFTP object Set objFTP = CreateObject( "ChilkatFTP.ChilkatFTP.1" ) ' pass the connection properties to the object objFTP.Username = user objFTP.Password = password objFTP.Hostname = host objFTP.Passive = True objFTP.Port = FTPPORT ' Connect, abort and return error message on failure ok = objFTP.Connect( ) If ( ok <> 1 ) Then FTPUpload = objFTP.LastErrorText If Len(objFTP.LastErrorText) = 0 Then FTPUpload = "failed to connect" Set objFTP = Nothing Exit Function End If If targetDir <> "" Then ' If specified, create target directory If blnMkDir = True Then objFTP.CreateRemoteDir targetDir End If ' Change directory remotely, abort and return error message on failure ok = objFTP.ChangeRemoteDir( targetDir ) If ( ok <> 1 ) Then FTPUpload = objFTP.LastErrorText If Len(objFTP.LastErrorText) = 0 Then FTPUpload = "failed to change folder" objFTP.Disconnect() Set objFTP = Nothing Exit Function End If End If ' Upload the file, abort and return error message on failure ok = objFTP.PutFile( locFile, strRemFile ) If ( ok <> 1 ) Then FTPUpload = objFTP.LastErrorText If Len(objFTP.LastErrorText) = 0 Then FTPUpload = "failed to transfer/put file" Else FTPUpload = "Upload succeeded" End If ' Disconnect,and release the object objFTP.Disconnect( ) Set objFTP = Nothing End Function Function GetMachineNameString() 'get a string representation of machine name with timestamp Set wshShell = WScript.CreateObject( "WScript.Shell" ) strComputerName = wshShell.ExpandEnvironmentStrings( "%COMPUTERNAME%" ) strComputerName = Replace(strComputerName, " ", "_") 'WScript.Echo "Computer Name: " & strComputerName GetMachineNameString = strComputerName Set wshShell = Nothing Exit Function End Function Function FreeDrive() 'find a free drive letter not in use CONST DefaultStart = "A" Ltr = DefaultStart Set objFS = CreateObject("Scripting.FileSystemObject") Do While objFS.DriveExists(Ltr) and UCase(Ltr) <= "Z" Ltr = Chr(ASC(Ltr) + 1) If Ltr = "[" Then Wscript.Echo "Drive letter allocation failed." FreeDrive = "" Exit Function End If Loop FreeDrive = Ltr Set objFS = Nothing Exit Function End Function Function MyComputerName 'fetch the name of the computer Set wshShell = CreateObject( "WScript.Shell" ) strComputerName = wshShell.ExpandEnvironmentStrings( "%COMPUTERNAME%" ) 'WScript.Echo "Computer Name: " & strComputerName MyComputerName = strComputerName Set wshShell = Nothing Exit Function End Function Function MapDrive(DriveLetter, UNCPath) 'attempt to map a network drive Set objNetwork = CreateObject("Wscript.Network") On Error Resume Next objNetwork.MapNetworkDrive DriveLetter & ":", UNCPath, FALSE If NOT (Err = 0 OR Err = 22) Then 'Wscript.Echo "Error = " & Err & " MAP Description: " & Err.Description Err.Clear On Error Resume Next 'Wscript.Echo "Failed to establish path mapping: " & DriveLetter & ": to " & UNCPath & " " & Err.Description MapDrive = FALSE Exit Function End If MapDrive = TRUE On Error GoTo 0 'Wscript.Echo "Mapped: " & DriveLetter & ": " & UNCPath Set objNetwork = Nothing End Function Function UnMapDrive(DriveLetter) 'attempt to unmap a network drive Set objNetwork = CreateObject("Wscript.Network") On Error Resume Next objNetwork.RemoveNetworkDrive DriveLetter & ":", TRUE, TRUE If Err <> 0 Then Err.Clear On Error Resume Next 'Wscript.Echo "Failed to remove path mapping: " & DriveLetter & ":" UnMapDrive = FALSE Exit Function End If UnMapDrive = TRUE 'Wscript.Sleep(100) Set objNetwork = Nothing On Error GoTo 0 End Function Function MakeShare(sSharePath, sShareName) Const FILE_SHARE = 0 Const MAX_CONNECT = 2 RemoveShare sShareName set ObjWin = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2") set ObjWinS = ObjWin.Get("Win32_Share") On Error Resume Next MakeShare = ObjWinS.Create(sSharePath, sShareName, FILE_SHARE, MAX_CONNECT, sShareName) 'Wscript.Echo "create share status=" & Err & " Description: " & Err.Description If NOT (Err = 0 OR Err = 22) Then Wscript.Echo "Failed to create share: " & sSharePath & " (" & Err.Description & ")" On Error GoTo 0 Exit Function End If Set objWinS = Nothing Set objWin = Nothing On Error GoTo 0 'Wscript.Echo "Created share: " & sShareName End Function Sub RemoveShare(sShareName) Dim cShares, oShare On Error Resume Next Set objWin = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2") Set cShares = objWin.ExecQuery _ ("Select * from Win32_Share Where Name = '" & sShareName & "'") For Each oShare in cShares oShare.Delete Next Set objWin = Nothing End Sub Sub Pause(strPause) WScript.Echo (strPause) z = WScript.StdIn.Read(1) End Sub Function UnicodeToAscii(sText) If Len(sText & " ") = 1 Then UnicodeToAscii = "" Exit Function End If sAscii = "" For x = 1 To Len(sText) ascval = AscW(Mid(sText, x, 1)) If (ascval < 0) Then ascval = 65536 + ascval ' http://support.microsoft.com/kb/272138 ElseIf (ascval < 32) Then ' non print characters sAscii = sAscii & "&#" & ascval & ";" ElseIf (acval = 34 Or acval = 39 Or acval = 38 Or acval = 60 Or acval = 62) Then 'reserved characters " ' & sAscii = sAscii & "&#" & ascval & ";" ElseIf (ascval > 127) Then 'unicode sAscii = sAscii & "&#" & ascval & ";" Else ' acceptable text characters sAscii = sAscii + Mid(sText, x, 1) End If Next UnicodeToAscii = sAscii End Function '' SIG '' Begin signature block '' SIG '' MIIk/gYJKoZIhvcNAQcCoIIk7zCCJOsCAQExCzAJBgUr '' SIG '' DgMCGgUAMGcGCisGAQQBgjcCAQSgWTBXMDIGCisGAQQB '' SIG '' gjcCAR4wJAIBAQQQTvApFpkntU2P5azhDxfrqwIBAAIB '' SIG '' AAIBAAIBAAIBADAhMAkGBSsOAwIaBQAEFKl8dxfAsZhO '' SIG '' +G1JU21IUjqu6bwsoIIeojCCBSUwggQNoAMCAQICEQC5 '' SIG '' oMkB8/HzaJWCExAMHIDEMA0GCSqGSIb3DQEBCwUAMHwx '' SIG '' CzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1h '' SIG '' bmNoZXN0ZXIxEDAOBgNVBAcTB1NhbGZvcmQxGDAWBgNV '' SIG '' BAoTD1NlY3RpZ28gTGltaXRlZDEkMCIGA1UEAxMbU2Vj '' SIG '' dGlnbyBSU0EgQ29kZSBTaWduaW5nIENBMB4XDTIxMDQw '' SIG '' MTAwMDAwMFoXDTIzMDQwMTIzNTk1OVowazELMAkGA1UE '' SIG '' BhMCVVMxETAPBgNVBAgMCE1hcnlsYW5kMQ8wDQYDVQQH '' SIG '' DAZMYXVyZWwxGzAZBgNVBAoMEkRvY3RvciBCYWNrdXAs '' SIG '' IExMQzEbMBkGA1UEAwwSRG9jdG9yIEJhY2t1cCwgTExD '' SIG '' MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA '' SIG '' tM/hT+5itHHS13dShxD82in/HUCl6lci5bPmivyH76cJ '' SIG '' dsQW2carMVq65URfr8LgzVGoRIWcdZufv5F91SZxuBA6 '' SIG '' KPgug7Y7obuDnhZ0TXa6Q3uWnh6RXwpQuz7Z35VbTdAe '' SIG '' k82twGLwAg5qljuehLYckRXlQ4+Ffn+ilfZGijqGGwVZ '' SIG '' HKnjNPiZjz1VIrOW/Y1ESWON8IUJu05HQhxTV9vaNsKu '' SIG '' WWk7wZ/ANZ/V3Q0QKA+vAqxZQSCMD05YxrKK1Dfonyjo '' SIG '' rFRfKO+EECVrnb+/B73VJ818SWyOVTD5Ww5yMAnXZWKN '' SIG '' MvaZNbUkF4MFG9TgK1JlS3K7CTRZiwd8sQIDAQABo4IB '' SIG '' sTCCAa0wHwYDVR0jBBgwFoAUDuE6qFM6MdWKvsG7rWca '' SIG '' A4WtNA4wHQYDVR0OBBYEFDEQykYmJXGfw0FHUronVNz3 '' SIG '' vdUMMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAA '' SIG '' MBMGA1UdJQQMMAoGCCsGAQUFBwMDMBEGCWCGSAGG+EIB '' SIG '' AQQEAwIEEDBKBgNVHSAEQzBBMDUGDCsGAQQBsjEBAgED '' SIG '' AjAlMCMGCCsGAQUFBwIBFhdodHRwczovL3NlY3RpZ28u '' SIG '' Y29tL0NQUzAIBgZngQwBBAEwQwYDVR0fBDwwOjA4oDag '' SIG '' NIYyaHR0cDovL2NybC5zZWN0aWdvLmNvbS9TZWN0aWdv '' SIG '' UlNBQ29kZVNpZ25pbmdDQS5jcmwwcwYIKwYBBQUHAQEE '' SIG '' ZzBlMD4GCCsGAQUFBzAChjJodHRwOi8vY3J0LnNlY3Rp '' SIG '' Z28uY29tL1NlY3RpZ29SU0FDb2RlU2lnbmluZ0NBLmNy '' SIG '' dDAjBggrBgEFBQcwAYYXaHR0cDovL29jc3Auc2VjdGln '' SIG '' by5jb20wHwYDVR0RBBgwFoEUc3VwcG9ydEBkcmJhY2t1 '' SIG '' cC5uZXQwDQYJKoZIhvcNAQELBQADggEBAGG8CB9UvO/t '' SIG '' GjAb1S6EQB7Dc1FMeAzfUeRfeAVYwEJThbAUkEzJplK1 '' SIG '' gnVOcf666HEGrwSERCtoW4ctn43Ba8swVEjalcw42EPp '' SIG '' bBEHPQ8BTBUAbmKeQ2FFCV8J6FXTUmtpTeHMhc+StyPl '' SIG '' HjVu//sGsKOm8CMN8NamSNA8Hl/+6NyM4Jerf6WIhkE1 '' SIG '' 2TrY+nLD37OsKXxvDIrycDQXlZKauMwpZmLpqDH0VRaL '' SIG '' gcH64eVG8sQWyDj5/ndUMjI8hbL2xQvKYXCeG3/Au8hN '' SIG '' ZKNQzEZpjJAY1XtV9lrFmimImC1kCALr6dMdjbGBA8mA '' SIG '' IIG763b/Hh+j1qoP+AlXOXcwggWBMIIEaaADAgECAhA5 '' SIG '' ckQ6+SK3UdfTbBDdMTWVMA0GCSqGSIb3DQEBDAUAMHsx '' SIG '' CzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVyIE1h '' SIG '' bmNoZXN0ZXIxEDAOBgNVBAcMB1NhbGZvcmQxGjAYBgNV '' SIG '' BAoMEUNvbW9kbyBDQSBMaW1pdGVkMSEwHwYDVQQDDBhB '' SIG '' QUEgQ2VydGlmaWNhdGUgU2VydmljZXMwHhcNMTkwMzEy '' SIG '' MDAwMDAwWhcNMjgxMjMxMjM1OTU5WjCBiDELMAkGA1UE '' SIG '' BhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNV '' SIG '' BAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNF '' SIG '' UlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVz '' SIG '' dCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIi '' SIG '' MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCAEmUX '' SIG '' Ng7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/M '' SIG '' Pans9s/B3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2 '' SIG '' dvKpOyuR+OJv0OwWIJAJPuLodMkYtJHUYmTbf6MG8YgY '' SIG '' apAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+ '' SIG '' bJz/Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3Lye '' SIG '' TP6vBZY1H1dat//O+T23LLb2VN3I5xI6Ta5MirdcmrS3 '' SIG '' ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT '' SIG '' 79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M '' SIG '' 0GkPGw/zJSZrM233bkf6c0Plfg6lZrEpfDKEY1WJxA3B '' SIG '' k1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmTYo61 '' SIG '' Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugT '' SIG '' ncxbgtNMs+1b/97lc6wjOy0AvzVVdAlJ2ElYGn+SNuZR '' SIG '' kg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4eeUB9XVKg+ '' SIG '' /XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+ '' SIG '' eLf8ZxXhyVeEHg9j1uliutZfVS7qXMYoCAQlObgOK6ny '' SIG '' TJccBz8NUvXt7y+CDwIDAQABo4HyMIHvMB8GA1UdIwQY '' SIG '' MBaAFKARCiM+lvEH7OKvKe+CpX/QMKS0MB0GA1UdDgQW '' SIG '' BBRTeb9aqitKz1SA4dibwJ3ysgNmyzAOBgNVHQ8BAf8E '' SIG '' BAMCAYYwDwYDVR0TAQH/BAUwAwEB/zARBgNVHSAECjAI '' SIG '' MAYGBFUdIAAwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDov '' SIG '' L2NybC5jb21vZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVT '' SIG '' ZXJ2aWNlcy5jcmwwNAYIKwYBBQUHAQEEKDAmMCQGCCsG '' SIG '' AQUFBzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20w '' SIG '' DQYJKoZIhvcNAQEMBQADggEBABiHUdx0IT2ciuAntzPQ '' SIG '' Lszs8ObLXhHeIm+bdY6ecv7k1v6qH5yWLe8DSn6u9I1v '' SIG '' cjxDO8A/67jfXKqpxq7y/Njuo3tD9oY2fBTgzfT3P/7e '' SIG '' uLSK8JGW/v1DZH79zNIBoX19+BkZyUIrE79Yi7qkomYE '' SIG '' doiRTgyJFM6iTckys7roFBq8cfFb8EELmAAKIgMQ5Qyx '' SIG '' +c2SNxntO/HkOrb5RRMmda+7qu8/e3c70sQCkT0ZANMX '' SIG '' XDnbP3sYDUXNk4WWL13fWRZPP1G91UUYP+1KjugGYXQj '' SIG '' FrUNUHMnREd/EF2JKmuFMRTE6KlqTIC8anjPuH+OdnKZ '' SIG '' DJ3+15EIFqGjX5UwggX1MIID3aADAgECAhAdokgwb5sm '' SIG '' GNCC4JZ9M9NqMA0GCSqGSIb3DQEBDAUAMIGIMQswCQYD '' SIG '' VQQGEwJVUzETMBEGA1UECBMKTmV3IEplcnNleTEUMBIG '' SIG '' A1UEBxMLSmVyc2V5IENpdHkxHjAcBgNVBAoTFVRoZSBV '' SIG '' U0VSVFJVU1QgTmV0d29yazEuMCwGA1UEAxMlVVNFUlRy '' SIG '' dXN0IFJTQSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe '' SIG '' Fw0xODExMDIwMDAwMDBaFw0zMDEyMzEyMzU5NTlaMHwx '' SIG '' CzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1h '' SIG '' bmNoZXN0ZXIxEDAOBgNVBAcTB1NhbGZvcmQxGDAWBgNV '' SIG '' BAoTD1NlY3RpZ28gTGltaXRlZDEkMCIGA1UEAxMbU2Vj '' SIG '' dGlnbyBSU0EgQ29kZSBTaWduaW5nIENBMIIBIjANBgkq '' SIG '' hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhiKNMoV6GJ9J '' SIG '' 8JYvYwgeLdx8nxTP4ya2JWYpQIZURnQxYsUQ7bKHJ6aZ '' SIG '' y5UwwFb1pHXGqQ5QYqVRkRBq4Etirv3w+Bisp//uLjMg '' SIG '' +gwZiahse60Aw2Gh3GllbR9uJ5bXl1GGpvQn5Xxqi5Ue '' SIG '' W2DVftcWkpwAL2j3l+1qcr44O2Pej79uTEFdEiAIWeg5 '' SIG '' zY/S1s8GtFcFtk6hPldrH5i8xGLWGwuNx2YbSp+dgcRy '' SIG '' QLXiX+8LRf+jzhemLVWwt7C8VGqdvI1WU8bwunlQSSz3 '' SIG '' A7n+L2U18iLqLAevRtn5RhzcjHxxKPP+p8YU3VWRbooR '' SIG '' Dd8GJJV9D6ehfDrahjVh0wIDAQABo4IBZDCCAWAwHwYD '' SIG '' VR0jBBgwFoAUU3m/WqorSs9UgOHYm8Cd8rIDZsswHQYD '' SIG '' VR0OBBYEFA7hOqhTOjHVir7Bu61nGgOFrTQOMA4GA1Ud '' SIG '' DwEB/wQEAwIBhjASBgNVHRMBAf8ECDAGAQH/AgEAMB0G '' SIG '' A1UdJQQWMBQGCCsGAQUFBwMDBggrBgEFBQcDCDARBgNV '' SIG '' HSAECjAIMAYGBFUdIAAwUAYDVR0fBEkwRzBFoEOgQYY/ '' SIG '' aHR0cDovL2NybC51c2VydHJ1c3QuY29tL1VTRVJUcnVz '' SIG '' dFJTQUNlcnRpZmljYXRpb25BdXRob3JpdHkuY3JsMHYG '' SIG '' CCsGAQUFBwEBBGowaDA/BggrBgEFBQcwAoYzaHR0cDov '' SIG '' L2NydC51c2VydHJ1c3QuY29tL1VTRVJUcnVzdFJTQUFk '' SIG '' ZFRydXN0Q0EuY3J0MCUGCCsGAQUFBzABhhlodHRwOi8v '' SIG '' b2NzcC51c2VydHJ1c3QuY29tMA0GCSqGSIb3DQEBDAUA '' SIG '' A4ICAQBNY1DtRzRKYaTb3moqjJvxAAAeHWJ7Otcywvaz '' SIG '' 4GOz+2EAiJobbRAHBE++uOqJeCLrD0bs80ZeQEaJEvQL '' SIG '' d1qcKkE6/Nb06+f3FZUzw6GDKLfeL+SU94Uzgy1KQEi/ '' SIG '' msJPSrGPJPSzgTfTt2SwpiNqWWhSQl//BOvhdGV5CPWp '' SIG '' k95rcUCZlrp48bnI4sMIFrGrY1rIFYBtdF5KdX6luMNs '' SIG '' tc/fSnmHXMdATWM19jDTz7UKDgsEf6BLrrujpdCEAJM+ '' SIG '' U100pQA1aWy+nyAlEA0Z+1CQYb45j3qOTfafDh7+B1ES '' SIG '' ZoMmGUiVzkrJwX/zOgWb+W/fiH/AI57SHkN6RTHBnE2p '' SIG '' 8FmyWRnoao0pBAJ3fEtLzXC+OrJVWng+vLtvAxAldxU0 '' SIG '' ivk2zEOS5LpP8WKTKCVXKftRGcehJUBqhFfGsp2xvBwK '' SIG '' 2nxnfn0u6ShMGH7EezFBcZpLKewLPVdQ0srd/Z4FUeVE '' SIG '' eN0B3rF1mA1UJP3wTuPi+IO9crrLPTru8F4XkmhtyGH5 '' SIG '' pvEqCgulufSe7pgyBYWe6/mDKdPGLH29OncuizdCoGqC '' SIG '' 7TtKqpQQpOEN+BfFtlp5MxiS47V1+KHpjgolHuQe8Z9a '' SIG '' hyP/n6RRnvs5gBHN27XEp6iAb+VT1ODjosLSWxr6MiYt '' SIG '' aldwHDykWC6j81tLB9wyWfOHpxptWDCCBuwwggTUoAMC '' SIG '' AQICEDAPb6zdZph0fKlGNqd4LbkwDQYJKoZIhvcNAQEM '' SIG '' BQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpOZXcg '' SIG '' SmVyc2V5MRQwEgYDVQQHEwtKZXJzZXkgQ2l0eTEeMBwG '' SIG '' A1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMS4wLAYD '' SIG '' VQQDEyVVU0VSVHJ1c3QgUlNBIENlcnRpZmljYXRpb24g '' SIG '' QXV0aG9yaXR5MB4XDTE5MDUwMjAwMDAwMFoXDTM4MDEx '' SIG '' ODIzNTk1OVowfTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT '' SIG '' EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2Fs '' SIG '' Zm9yZDEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMSUw '' SIG '' IwYDVQQDExxTZWN0aWdvIFJTQSBUaW1lIFN0YW1waW5n '' SIG '' IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC '' SIG '' AgEAyBsBr9ksfoiZfQGYPyCQvZyAIVSTuc+gPlPvs1rA '' SIG '' dtYaBKXOR4O168TMSTTL80VlufmnZBYmCfvVMlJ5Lslj '' SIG '' whObtoY/AQWSZm8hq9VxEHmH9EYqzcRaydvXXUlNclYP '' SIG '' 3MnjU5g6Kh78zlhJ07/zObu5pCNCrNAVw3+eolzXOPEW '' SIG '' snDTo8Tfs8VyrC4Kd/wNlFK3/B+VcyQ9ASi8Dw1Ps5EB '' SIG '' jm6dJ3VV0Rc7NCF7lwGUr3+Az9ERCleEyX9W4L1GnIK+ '' SIG '' lJ2/tCCwYH64TfUNP9vQ6oWMilZx0S2UTMiMPNMUopy9 '' SIG '' Jv/TUyDHYGmbWApU9AXn/TGs+ciFF8e4KRmkKS9G493b '' SIG '' kV+fPzY+DjBnK0a3Na+WvtpMYMyou58NFNQYxDCYdIIh '' SIG '' z2JWtSFzEh79qsoIWId3pBXrGVX/0DlULSbuRRo6b83X '' SIG '' hPDX8CjFT2SDAtT74t7xvAIo9G3aJ4oG0paH3uhrDvBb '' SIG '' fel2aZMgHEqXLHcZK5OVmJyXnuuOwXhWxkQl3wYSmgYt '' SIG '' nwNe/YOiU2fKsfqNoWTJiJJZy6hGwMnypv99V9sSdvqK '' SIG '' QSTUG/xypRSi1K1DHKRJi0E5FAMeKfobpSKupcNNgtCN '' SIG '' 2mu32/cYQFdz8HGj+0p9RTbB942C+rnJDVOAffq2OVgy '' SIG '' 728YUInXT50zvRq1naHelUF6p4MCAwEAAaOCAVowggFW '' SIG '' MB8GA1UdIwQYMBaAFFN5v1qqK0rPVIDh2JvAnfKyA2bL '' SIG '' MB0GA1UdDgQWBBQaofhhGSAPw0F3RSiO0TVfBhIEVTAO '' SIG '' BgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIB '' SIG '' ADATBgNVHSUEDDAKBggrBgEFBQcDCDARBgNVHSAECjAI '' SIG '' MAYGBFUdIAAwUAYDVR0fBEkwRzBFoEOgQYY/aHR0cDov '' SIG '' L2NybC51c2VydHJ1c3QuY29tL1VTRVJUcnVzdFJTQUNl '' SIG '' cnRpZmljYXRpb25BdXRob3JpdHkuY3JsMHYGCCsGAQUF '' SIG '' BwEBBGowaDA/BggrBgEFBQcwAoYzaHR0cDovL2NydC51 '' SIG '' c2VydHJ1c3QuY29tL1VTRVJUcnVzdFJTQUFkZFRydXN0 '' SIG '' Q0EuY3J0MCUGCCsGAQUFBzABhhlodHRwOi8vb2NzcC51 '' SIG '' c2VydHJ1c3QuY29tMA0GCSqGSIb3DQEBDAUAA4ICAQBt '' SIG '' VIGlM10W4bVTgZF13wN6MgstJYQRsrDbKn0qBfW8Oyf0 '' SIG '' WqC5SVmQKWxhy7VQ2+J9+Z8A70DDrdPi5Fb5WEHP8ULl '' SIG '' EH3/sHQfj8ZcCfkzXuqgHCZYXPO0EQ/V1cPivNVYeL9I '' SIG '' duFEZ22PsEMQD43k+ThivxMBxYWjTMXMslMwlaTW9JZW '' SIG '' CLjNXH8Blr5yUmo7Qjd8Fng5k5OUm7Hcsm1BbWfNyW+Q '' SIG '' PX9FcsEbI9bCVYRm5LPFZgb289ZLXq2jK0KKIZL+qG9a '' SIG '' JXBigXNjXqC72NzXStM9r4MGOBIdJIct5PwC1j53BLwE '' SIG '' NrXnd8ucLo0jGLmjwkcd8F3WoXNXBWiap8k3ZR2+6rzY '' SIG '' QoNDBaWLpgn/0aGUpk6qPQn1BWy30mRa2Coiwkud8Tle '' SIG '' TN5IPZs0lpoJX47997FSkc4/ifYcobWpdR9xv1tDXWU9 '' SIG '' UIFuq/DQ0/yysx+2mZYm9Dx5i1xkzM3uJ5rloMAMcofB '' SIG '' bk1a0x7q8ETmMm8c6xdOlMN4ZSA7D0GqH+mhQZ3+sbig '' SIG '' ZSo04N6o+TzmwTC7wKBjLPxcFgCo0MR/6hGdHgbGpm0y '' SIG '' XbQ4CStJB6r97DDa8acvz7f9+tCjhNknnvsBZne5VhDh '' SIG '' IG7GrrH5trrINV0zdo7xfCAMKneutaIChrop7rRaALGM '' SIG '' q+P5CslUXdS5anSevUiumDCCBwcwggTvoAMCAQICEQCM '' SIG '' d6AAj/TRsMY9nzpIg41rMA0GCSqGSIb3DQEBDAUAMH0x '' SIG '' CzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1h '' SIG '' bmNoZXN0ZXIxEDAOBgNVBAcTB1NhbGZvcmQxGDAWBgNV '' SIG '' BAoTD1NlY3RpZ28gTGltaXRlZDElMCMGA1UEAxMcU2Vj '' SIG '' dGlnbyBSU0EgVGltZSBTdGFtcGluZyBDQTAeFw0yMDEw '' SIG '' MjMwMDAwMDBaFw0zMjAxMjIyMzU5NTlaMIGEMQswCQYD '' SIG '' VQQGEwJHQjEbMBkGA1UECBMSR3JlYXRlciBNYW5jaGVz '' SIG '' dGVyMRAwDgYDVQQHEwdTYWxmb3JkMRgwFgYDVQQKEw9T '' SIG '' ZWN0aWdvIExpbWl0ZWQxLDAqBgNVBAMMI1NlY3RpZ28g '' SIG '' UlNBIFRpbWUgU3RhbXBpbmcgU2lnbmVyICMyMIICIjAN '' SIG '' BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAkYdLLIvB '' SIG '' 8R6gntMHxgHKUrC+eXldCWYGLS81fbvA+yfaQmpZGyVM '' SIG '' 6u9A1pp+MshqgX20XD5WEIE1OiI2jPv4ICmHrHTQG2K8 '' SIG '' P2SHAl/vxYDvBhzcXk6Th7ia3kwHToXMcMUNe+zD2eOX '' SIG '' 6csZ21ZFbO5LIGzJPmz98JvxKPiRmar8WsGagiA6t+/n '' SIG '' 1rglScI5G4eBOcvDtzrNn1AEHxqZpIACTR0FqFXTbVKA '' SIG '' g+ZuSKVfwYlYYIrv8azNh2MYjnTLhIdBaWOBvPYfqnzX '' SIG '' wUHOrat2iyCA1C2VB43H9QsXHprl1plpUcdOpp0pb+d5 '' SIG '' kw0yY1OuzMYpiiDBYMbyAizE+cgi3/kngqGDUcK8yYIa '' SIG '' IYSyl7zUr0QcloIilSqFVK7x/T5JdHT8jq4/pXL0w1oB '' SIG '' qlCli3aVG2br79rflC7ZGutMJ31MBff4I13EV8gmBXr8 '' SIG '' gSNfVAk4KmLVqsrf7c9Tqx/2RJzVmVnFVmRb945SD2b8 '' SIG '' mD9EBhNkbunhFWBQpbHsz7joyQu+xYT33Qqd2rwpbD1W '' SIG '' 7b94Z7ZbyF4UHLmvhC13ovc5lTdvTn8cxjwE1jHFfu89 '' SIG '' 6FF+ca0kdBss3Pl8qu/CdkloYtWL9QPfvn2ODzZ1RluT '' SIG '' dsSD7oK+LK43EvG8VsPkrUPDt2aWXpQy+qD2q4lQ+s6g '' SIG '' 8wiBGtFEp8z3uDECAwEAAaOCAXgwggF0MB8GA1UdIwQY '' SIG '' MBaAFBqh+GEZIA/DQXdFKI7RNV8GEgRVMB0GA1UdDgQW '' SIG '' BBRpdTd7u501Qk6/V9Oa258B0a7e0DAOBgNVHQ8BAf8E '' SIG '' BAMCBsAwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAK '' SIG '' BggrBgEFBQcDCDBABgNVHSAEOTA3MDUGDCsGAQQBsjEB '' SIG '' AgEDCDAlMCMGCCsGAQUFBwIBFhdodHRwczovL3NlY3Rp '' SIG '' Z28uY29tL0NQUzBEBgNVHR8EPTA7MDmgN6A1hjNodHRw '' SIG '' Oi8vY3JsLnNlY3RpZ28uY29tL1NlY3RpZ29SU0FUaW1l '' SIG '' U3RhbXBpbmdDQS5jcmwwdAYIKwYBBQUHAQEEaDBmMD8G '' SIG '' CCsGAQUFBzAChjNodHRwOi8vY3J0LnNlY3RpZ28uY29t '' SIG '' L1NlY3RpZ29SU0FUaW1lU3RhbXBpbmdDQS5jcnQwIwYI '' SIG '' KwYBBQUHMAGGF2h0dHA6Ly9vY3NwLnNlY3RpZ28uY29t '' SIG '' MA0GCSqGSIb3DQEBDAUAA4ICAQBKA3iQQjPsexqDCTYz '' SIG '' mFW7nUAGMGtFavGUDhlQ/1slXjvhOcRbuumVkDc3vd/7 '' SIG '' ZOzlgreVzFdVcEtO9KiH3SKFple7uCEn1KAqMZSKByGe '' SIG '' ir2nGvUCFctEUJmM7D66A3emggKQwi6Tqb4hNHVjueAt '' SIG '' D88BN8uNovq4WpquoXqeE5MZVY8JkC7f6ogXFutp1uEl '' SIG '' vUUIl4DXVCAoT8p7s7Ol0gCwYDRlxOPFw6XkuoWqemnb '' SIG '' daQ+eWiaNotDrjbUYXI8DoViDaBecNtkLwHHwaHHJJSj '' SIG '' sjxusl6i0Pqo0bglHBbmwNV/aBrEZSk1Ki2IvOqudNaC '' SIG '' 58CIuOFPePBcysBAXMKf1TIcLNo8rDb3BlKao0AwF7Ap '' SIG '' FpnJqreISffoCyUztT9tr59fClbfErHD7s6Rd+ggE+lc '' SIG '' JMfqRAtK5hOEHE3rDbW4hqAwp4uhn7QszMAWI8mR5UID '' SIG '' S4DO5E3mKgE+wF6FoCShF0DV29vnmBCk8eoZG4BU+keJ '' SIG '' 6JiBqXXADt/QaJR5oaCejra3QmbL2dlrL03Y3j4yHiDk '' SIG '' 7JxNQo2dxzOZgjdE1CYpJkCOeC+57vov8fGP/lC4eN0U '' SIG '' lt4cDnCwKoVqsWxo6SrkECtuIf3TfJ035CoG1sPx12jj '' SIG '' Twd5gQgT/rJkXumxPObQeCOyCSziJmK/O6mXUczHRDKB '' SIG '' sq/P3zGCBcgwggXEAgEBMIGRMHwxCzAJBgNVBAYTAkdC '' SIG '' MRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO '' SIG '' BgNVBAcTB1NhbGZvcmQxGDAWBgNVBAoTD1NlY3RpZ28g '' SIG '' TGltaXRlZDEkMCIGA1UEAxMbU2VjdGlnbyBSU0EgQ29k '' SIG '' ZSBTaWduaW5nIENBAhEAuaDJAfPx82iVghMQDByAxDAJ '' SIG '' BgUrDgMCGgUAoIG8MBkGCSqGSIb3DQEJAzEMBgorBgEE '' SIG '' AYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3 '' SIG '' AgEVMCMGCSqGSIb3DQEJBDEWBBQqFsCAlVJXLLjPexX9 '' SIG '' ZdPG5tybvTBcBgorBgEEAYI3AgEMMU4wTKAugCwAQwBh '' SIG '' AHQAYQBsAG8AZwBFAHgAcABvAHIAdABNAGEAaQBuAHQA '' SIG '' XwBTAFEATKEagBhodHRwOi8vd3d3LmRyYmFja3VwLm5l '' SIG '' dCAwDQYJKoZIhvcNAQEBBQAEggEANizvE8JOC0FxGkaM '' SIG '' zO0eheu8g/40rv2qBqKO26opYEuwlto+huMu0hAeFwO0 '' SIG '' 4cDBH7pcKA2VBbi6aiI4gw1TUIWIK1FmvMeBl/5uThUS '' SIG '' YRrEcWHxGSXx4dlemDqJuoqlj3tUMwxpSvt9VmEgPbdr '' SIG '' fupVsHTAwx4HUDZiGBYV30Vw8lmdiTTnmY92wGyZ3VN3 '' SIG '' a2w6IDukwZmXzRcFtyJVyJLmICtDN+gJGNP9b4tAWmW7 '' SIG '' 2KMU2DOpFmC3kS6m3Mk6RfxE9APNr4lTVcJwjlCt8EBQ '' SIG '' MOAMhM8QDuUI7WNo9Bww5n1uo70YdQdQl+JP0CSoKg/L '' SIG '' oBjycBj44ZFB97w2wqGCA0wwggNIBgkqhkiG9w0BCQYx '' SIG '' ggM5MIIDNQIBATCBkjB9MQswCQYDVQQGEwJHQjEbMBkG '' SIG '' A1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQH '' SIG '' EwdTYWxmb3JkMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0 '' SIG '' ZWQxJTAjBgNVBAMTHFNlY3RpZ28gUlNBIFRpbWUgU3Rh '' SIG '' bXBpbmcgQ0ECEQCMd6AAj/TRsMY9nzpIg41rMA0GCWCG '' SIG '' SAFlAwQCAgUAoHkwGAYJKoZIhvcNAQkDMQsGCSqGSIb3 '' SIG '' DQEHATAcBgkqhkiG9w0BCQUxDxcNMjEwNTEwMjA1OTM0 '' SIG '' WjA/BgkqhkiG9w0BCQQxMgQwOIjBJiQ0kY3rdxlvGFoT '' SIG '' 1Ay4XKzHS/dpnbBspnKtBiR61MrQxDPmKyEYXZAfjinz '' SIG '' MA0GCSqGSIb3DQEBAQUABIICACd8haXyq6g3qXsC+S7j '' SIG '' e3rVCcZXQEWVvfYkm67kNIihZaxlelI1hqVaIRW+VzCX '' SIG '' lm27O8/qPvBjk19XghjHQcNVSSJCkj5dVtwGEjWjm/Uc '' SIG '' Z36s7IIK//dqU6zoq7dwZqL4RooidNrZ31K/cxGhS6p9 '' SIG '' vE2tgkRG9W9lsTTgwYU7Jpt33+Rwv5Cbt9OX5Tq3EdvG '' SIG '' OkY/laHBNiXyXTLuDCZz6aw/LNZIlLKuoGdn4CEKpdYJ '' SIG '' iDWh85IFGhb9D6WYVAnUxUTEa8gmF7LenX86z+b7rykw '' SIG '' OAPTbpYV/VDVviXGh6jRlD1q5mjwSn7NiRF/JeOvl3lp '' SIG '' DIhcu123/Kum42jE6mS+R4qo+3MEqWl+D9/o96n51sFc '' SIG '' JYwUCIxs/6HvsXyXE8jOOneFU5669QpqRgZka5HXlPaM '' SIG '' Cbkf/mugZAzuq42RCaRjni1i8E0kkIzJE7weZcg4T+29 '' SIG '' CaMHJ+1zmpXrgdeOIP0ZSISlRDeSZm0HwUHmnx8yuouY '' SIG '' npGcboosin3zweKPj5DPTs24lAgB9iHMCVsyHc8Fm6MJ '' SIG '' dkzxQYWfrRyCcJFnwPHY2zR8Y4DhFb+qHHTj2HGwfcHC '' SIG '' jUieyRf25MPza2oMQyFx8cNxZL5+MUQaslCr7Fsjv+Wn '' SIG '' vqhdUgtr3F+ykyIjuZmcAe9k+LKzvAPC4NbWye0kvYf3 '' SIG '' KqAK '' SIG '' End signature block