Esempio n. 1
0
        /// <summary>
        /// Processes the amount of work that will be done for a single site/web
        /// </summary>
        /// <param name="site">Url of the site to process</param>
        private void DoWork(string site)
        {
            // Get the root site of the passed site
            string rootSite = GetRootSite(site);

            // Instantiate the needed ClientContext objects
            ClientContext ccWeb = CreateClientContext(site);
            ClientContext ccSite = null;

            if (rootSite.Equals(site, StringComparison.InvariantCultureIgnoreCase))
            {
                ccSite = ccWeb;
            }
            else
            {
                ccSite = CreateClientContext(rootSite);
            }

            #if !ONPREMISES
            // Instantiate ClientContext against tenant admin site, this is needed to operate using the Tenant API
            string tenantAdminSiteUrl = tenantAdminSite;
            if (string.IsNullOrEmpty(tenantAdminSiteUrl))
            {
                tenantAdminSiteUrl = GetTenantAdminSite(site);
            }
            ClientContext ccTenant = CreateClientContext(tenantAdminSiteUrl);
            #else
            // No easy way to detect tenant admin site in on-premises, so uses has to specify it
            ClientContext ccTenant = null;
            if (!String.IsNullOrEmpty(tenantAdminSite))
            {
                ccTenant = CreateClientContext(tenantAdminSite);
            }
            #endif

            // Prepare the timerjob callback event arguments
            TimerJobRunEventArgs e = new TimerJobRunEventArgs(site, ccSite, ccWeb, ccTenant, null, null, "", new Dictionary<string, string>(), this.ConfigurationData);

            // Trigger the event to fire, but only when there's an event handler connected
            if (TimerJobRun != null)
            {
                OnTimerJobRun(e);
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Triggers the event to fire and deals with all the pre/post processing needed to automatically manage state
        /// </summary>
        /// <param name="e">TimerJobRunEventArgs event arguments class that will be passed to the event handler</param>
        private void OnTimerJobRun(TimerJobRunEventArgs e)
        {
            try
            {
                // Copy for thread safety?
                TimerJobRunHandler timerJobRunHandlerThreadCopy = TimerJobRun;
                if (timerJobRunHandlerThreadCopy != null)
                {
                    PropertyValues props = null;
                    JavaScriptSerializer s = null;

                    // if state is managed then the state value is stored in a property named "<timerjobname>_Properties"
                    string propertyKey = String.Format("{0}_Properties", NormalizedTimerJobName(this.name));

                    // read the properties from the web property bag
                    if (this.manageState)
                    {
                        props = e.WebClientContext.Web.AllProperties;
                        e.WebClientContext.Load(props);
                        e.WebClientContext.ExecuteQueryRetry();

                        s = new JavaScriptSerializer();

                        // we've found previously stored state, so this is not the first timer job run
                        if (props.FieldValues.ContainsKey(propertyKey))
                        {
                            string timerJobProps = props.FieldValues[propertyKey].ToString();

                            // We should have a value, but you never know...
                            if (!string.IsNullOrEmpty(timerJobProps))
                            {
                                // Deserialize the json string into a TimerJobRun class instance
                                TimerJobRun timerJobRunProperties = s.Deserialize<TimerJobRun>(timerJobProps);

                                // Pass the state information as part of the event arguments
                                if (timerJobRunProperties != null)
                                {
                                    e.PreviousRun = timerJobRunProperties.PreviousRun;
                                    e.PreviousRunSuccessful = timerJobRunProperties.PreviousRunSuccessful;
                                    e.PreviousRunVersion = timerJobRunProperties.PreviousRunVersion;
                                    e.Properties = timerJobRunProperties.Properties;
                                }
                            }
                        }
                    }

                    // trigger the event
                    timerJobRunHandlerThreadCopy(this, e);

                    // Update and store the properties to the web property bag
                    if (this.manageState)
                    {
                        // Retrieve the values of the event arguments and complete them with defaults
                        TimerJobRun timerJobRunProperties = new TimerJobRun()
                        {
                            PreviousRun = DateTime.Now,
                            PreviousRunSuccessful = e.CurrentRunSuccessful,
                            PreviousRunVersion = this.version,
                            Properties = e.Properties,
                        };

                        // Serialize to json string
                        string timerJobProps = s.Serialize(timerJobRunProperties);

                        props = e.WebClientContext.Web.AllProperties;

                        // Get the value, if the web properties are already loaded
                        if (props.FieldValues.Count > 0)
                        {
                            props[propertyKey] = timerJobProps;
                        }
                        else
                        {
                            // Load the web properties
                            e.WebClientContext.Load(props);
                            e.WebClientContext.ExecuteQueryRetry();

                            props[propertyKey] = timerJobProps;
                        }

                        // Persist the web property bag entries
                        e.WebClientContext.Web.Update();
                        e.WebClientContext.ExecuteQueryRetry();
                    }

                }
            }
            catch (Exception ex)
            {
                // Catch error in this case as we don't want to the whole program to terminate if one single site operation fails
            }
        }
Esempio n. 3
0
        /// <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);
            }
        }