'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 CONST RBSProvider = "Dr.Backup" CONST Version = "(v2.9.1)" CONST MAXVAL = 10000 '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] '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 '------------------------------------------------------------------------------------------------------------------ '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. '** 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 strCommand = "%comspec% /k " & ScriptEXE & " //nologo """ & strPath & """" & " 1 2" 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 Resume Next Err.Clear '** Let's get started ** Wscript.StdOut.WriteLine(RBSProvider & " - Export Catalog Analysis " & Version & VbCrLF) Wscript.StdOut.WriteLine("Start : " & Now() & VbCr) '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 'All that to get the location of the backup.mdb database Database = strValue & "\" & "backup.mdb" '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 = strValue & "\Custom\" & "ChilkatFTP.dll" 'troubleshooting prompt to dislay working database location 'Wscript.StdOut.WriteLine("Processing Database: " & Database) 'FTP option enabled? CONST YESNO = 4 '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-30): ","Delete File Restrictions", 0) If CInt(PromptDay) > 30 or CInt(PromptDay) < 0 Then DaysBeforeDelete = 0 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 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 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) '3/29/2013 - If we are going to be marked entries for delete, we need to add the delete 'field to the catalog so that the new GUI will work. Easier to just do this than to check 'to see if already done. If ADDDELETECOLUMN = TRUE Then 'let's attempt to add field to table. ADDFIELD = "ALTER TABLE Catalog ADD Delete YESNO ;" 'open connection and update database structure SET objConnAlter = CreateObject("ADODB.Connection") objConnAlter.Open "Provider=MSDASQL; DRIVER={Microsoft Access Driver (*.mdb)};DBQ=" & DataBase & ";UID=admin;PWD= ;" objConnAlter.Execute ADDFIELD If err.number <> 0 Then 'Wscript.StdOut.Writeline("ALTER TABLE ERROR: " & err.number & " Message: " & err.description) err.clear Else 'Wscript.StdOut.Writeline("INFO: Delete field added to Catalog.") objConnAlter.Close End If End If 'open up a connection to database, so we can use updatable recordsets SET objConn = CreateObject("ADODB.Connection") objConn.Open "Provider=MSDASQL; DRIVER={Microsoft Access Driver (*.mdb)};DBQ=" & DataBase & ";UID=admin;PWD= ;" '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. 'Create recordset for backupset enumeration Set rsSetname = CreateObject ("ADODB.Recordset") strConnectSetname = "Provider=MSDASQL; DRIVER={Microsoft Access Driver (*.mdb)};DBQ=" & DataBase & ";UID=admin;PWD=;" rsSetname.ActiveConnection = strConnectSetname rsSetname.CursorLocation = 3 rsSetname.CursorType = 3 sqlSetname = "Select * from SETNAMES" rsSetname.open sqlSetname 'enumerate through all backup sets in catalog Do Until rsSetname.EOF 'Wscript.Stdout.Writeline("SETID = " & rsSetname("ID")) 'Open up catalog for reading Set rsFileEntry = CreateObject("ADODB.Recordset") 'put in advanced SQL query to backup location field for cloud storage sqlStmt = GetSQLQuery(2, rsSetname("ID")) 'Wscript.StdOut.Writeline("SQL: " & sqlStmt) err.clear Supports117 = TRUE 'flag that says full feature support available including cloud storage (backuplocation) rsFileEntry.open sqlStmt, objConn, 3, 3 'if all catalog features supported, we are done. otherwise, scale it back to older catalog format If Err.Number <> 0 Then If FirstPass = TRUE then Wscript.StdOut.Writeline("WARN: Old catalog doesn't support 'deleted' field. Using earlier catalog version.") End If Err.clear Supports117 = FALSE sqlStmt = GetSQLQuery(1, rsSetname("ID")) rsFileEntry.open sqlStmt, ObjConn, 3, 3 End If 'If non-complex query fails, there is probably catalog trouble, 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 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 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 FullFilename = "" 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 'Just a simple file. Load into Fullfilename field FullFilename = RsFileEntry("Filename") End If 'check if file exists on disk If Len(FullFilename & " ") <> 1 Then 'filename in catalog. Check disk '3/27/2013 - bypass the on disk checking here If CHECKFORDELETES = FALSE Then 'We don't need to do any delete checks Deleted = "No" ElseIf objFSO.FileExists(Fullfilename) Then 'Simple case. file on disk. nothing to do. Deleted = "No" Else 'If this is NOT a plugin, (empty or NULL string) then it is a deleted file If Len(RsFileEntry("Plugin") & " ") = 1 Then 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 'Wscript.StdOut.Writeline("Deleted: " & fullfilename) 'update deleted statistics 'Remote Server 'Wscript.StdOut.Writeline("StorageLoc=" & StorageLoc) If Instr(StorageLoc, "R") > 0 Then 'Remote server is a location. update deleted stats FilesDeletedR = FilesDeletedR + 1 FilesDeletedSizeR = FilesDeletedSizeR + RsFileEntry("PrepSize") FilesDeletedOriginalSizeR = FilesDeletedOriginalSizeR + RsFileEntry("OrigSize") End If 'Local Mirror If Instr(StorageLoc, "M") > 0 Then 'Mirror server is a location. update deleted stats FilesDeletedM = FilesDeletedM + 1 FilesDeletedSizeM = FilesDeletedSizeM + RsFileEntry("PrepSize") FilesDeletedOriginalSizeM = FilesDeletedOriginalSizeM + RsFileEntry("OrigSize") End If 'Private Cloud Storage If Instr(StorageLoc, "C") > 0 Then 'Cloud storage is a location. update deleted stats FilesDeletedC = FilesDeletedC + 1 FilesDeletedSizeC = FilesDeletedSizeC + RsFileEntry("PrepSize") FilesDeletedOriginalSizeC = FilesDeletedOriginalSizeC + RsFileEntry("OrigSize") End If Else 'This is a plugin. Ignore it as plugins are not ordinary user files. Deleted = "No" End If End If 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 + rsFileEntry("PrepSize") OriginalSizeR = OriginalSizeR + RsFileEntry("OrigSize") FileCountR = FileCountR + 1 End If If Instr(StorageLoc, "M") > 0 Then 'Local Mirror is a location. update stats CompressedSizeM = CompressedSizeM + rsFileEntry("PrepSize") OriginalSizeM = OriginalSizeM + RsFileEntry("OrigSize") FileCountM = FileCountM + 1 End If If Instr(StorageLoc, "C") > 0 Then 'Cloud storage is a location. update stats CompressedSizeC = CompressedSizeC + rsFileEntry("PrepSize") OriginalSizeC = OriginalSizeC + RsFileEntry("OrigSize") FileCountC = FileCountC + 1 End If 'summary totals updated CompressedSizeT = CompressedSizeT + rsFileEntry("PrepSize") OriginalSizeT = OriginalSizeT + RsFileEntry("OrigSize") FileCountT = FileCountT + 1 'Keep track if we have long filenames - may be troublesome to ultimately restore unless to orig path If Len(FullFilename) > LongFileLength 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 objFile.Writeline """" & Fullfilename & """,""" & _ RsFileEntry("PrepSize") & """,""" & RsFileentry("OrigSize") & """,""" & _ RsFileEntry("DateStamp") & """,""" & RsFileEntry("SetnameID") & """,""" & _ RsFileEntry("SubFolder") & """,""" & RsFileEntry("FullPFolder") & """,""" & _ GetExtension(FullFilename) & """,""" & _ Deleted & """" & LocationStr UpdateExtStats GetExtension(FullFilename), RsFileentry("OrigSize"), 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 rsFileEntry.MoveNext Loop 'files within backup set rsFileEntry.Close rsSetname.MoveNext 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("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 (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() For i = 0 to NextExt -1 If i <= maxdisplay -1 then Wscript.StdOut.Writeline(LeftJustify(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)) End If objstatsFile.Writeline """" & extstr(i) & """,""" & _ extcount(i) & """,""" & extcbytes(i) & """,""" & _ extbytes(i) & """" Next 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 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("Please download http://www.drbackup.net/web/chilkatftp.dll to " & FTPDLL & " and retry script." ) Wscript.StdOut.Writeline("File not transfered." ) End If End If Wscript.StdOut.WriteLine(VbCrLf & VbCRLf& "Catalog Export Analysis Completed: " & Now() & VbCrLf) 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 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.File, Catalog.Restore, Catalog.Status, Catalog.Attributes, Catalog.Filename, " & _ "Catalog.StoreMethod, Catalog.StoreName, Catalog.LongPathID, Catalog.OrigSize, " & _ "Catalog.PrepSize, Catalog.TrfResult, Catalog.PrepResult, Catalog.Patch, " & _ "Catalog.FullPFolder, LongPaths.LongPath, IIf(IsNull([LongPath]),[Filename], " & _ "[LongPath]) 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.File, Catalog.Restore, Catalog.Status, Catalog.Attributes, Catalog.Filename, " & _ "Catalog.StoreMethod, Catalog.StoreName, Catalog.LongPathID, Catalog.OrigSize, " & _ "Catalog.PrepSize, Catalog.TrfResult, Catalog.PrepResult, Catalog.Patch, " & _ "Catalog.FullPFolder, Catalog.BackupLocation, Catalog.Delete, LongPaths.LongPath, IIf(IsNull([LongPath]),[Filename], " & _ "[LongPath]) 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 '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 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")) 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 = Cint(mid(master, p2, p3-p2)) End If 'wscript.echo Cint(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 Exit Function End Function '' SIG '' Begin signature block '' SIG '' MIIVKgYJKoZIhvcNAQcCoIIVGzCCFRcCAQExCzAJBgUr '' SIG '' DgMCGgUAMGcGCisGAQQBgjcCAQSgWTBXMDIGCisGAQQB '' SIG '' gjcCAR4wJAIBAQQQTvApFpkntU2P5azhDxfrqwIBAAIB '' SIG '' AAIBAAIBAAIBADAhMAkGBSsOAwIaBQAEFFKyJQOUr6wd '' SIG '' 8NspexUwJbX1jdfroIIPzTCCBJQwggN8oAMCAQICEQCf '' SIG '' 6sgRsPFiR6X8INgFI6zmMA0GCSqGSIb3DQEBBQUAMIGV '' SIG '' MQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNV '' SIG '' BAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUg '' SIG '' VVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6 '' SIG '' Ly93d3cudXNlcnRydXN0LmNvbTEdMBsGA1UEAxMUVVRO '' SIG '' LVVTRVJGaXJzdC1PYmplY3QwHhcNMTUwNTA1MDAwMDAw '' SIG '' WhcNMTUxMjMxMjM1OTU5WjB+MQswCQYDVQQGEwJHQjEb '' SIG '' MBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYD '' SIG '' VQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01PRE8gQ0Eg '' SIG '' TGltaXRlZDEkMCIGA1UEAxMbQ09NT0RPIFRpbWUgU3Rh '' SIG '' bXBpbmcgU2lnbmVyMIIBIjANBgkqhkiG9w0BAQEFAAOC '' SIG '' AQ8AMIIBCgKCAQEAvDWgNnAigRHDsoO50yjGNs0la6l7 '' SIG '' shz2m1Gc7zX07QiOXjgI+Hc8CkLg83Dco9fK9UwLz/8i '' SIG '' nAp+aNYJoiqEe6adtKnBM+LvHxdIyjrNRubFqne943ea '' SIG '' +kdTQChZQ5PxpIHq74C1T6cIzrpuvMp2DJdkWYYkuz2C '' SIG '' kKhVsZLToKcFrJ9TJQgQR5nNmN5o5bRQeKOvAcxZQ1jk '' SIG '' dm5+rMfinh9PsEctyAyjSSeAdYy7BpFlD5Cb9LrRgchc '' SIG '' auwU6SUJvyMW9JVGQEAhu4OW/YYfesgNEI6i+BkHWH+f '' SIG '' vTcCYPKk6Z1EPzAF5KdwmVGa6BfxVcqyYYllRqdq8lhG '' SIG '' fqqgBwIDAQABo4H0MIHxMB8GA1UdIwQYMBaAFNrtZHQU '' SIG '' nBQ8q92Zqb1bKE2LPMnYMB0GA1UdDgQWBBQuLbAKRErT '' SIG '' h8ACB86XfVBiIP0PgzAOBgNVHQ8BAf8EBAMCBsAwDAYD '' SIG '' VR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcD '' SIG '' CDBCBgNVHR8EOzA5MDegNaAzhjFodHRwOi8vY3JsLnVz '' SIG '' ZXJ0cnVzdC5jb20vVVROLVVTRVJGaXJzdC1PYmplY3Qu '' SIG '' Y3JsMDUGCCsGAQUFBwEBBCkwJzAlBggrBgEFBQcwAYYZ '' SIG '' aHR0cDovL29jc3AudXNlcnRydXN0LmNvbTANBgkqhkiG '' SIG '' 9w0BAQUFAAOCAQEADbutYBEbtfANzOZIOno+DjPcHLnq '' SIG '' 1iD+o03QzHZO6BjYed/TT5pCZCOKKXKKOmxmpjw6F6hw '' SIG '' RWXGc8PQzolU+6xpD1iwGcuGn365futRkr+b3evRZfAl '' SIG '' e4h83r2lyLUUUbzAgTCKhTh75nn+Z1WTh/5P6I0O7fNy '' SIG '' krXCiYBt0VnjHQ3qsTjuA50AGaWrIZt5w8zCPmh+vclN '' SIG '' aU20ZFH7sih04lOJzp36reLbzqt7fgZEdP0Ko8m3pzDN '' SIG '' SdKSZPEiprgoRXR56afOOzP5g1CUfWjAHUnHYHh6PGQm '' SIG '' 1b76Cm3kHuEJU4+pxSOsx51hQiHwLBZxSTsQryxvGuYx '' SIG '' 8RT9bDCCBU0wggQ1oAMCAQICEG3QYcfzk0L4XMBRqz9O '' SIG '' CTwwDQYJKoZIhvcNAQELBQAwfTELMAkGA1UEBhMCR0Ix '' SIG '' GzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G '' SIG '' A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENB '' SIG '' IExpbWl0ZWQxIzAhBgNVBAMTGkNPTU9ETyBSU0EgQ29k '' SIG '' ZSBTaWduaW5nIENBMB4XDTE1MDUwNzAwMDAwMFoXDTE3 '' SIG '' MDUwNjIzNTk1OVowgZUxCzAJBgNVBAYTAlVTMQ4wDAYD '' SIG '' VQQRDAUyMDcyMzELMAkGA1UECAwCTUQxDzANBgNVBAcM '' SIG '' BkxhdXJlbDEeMBwGA1UECQwVODQwNSBDaGVycnkgTGF1 '' SIG '' cmVsIEN0MRswGQYDVQQKDBJEb2N0b3IgQmFja3VwLCBM '' SIG '' TEMxGzAZBgNVBAMMEkRvY3RvciBCYWNrdXAsIExMQzCC '' SIG '' ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOfR '' SIG '' LlbkVyhXNDEx7LSMn+nO46KymNcdySoMgqrUPzZ9S7iR '' SIG '' 4VPIJE6LP/xf+SxAAdtEtit/xpQ6aMuZXzBg6lETjwDT '' SIG '' xlFeKqXgemCLSIkR7sQdyrbqwkLWJiVq8uLLgBdKmMG+ '' SIG '' rqi+fF3RbBkklguesBkcWrL8q+EnfNiMNhdXJAuqmmkd '' SIG '' RFvfDgJSMnWf6NDZ9n6GfF6SGcQB/PWUT16lH2WdHo4G '' SIG '' 960T3l/Rp98za31SmYV/tPKFdmHcxGYDmRoLC8b+bI1B '' SIG '' qWo4y+sSi4Clq4P+ViHjRRJZ22d74vzJqNtkivaxD+Sa '' SIG '' X6ZEkDHsapnrDYuTTVqSatVBP5TOMvMCAwEAAaOCAa4w '' SIG '' ggGqMB8GA1UdIwQYMBaAFCmRYP+KTfrr+aZquM/55ku9 '' SIG '' Sc4SMB0GA1UdDgQWBBTNJAO80tucPCkFDpOOEEFCVvE1 '' SIG '' WTAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAT '' SIG '' BgNVHSUEDDAKBggrBgEFBQcDAzARBglghkgBhvhCAQEE '' SIG '' BAMCBBAwRgYDVR0gBD8wPTA7BgwrBgEEAbIxAQIBAwIw '' SIG '' KzApBggrBgEFBQcCARYdaHR0cHM6Ly9zZWN1cmUuY29t '' SIG '' b2RvLm5ldC9DUFMwQwYDVR0fBDwwOjA4oDagNIYyaHR0 '' SIG '' cDovL2NybC5jb21vZG9jYS5jb20vQ09NT0RPUlNBQ29k '' SIG '' ZVNpZ25pbmdDQS5jcmwwdAYIKwYBBQUHAQEEaDBmMD4G '' SIG '' CCsGAQUFBzAChjJodHRwOi8vY3J0LmNvbW9kb2NhLmNv '' SIG '' bS9DT01PRE9SU0FDb2RlU2lnbmluZ0NBLmNydDAkBggr '' SIG '' BgEFBQcwAYYYaHR0cDovL29jc3AuY29tb2RvY2EuY29t '' SIG '' MB8GA1UdEQQYMBaBFHN1cHBvcnRAZHJiYWNrdXAubmV0 '' SIG '' MA0GCSqGSIb3DQEBCwUAA4IBAQAcOnP6LKI3q8ltsgkD '' SIG '' FwZOTjorCu2qw8QG5biTyjyerj0S2EBW8dlusXffpa1l '' SIG '' R9tMcQzD4HXna+XkeGWpay+YE6gmUpFXw2PjFzI/udBT '' SIG '' gs0/3fCIPw9EukICD9aZ55G6T3PHuRTRQJUeWToQrLT0 '' SIG '' 3MWIa+mEvkJVj1RCw52p0kV9BLyf6dCnIm8/mjiOXxkA '' SIG '' hJz3apZhz4AUC8oTEMNfYKX9pXKSNpHM3xa8N9QyKCBW '' SIG '' Eb9UVwf0Ob0bkp2nZ8hVKCJ1ALCqdGhsmBAnlXceopXN '' SIG '' zY1t1KSAu6UxD91CqxYyYZ+PK8Wy4AM2K02C1GkDTmE+ '' SIG '' lm2GBwVr2HCAKwfVMIIF4DCCA8igAwIBAgIQLnyHzA6T '' SIG '' SlL+lP0ct800rzANBgkqhkiG9w0BAQwFADCBhTELMAkG '' SIG '' A1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hl '' SIG '' c3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR '' SIG '' Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9E '' SIG '' TyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcN '' SIG '' MTMwNTA5MDAwMDAwWhcNMjgwNTA4MjM1OTU5WjB9MQsw '' SIG '' CQYDVQQGEwJHQjEbMBkGA1UECBMSR3JlYXRlciBNYW5j '' SIG '' aGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQK '' SIG '' ExFDT01PRE8gQ0EgTGltaXRlZDEjMCEGA1UEAxMaQ09N '' SIG '' T0RPIFJTQSBDb2RlIFNpZ25pbmcgQ0EwggEiMA0GCSqG '' SIG '' SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCmmJBjd5E0f4rR '' SIG '' 3elnMRHrzB79MR2zuWJXP5O8W+OfHiQyESdrvFGRp8+e '' SIG '' niWzX4GoGA8dHiAwDvthe4YJs+P9omidHCydv3Lj5HWg '' SIG '' 5TUjjsmK7hoMZMfYQqF7tVIDSzqwjiNLS2PgIpQ3e9V5 '' SIG '' kAoUGFEs5v7BEvAcP2FhCoyi3PbDMKrNKBh1SMF5WgjN '' SIG '' u4xVjPfUdpA6M0ZQc5hc9IVKaw+A3V7Wvf2pL8Al9fl4 '' SIG '' 141fEMJEVTyQPDFGy3CuB6kK46/BAW+QGiPiXzjbxghd '' SIG '' R7ODQfAuADcUuRKqeZJSzYcPe9hiKaR+ML0btYxytEjy '' SIG '' 4+gh+V5MYnmLAgaff9ULAgMBAAGjggFRMIIBTTAfBgNV '' SIG '' HSMEGDAWgBS7r34CPfqm8TyEjq3uOJjs2TIy1DAdBgNV '' SIG '' HQ4EFgQUKZFg/4pN+uv5pmq4z/nmS71JzhIwDgYDVR0P '' SIG '' AQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAwEwYD '' SIG '' VR0lBAwwCgYIKwYBBQUHAwMwEQYDVR0gBAowCDAGBgRV '' SIG '' HSAAMEwGA1UdHwRFMEMwQaA/oD2GO2h0dHA6Ly9jcmwu '' SIG '' Y29tb2RvY2EuY29tL0NPTU9ET1JTQUNlcnRpZmljYXRp '' SIG '' b25BdXRob3JpdHkuY3JsMHEGCCsGAQUFBwEBBGUwYzA7 '' SIG '' BggrBgEFBQcwAoYvaHR0cDovL2NydC5jb21vZG9jYS5j '' SIG '' b20vQ09NT0RPUlNBQWRkVHJ1c3RDQS5jcnQwJAYIKwYB '' SIG '' BQUHMAGGGGh0dHA6Ly9vY3NwLmNvbW9kb2NhLmNvbTAN '' SIG '' BgkqhkiG9w0BAQwFAAOCAgEAAj8COcPu+Mo7id4MbU2x '' SIG '' 8U6ST6/COCwEzMVjEasJY6+rotcCP8xvGcM91hoIlP8l '' SIG '' 2KmIpysQGuCbsQciGlEcOtTh6Qm/5iR0rx57FjFuI+9U '' SIG '' US1SAuJ1CAVM8bdR4VEAxof2bO4QRHZXavHfWGshqknU '' SIG '' fDdOvf+2dVRAGDZXZxHNTwLk/vPa/HUX2+y392UJI0kf '' SIG '' Q1eD6n4gd2HITfK7ZU2o94VFB696aSdlkClAi997OlE5 '' SIG '' jKgfcHmtbUIgos8MbAOMTM1zB5TnWo46BLqioXwfy2M6 '' SIG '' FafUFRunUkcyqfS/ZEfRqh9TTjIwc8Jvt3iCnVz/Rrtr '' SIG '' Ih2IC/gbqjSm/Iz13X9ljIwxVzHQNuxHoc/Li6jvHBhY '' SIG '' xQZ3ykubUa9MCEp6j+KjUuKOjswm5LLY5TjCqO3GgZw1 '' SIG '' a6lYYUoKl7RLQrZVnb6Z53BtWfhtKgx/GWBfDJqIbDCs '' SIG '' UgmQFhv/K53b0CDKieoofjKOGd97SDMe12X4rsn4gxST '' SIG '' dn1k0I7OvjV9/3IxTZ+evR5sL6iPDAZQ+4wns3bJ9ObX '' SIG '' wzTijIchhmH+v1V04SF3AwpobLvkyanmz1kl63zsRQ55 '' SIG '' ZmjoIs2475iFTZYRPAmK0H+8KCgT+2rKVI2SXM3CZZgG '' SIG '' ns5IW9S1N5NGQXwH3c/6Q++6Z2H/fUnguzB9XIDj5hY5 '' SIG '' S6cxggTJMIIExQIBATCBkTB9MQswCQYDVQQGEwJHQjEb '' SIG '' MBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYD '' SIG '' VQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01PRE8gQ0Eg '' SIG '' TGltaXRlZDEjMCEGA1UEAxMaQ09NT0RPIFJTQSBDb2Rl '' SIG '' IFNpZ25pbmcgQ0ECEG3QYcfzk0L4XMBRqz9OCTwwCQYF '' SIG '' Kw4DAhoFAKCBxDAZBgkqhkiG9w0BCQMxDAYKKwYBBAGC '' SIG '' NwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIB '' SIG '' FTAjBgkqhkiG9w0BCQQxFgQU3N4FGg7l/ZBk2AwhVP4M '' SIG '' gizwtlkwZAYKKwYBBAGCNwIBDDFWMFSgNoA0AEMAYQB0 '' SIG '' AGEAbABvAGcAIABFAHgAcABvAHIAdAAgAE0AYQBpAG4A '' SIG '' dABlAG4AYQBuAGMAZaEagBhodHRwOi8vd3d3LmRyYmFj '' SIG '' a3VwLm5ldCAwDQYJKoZIhvcNAQEBBQAEggEAU9KuFzG3 '' SIG '' UM/B5fW6l4CE5FIkcZ8yQA89EO+eenliE4CuOyZ10Pvr '' SIG '' IatOoVq6Vw/wu/ik0ztu1FE4vNnR0SJnUUkOeAEwC63x '' SIG '' vnADZci3+3DqTElJNPgS2s8+Rcf1sU3AYm1Ttf+5D0IP '' SIG '' 5GbmQzI9eyQiJk38uwL3FrvgxcD1R2IzL+cCfIf6X5gJ '' SIG '' Ca1s3+y0jYSEd0tAuZDokQmRHdtHT//So2HZ2HXHYbgv '' SIG '' /sy/aww3xooaXhAAlL2D6iKeRkgNm3CPjOGk+onRrLRu '' SIG '' 7rM74NBxVyfpY0DwbpoyihGykQcyP0NNq0uTDEIZJdJc '' SIG '' mkf3yQoiPNucc/u5o5c3uojTzqGCAkUwggJBBgkqhkiG '' SIG '' 9w0BCQYxggIyMIICLgIBADCBqzCBlTELMAkGA1UEBhMC '' SIG '' VVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExh '' SIG '' a2UgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBO '' SIG '' ZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0 '' SIG '' cnVzdC5jb20xHTAbBgNVBAMTFFVUTi1VU0VSRmlyc3Qt '' SIG '' T2JqZWN0AhEAn+rIEbDxYkel/CDYBSOs5jAJBgUrDgMC '' SIG '' GgUAoF0wGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAc '' SIG '' BgkqhkiG9w0BCQUxDxcNMTUxMDMwMTY1MDA4WjAjBgkq '' SIG '' hkiG9w0BCQQxFgQUThI7Bm7YMhQdTuLLgBDH1wSk6tsw '' SIG '' DQYJKoZIhvcNAQEBBQAEggEAmHqHJu5j7DZgNl3StMVD '' SIG '' x/4WKwACU+C/JkPqksjdLnGqJ9JnmkTt38tEyHC7MEvf '' SIG '' XI7cKUBsPBc4uf2yfCMz+hGuzU8Ezyl+vKi9Rw59SDqq '' SIG '' 2DwqolObu6LaTE75vZehl5ErlEw2+hIGiJ0YNcUzFe9k '' SIG '' dWD0hkgDi1DcZpcIC7AwBMUi4dArMu0HXZYKsjEkhkeI '' SIG '' uBn3CUcuqNWd4ylxUckv58uCs+ZJtM3rCn+j/VILVcEe '' SIG '' N2LurBzpI+d5X3NSr6fTS8oSIDCfUF/ZDluxPcP40JHb '' SIG '' juJNY9LaZ6hdjN+AQ6TTegnFa6jlqXF7lpIXeDIzuN6z '' SIG '' wbsh/sDhRqKXtA== '' SIG '' End signature block