/// <summary> /// Event handler that's being executed by the threads processing the sites. Everything in here must be coded in a thread-safe manner /// </summary> private void SBScanner_TimerJobRun(object sender, TimerJobRunEventArgs e) { lock (scannedSitesLock) { ScannedSites++; } Console.WriteLine("Processing site {0}...", e.Url); try { if (!firstSiteCollectionDone) { firstSiteCollectionDone = true; // Telemetry e.WebClientContext.ClientTag = "SPDev:SBScanner"; e.WebClientContext.Load(e.WebClientContext.Web, p => p.Description); e.WebClientContext.ExecuteQuery(); } // Query the solution gallery CamlQuery camlQuery = CamlQuery.CreateAllItemsQuery(); ListItemCollection itemCollection = e.WebClientContext.Web.GetCatalog(121).GetItems(camlQuery); e.WebClientContext.Load(e.WebClientContext.Site, s => s.Id); e.WebClientContext.Load(itemCollection); e.WebClientContext.ExecuteQueryRetry(); string siteOwner = string.Empty; int totalSolutions = 0; int assemblySolutions = 0; int activeSolutions = 0; int activeAssemblySolutions = 0; foreach (ListItem item in itemCollection) { // We've found solutions totalSolutions++; bool status = false; if (item["Status"] != null) { activeSolutions++; status = true; } bool hasAssembly = false; foreach (string s in item["MetaInfo"].ToString().Split("\r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)) { if (s.Contains("SolutionHasAssemblies")) { if (s.Contains("1")) { assemblySolutions++; if (status) { activeAssemblySolutions++; } hasAssembly = true; } break; } } if (hasAssembly && string.IsNullOrEmpty(siteOwner)) { // Let's add site owners for the solutions which need our attention List <string> admins = new List <string>(); UserCollection users = e.WebClientContext.Web.SiteUsers; e.WebClientContext.Load(users); e.WebClientContext.ExecuteQueryRetry(); foreach (User u in users) { if (u.IsSiteAdmin) { if (!string.IsNullOrEmpty(u.Email) && u.Email.Contains("@")) { admins.Add(u.Email); } } } if (this.Separator == ";") { siteOwner = string.Join(",", admins.ToArray()); } else { siteOwner = string.Join(";", admins.ToArray()); } } SBScanResult result = new SBScanResult() { SiteURL = e.Url, SiteOwner = hasAssembly ? siteOwner : "", WSPName = item["FileLeafRef"].ToString(), Author = ((FieldUserValue)item["Author"]).LookupValue.Replace(",", ""), CreatedDate = Convert.ToDateTime(item["Created"]), Activated = status, HasAssemblies = hasAssembly, SolutionHash = item["SolutionHash"].ToString(), SolutionID = item["SolutionId"].ToString(), SiteId = e.WebClientContext.Site.Id.ToString(), }; // Doing more than a simple scan... if (Mode == Mode.scananddownload || Mode == Mode.scanandanalyze) { // Only download the solution when there's an assembly. By default we're only downloading and scanning each unique solution just once if (hasAssembly && (!SBProcessed.ContainsKey(result.SolutionHash) || Duplicates == true)) { // Add this solution hash to the dictionary SBProcessed.TryAdd(result.SolutionHash, ""); // Download the WSP package ClientResult <Stream> data = item.File.OpenBinaryStream(); e.WebClientContext.Load(item.File); e.WebClientContext.ExecuteQueryRetry(); if (data != null) { int position = 1; int bufferSize = 200000; Byte[] readBuffer = new Byte[bufferSize]; string localFilePath = System.IO.Path.Combine(".", this.OutputFolder, e.WebClientContext.Site.Id.ToString()); System.IO.Directory.CreateDirectory(localFilePath); string wspPath = System.IO.Path.Combine(localFilePath, item["FileLeafRef"].ToString()); using (System.IO.Stream stream = System.IO.File.Create(wspPath)) { while (position > 0) { // data.Value holds the Stream position = data.Value.Read(readBuffer, 0, bufferSize); stream.Write(readBuffer, 0, position); readBuffer = new Byte[bufferSize]; } stream.Flush(); } // Analyze the WSP package by cracking it open and looking inside if (Mode == Mode.scanandanalyze) { Analyzer analyzer = new Analyzer(); analyzer.Init(this.Verbose); var res = analyzer.ProcessFileInfo(System.IO.Path.GetFullPath(wspPath)); result.IsEmptyAssembly = (res.Assemblies.Count == 1 && res.Assemblies[0].ReferencedAssemblies.Count <= 1 && res.Assemblies[0].Classes.Count == 0); result.IsInfoPath = res.InfoPathSolution; result.HasWebParts = (res.WebPartsCount > 0) || (res.UserControlsCount > 0) || res.Features.Where(f => f.WebParts.Any()).Count() > 0; result.HasWebTemplate = res.Features.Where(f => f.WebTemplateDetails.Any()).Count() > 0; result.HasFeatureReceivers = res.FeatureReceiversCount > 0 || res.Features.Where(f => f.FeatureReceivers.Any()).Count() > 0; result.HasEventReceivers = res.EventHandlersCount > 0 || res.Features.Where(f => f.EventReceivers.Any()).Count() > 0; result.HasListDefinition = res.ListTemplatesCount > 0 || res.Features.Where(f => f.ListTemplates.Any()).Count() > 0; result.HasWorkflowAction = res.Features.Where(f => f.WorkflowActionDetails.Any()).Count() > 0; if (res.InfoPathSolution) { result.IsEmptyInfoPathAssembly = IsEmptyInfoPathAssembly(res); } // Dump the analysis results var serializer = new XmlSerializer(typeof(SolutionInformation)); using (var writer = new StreamWriter(System.IO.Path.Combine(System.IO.Path.GetDirectoryName(wspPath), (System.IO.Path.GetFileNameWithoutExtension(wspPath) + ".xml")))) { serializer.Serialize(writer, res); } // Create new package without assembly if (result.IsEmptyAssembly.Value) { string tempFolder = null; tempFolder = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString().Replace("-", "")); // unpack analyzer.UnCab(System.IO.Path.GetFullPath(wspPath), tempFolder); // delete all assemblies var filesToDelete = Directory.GetFiles(tempFolder, "*.*", SearchOption.AllDirectories).Where(s => s.EndsWith(".dll", StringComparison.InvariantCultureIgnoreCase)); foreach (var file in filesToDelete) { System.IO.File.Delete(file); } // repack (also deletes the temp folder) analyzer.ReCab(System.IO.Path.Combine(System.IO.Path.GetDirectoryName(wspPath), (System.IO.Path.GetFileNameWithoutExtension(wspPath) + "_fixed.wsp")), tempFolder); } } } } } this.SBScanResults.Push(result); } if (totalSolutions > 0) { Console.WriteLine("Site {0} processed. Found {1} solutions in total of which {2} have assemblies and are activated", e.Url, totalSolutions, activeAssemblySolutions); } } catch (Exception ex) { SBScanError error = new SBScanError() { Error = ex.Message, SiteURL = e.Url, }; this.SBScanErrors.Push(error); Console.WriteLine("Error for site {1}: {0}", ex.Message, e.Url); } }
/// <summary> /// Event handler that's being executed by the threads processing the sites. Everything in here must be coded in a thread-safe manner /// </summary> private void SBScanner_TimerJobRun(object sender, TimerJobRunEventArgs e) { lock (scannedSitesLock) { ScannedSites++; } Console.WriteLine("Processing site {0}...", e.Url); try { if (!firstSiteCollectionDone) { firstSiteCollectionDone = true; // Telemetry e.WebClientContext.ClientTag = "SPDev:SBScanner"; e.WebClientContext.Load(e.WebClientContext.Web, p => p.Description); e.WebClientContext.ExecuteQuery(); } // Query the solution gallery CamlQuery camlQuery = CamlQuery.CreateAllItemsQuery(); ListItemCollection itemCollection = e.WebClientContext.Web.GetCatalog(121).GetItems(camlQuery); e.WebClientContext.Load(e.WebClientContext.Site, s => s.Id); e.WebClientContext.Load(itemCollection); e.WebClientContext.ExecuteQueryRetry(); string siteOwner = string.Empty; int totalSolutions = 0; int assemblySolutions = 0; int activeSolutions = 0; int activeAssemblySolutions = 0; foreach (ListItem item in itemCollection) { // We've found solutions totalSolutions++; bool status = false; if (item["Status"] != null) { activeSolutions++; status = true; } bool hasAssembly = false; foreach (string s in item["MetaInfo"].ToString().Split("\r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)) { if (s.Contains("SolutionHasAssemblies")) { if (s.Contains("1")) { assemblySolutions++; if (status) { activeAssemblySolutions++; } hasAssembly = true; } break; } } if (hasAssembly && string.IsNullOrEmpty(siteOwner)) { // Let's add site owners for the solutions which need our attention List<string> admins = new List<string>(); UserCollection users = e.WebClientContext.Web.SiteUsers; e.WebClientContext.Load(users); e.WebClientContext.ExecuteQueryRetry(); foreach (User u in users) { if (u.IsSiteAdmin) { if (!string.IsNullOrEmpty(u.Email) && u.Email.Contains("@")) { admins.Add(u.Email); } } } if (this.Separator == ";") { siteOwner = string.Join(",", admins.ToArray()); } else { siteOwner = string.Join(";", admins.ToArray()); } } SBScanResult result = new SBScanResult() { SiteURL = e.Url, SiteOwner = hasAssembly ? siteOwner : "", WSPName = item["FileLeafRef"].ToString(), Author = ((FieldUserValue)item["Author"]).LookupValue.Replace(",", ""), CreatedDate = Convert.ToDateTime(item["Created"]), Activated = status, HasAssemblies = hasAssembly, SolutionHash = item["SolutionHash"] != null ? item["SolutionHash"].ToString() : "", SolutionID = item["SolutionId"] != null ? item["SolutionId"].ToString() : "", SiteId = e.WebClientContext.Site.Id.ToString(), }; // Doing more than a simple scan... if (Mode == Mode.scananddownload || Mode == Mode.scanandanalyze) { // Only download the solution when there's an assembly. By default we're only downloading and scanning each unique solution just once if (hasAssembly && (!SBProcessed.ContainsKey(result.SolutionHash) || Duplicates == true)) { // Add this solution hash to the dictionary SBProcessed.TryAdd(result.SolutionHash, ""); // Download the WSP package ClientResult<Stream> data = item.File.OpenBinaryStream(); e.WebClientContext.Load(item.File); e.WebClientContext.ExecuteQueryRetry(); if (data != null) { int position = 1; int bufferSize = 200000; Byte[] readBuffer = new Byte[bufferSize]; string localFilePath = System.IO.Path.Combine(".", this.OutputFolder, e.WebClientContext.Site.Id.ToString()); System.IO.Directory.CreateDirectory(localFilePath); string wspPath = System.IO.Path.Combine(localFilePath, item["FileLeafRef"].ToString()); using (System.IO.Stream stream = System.IO.File.Create(wspPath)) { while (position > 0) { // data.Value holds the Stream position = data.Value.Read(readBuffer, 0, bufferSize); stream.Write(readBuffer, 0, position); readBuffer = new Byte[bufferSize]; } stream.Flush(); } // Analyze the WSP package by cracking it open and looking inside if (Mode == Mode.scanandanalyze) { Analyzer analyzer = new Analyzer(); analyzer.Init(this.Verbose); var res = analyzer.ProcessFileInfo(System.IO.Path.GetFullPath(wspPath)); result.IsEmptyAssembly = (res.Assemblies.Count == 1 && res.Assemblies[0].ReferencedAssemblies.Count <= 1 && res.Assemblies[0].Classes.Count == 0); result.IsInfoPath = res.InfoPathSolution; result.HasWebParts = (res.WebPartsCount > 0) || (res.UserControlsCount > 0) || res.Features.Where(f => f.WebParts.Any()).Count() > 0; result.HasWebTemplate = res.Features.Where(f => f.WebTemplateDetails.Any()).Count() > 0; result.HasFeatureReceivers = res.FeatureReceiversCount > 0 || res.Features.Where(f => f.FeatureReceivers.Any()).Count() > 0; result.HasEventReceivers = res.EventHandlersCount > 0 || res.Features.Where(f => f.EventReceivers.Any()).Count() > 0; result.HasListDefinition = res.ListTemplatesCount > 0 || res.Features.Where(f => f.ListTemplates.Any()).Count() > 0; result.HasWorkflowAction = res.Features.Where(f => f.WorkflowActionDetails.Any()).Count() > 0; if (res.InfoPathSolution) { result.IsEmptyInfoPathAssembly = IsEmptyInfoPathAssembly(res); } // Dump the analysis results var serializer = new XmlSerializer(typeof(SolutionInformation)); using (var writer = new StreamWriter(System.IO.Path.Combine(System.IO.Path.GetDirectoryName(wspPath), (System.IO.Path.GetFileNameWithoutExtension(wspPath) + ".xml")))) { serializer.Serialize(writer, res); } // Create new package without assembly if (result.IsEmptyAssembly.Value) { string tempFolder = null; tempFolder = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString().Replace("-", "")); // unpack analyzer.UnCab(System.IO.Path.GetFullPath(wspPath), tempFolder); // delete all assemblies var filesToDelete = Directory.GetFiles(tempFolder, "*.*", SearchOption.AllDirectories).Where(s => s.EndsWith(".dll", StringComparison.InvariantCultureIgnoreCase)); foreach (var file in filesToDelete) { System.IO.File.Delete(file); } // repack (also deletes the temp folder) analyzer.ReCab(System.IO.Path.Combine(System.IO.Path.GetDirectoryName(wspPath), (System.IO.Path.GetFileNameWithoutExtension(wspPath) + "_fixed.wsp")), tempFolder); } } } } } this.SBScanResults.Push(result); } if (totalSolutions > 0) { Console.WriteLine("Site {0} processed. Found {1} solutions in total of which {2} have assemblies and are activated", e.Url, totalSolutions, activeAssemblySolutions); } } catch (Exception ex) { SBScanError error = new SBScanError() { Error = ex.Message, SiteURL = e.Url, }; this.SBScanErrors.Push(error); Console.WriteLine("Error for site {1}: {0}", ex.Message, e.Url); } }