/// <summary>
 /// Handle requests to sync updates. A client presents the list of installed updates and detectoids and the server
 /// replies with a list of more applicable updates, if any.
 /// </summary>
 /// <param name="cookie">Access cookie</param>
 /// <param name="parameters">Request parameters: list of installed updates, list of known updates, etc.</param>
 /// <returns>SyncInfo containing updates applicable to the caller.</returns>
 public Task <SyncInfo> SyncUpdatesAsync(Cookie cookie, SyncUpdateParameters parameters)
 {
     if (parameters.SkipSoftwareSync)
     {
         return(DoDriversSync(parameters));
     }
     else
     {
         return(DoSoftwareUpdateSync(parameters));
     }
 }
        /// <summary>
        /// Handle software sync request from a client
        /// </summary>
        /// <param name="parameters">Sync parameters</param>
        /// <returns></returns>
        private Task <SyncInfo> DoSoftwareUpdateSync(SyncUpdateParameters parameters)
        {
            MetadataSourceLock.EnterReadLock();

            if (MetadataSource == null)
            {
                throw new FaultException();
            }

            // Get list of installed non leaf updates; these are prerequisites that the client has installed.
            // This list is used to check what updates are applicable to the client
            // We will not send updates that already appear on this list
            var installedNonLeafUpdatesGuids = GetInstalledNotLeafGuidsFromSyncParameters(parameters);

            // Other known updates to the client; we will not send any updates that are on this list
            var otherCachedUpdatesGuids = GetOtherCachedUpdateGuidsFromSyncParameters(parameters);

            // Initialize the response
            var response = new SyncInfo()
            {
                NewCookie = new Cookie()
                {
                    Expiration = DateTime.Now.AddDays(5), EncryptedData = new byte[12]
                },
                DriverSyncNotNeeded = "false"
            };

            // Add root updates first; if any new root updates were added, return the response to the client immediatelly
            AddMissingRootUpdatesToSyncUpdatesResponse(installedNonLeafUpdatesGuids, otherCachedUpdatesGuids, response, out bool rootUpdatesAdded);
            if (!rootUpdatesAdded)
            {
                // No root updates were added; add non-leaf updates now
                AddMissingNonLeafUpdatesToSyncUpdatesResponse(installedNonLeafUpdatesGuids, otherCachedUpdatesGuids, response, out bool nonLeafUpdatesAdded);
                if (!nonLeafUpdatesAdded)
                {
                    // No leaf updates were added; add leaf bundle updates now
                    AddMissingBundleUpdatesToSyncUpdatesResponse(installedNonLeafUpdatesGuids, otherCachedUpdatesGuids, response, out bool bundleUpdatesAdded);
                    if (!bundleUpdatesAdded)
                    {
                        // No bundles were added; finally add leaf software updates
                        AddMissingSoftwareUpdatesToSyncUpdatesResponse(installedNonLeafUpdatesGuids, otherCachedUpdatesGuids, response, out bool softwareUpdatesAdded);
                    }
                }
            }

            MetadataSourceLock.ExitReadLock();

            return(Task.FromResult(response));
        }
        /// <summary>
        /// Handle driver sync requests
        /// </summary>
        /// <param name="parameters"></param>
        /// <returns></returns>
        private Task <SyncInfo> DoDriversSync(SyncUpdateParameters parameters)
        {
            // Get list of driver updates known to the client
            var cachedDrivers = GetUpdateIdentitiesFromClientIndexes(parameters.CachedDriverIDs);

            // Get list of installed non-leaf updates. Used to match pre-requisites for driver updates
            var installedNonLeafUpdatesGuids = GetInstalledNotLeafGuidsFromSyncParameters(parameters);

            // Initialize the response
            var syncResult = new SyncInfo()
            {
                NewCookie = new Cookie()
                {
                    Expiration = DateTime.Now.AddDays(5), EncryptedData = new byte[12]
                },
                DriverSyncNotNeeded = "false",
                Truncated           = false
            };

            List <Guid> computerHardwareIds = parameters.ComputerSpec.HardwareIDs != null?parameters.ComputerSpec.HardwareIDs.ToList() : new List <Guid>();

            List <UpdateInfo> driverUpdates = new List <UpdateInfo>();

            List <Update> unapprovedDriversMatched = new List <Update>();

            // Go through all client reported devices
            foreach (var device in parameters.SystemSpec)
            {
                // Combine the list hardware ids and compatible hwids; we will
                // match them in this order, from specific to less specific
                var hardwareIdsToMatch = new List <string>(device.HardwareIDs);
                if (device.CompatibleIDs != null)
                {
                    hardwareIdsToMatch.AddRange(device.CompatibleIDs);
                }

                // Get best match driver
                var driverMatchResult = MetadataSource.MatchDriver(hardwareIdsToMatch, computerHardwareIds, installedNonLeafUpdatesGuids);

                // If we have a match and the client does not have it, add it to the list
                if (driverMatchResult != null &&
                    !cachedDrivers.Contains(driverMatchResult.Driver.Identity) &&
                    !IsInstalledDriverBetterMatch(device.installedDriver, driverMatchResult, hardwareIdsToMatch, computerHardwareIds))
                {
                    if (ApprovedSoftwareUpdates.Contains(driverMatchResult.Driver.Identity))
                    {
                        // Get core XML fragment for driver update
                        var coreXml = GetCoreFragment(driverMatchResult.Driver.Identity);

                        driverUpdates.Add(new UpdateInfo()
                        {
                            Deployment = new Deployment()
                            {
                                Action               = DeploymentAction.Install,
                                ID                   = 25000,
                                AutoDownload         = "0",
                                AutoSelect           = "0",
                                SupersedenceBehavior = "0",
                                IsAssigned           = true,
                                LastChangeTime       = "2019-08-06"
                            },
                            ID           = IdToRevisionMap[driverMatchResult.Driver.Identity.ID],
                            IsLeaf       = true,
                            Xml          = coreXml,
                            IsShared     = false,
                            Verification = null
                        });
                    }
                    else
                    {
                        unapprovedDriversMatched.Add(driverMatchResult.Driver);
                    }
                }

                // Stop matching if we have max updates already
                if (driverUpdates.Count == MaxUpdatesInResponse)
                {
                    syncResult.Truncated = true;
                    break;
                }
            }

            if (unapprovedDriversMatched.Count > 0)
            {
                OnUnApprovedDriverUpdatesRequested?.Invoke(unapprovedDriversMatched);
            }

            syncResult.NewUpdates = driverUpdates.ToArray();

            return(Task.FromResult(syncResult));
        }
 /// <summary>
 /// Extract list of other known updates from the client and maps them to a  GUID
 /// </summary>
 /// <param name="parameters">Sync parameters</param>
 /// <returns>List of update GUIDs</returns>
 private List <Guid> GetOtherCachedUpdateGuidsFromSyncParameters(SyncUpdateParameters parameters)
 {
     return(GetUpdateIdentitiesFromClientIndexes(parameters.OtherCachedUpdateIDs)
            .Select(u => u.ID)
            .ToList());
 }
 /// <summary>
 /// Extract installed non-leaf updates from the response and maps them to a GUID
 /// </summary>
 /// <param name="parameters">Sync parameters</param>
 /// <returns>List of update GUIDs</returns>
 private List <Guid> GetInstalledNotLeafGuidsFromSyncParameters(SyncUpdateParameters parameters)
 {
     return(GetUpdateIdentitiesFromClientIndexes(parameters.InstalledNonLeafUpdateIDs)
            .Select(u => u.ID)
            .ToList());
 }