private static Deployment GetExistingAuditDeployment (Deployment deployment)
 {
     lock (LockObject)
     {
         return HashesInProgress.FirstOrDefault(h => h.ApplicationName == deployment.ApplicationName && h.ServerName == deployment.ServerName);
     }
 }
 private static void RemoveFromHashingInProgress(Deployment deployment)
 {
     lock (LockObject)
     {
         HashesInProgress.Remove(deployment);
         AbortHashing.Remove(deployment.DeploymentId);
     }
 }
 public async Task InsertDeploymentAsync(Deployment deployment)
 {
     var matchingDeployment = await GetActiveDeployment(deployment.ApplicationName, deployment.ServerName);
     if (matchingDeployment != null)
     {
         await ChangeDeploymentEndDateTime(matchingDeployment.DeploymentId, deployment.StartDateTime);
     }
     await collection.InsertOneAsync(deployment);
 }
 private static void AbortHashIfNecessary(Deployment deployment)
 {
     lock (LockObject)
     {
         if (AbortHashing.ContainsKey(deployment.DeploymentId) && AbortHashing[deployment.DeploymentId])
         {
             throw new ApplicationException("The audit running for application " + deployment.ApplicationName + " and server " + deployment.ServerName + " was aborted for a new audit request.");
         }
     }
 }
 public async Task<DeploymentAudit> HashDeployment(Deployment deployment, IList<Regex> fileExclusionExpressions, bool hashHiddenFiles, bool overrideExistingAudit)
 {
     try
     {
         AddToHashesInProgress(deployment, overrideExistingAudit);
         var audit = await HashDeployment(deployment, fileExclusionExpressions, hashHiddenFiles);
         return audit;
     }
     finally
     {
         RemoveFromHashingInProgress(deployment);
     }
 }
 private static void AddToHashesInProgress(Deployment deployment, bool overrideExistingAudit)
 {
     var existingAuditDeployment = GetExistingAuditDeployment(deployment);
     if (existingAuditDeployment != null)
     {
         if (overrideExistingAudit)
         {
             AbortExistingAudit(existingAuditDeployment);
         }
         else
         {
             throw new InvalidOperationException("There is already an audit running for application " + deployment.ApplicationName + " and server " + deployment.ServerName + ".");
         }
     }
     lock (LockObject)
     {
         HashesInProgress.Add(deployment);
         AbortHashing.Add(deployment.DeploymentId, false);
     }
 }
 private static void AbortExistingAudit(Deployment existingAuditDeployment)
 {
     lock (LockObject)
     {
         if (AbortHashing.ContainsKey(existingAuditDeployment.DeploymentId)) AbortHashing[existingAuditDeployment.DeploymentId] = true;
     }
     bool abortedFlag = false;
     for (int i = 0; i < 50; i++)
     {
         Thread.Sleep(500);
         lock (LockObject)
         {
             if (!AbortHashing.ContainsKey(existingAuditDeployment.DeploymentId))
             {
                 abortedFlag = true;
                 break;
             }
         }
     }
     if (!abortedFlag)
     {
         throw new InvalidOperationException("There is already an audit running for application " + existingAuditDeployment.ApplicationName + " and server " + existingAuditDeployment.ServerName + " and it could not be abandoned.");
     }
 }
        private async Task HashFile(Deployment deployment, string path, bool hashHiddenFiles, IList<FileHash> hashResults )
        {
            AbortHashIfNecessary(deployment);
            var fileInfo = new FileInfo(path);
            var fileIsHidden = (fileInfo.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden;
            if (fileIsHidden && !hashHiddenFiles)
            {
                return;
            }

            var hasher = CreateHashAlgorithm();
            var buffer = new byte[1024]; //what is optimal here?
            using (var fileStream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
            {
                while (true)
                {
                    var bytesread = await fileStream.ReadAsync(buffer, 0, 1024);
                    if (bytesread == 0) break;
                    hasher.TransformBlock(buffer, 0, bytesread, null, 0);
                }
            }
            HashString(hasher, path);
            HashDateTime(hasher, fileInfo.LastWriteTimeUtc);
            HashIsHidden(hasher, fileIsHidden);

            hasher.TransformFinalBlock(new byte[0], 0, 0);
            hashResults.Add(new FileHash {Path = path, IsHidden = fileIsHidden, LastWriteTime = fileInfo.LastWriteTime, Hash = BytesToString(hasher.Hash)});
        }
        private async Task HashSubDirectoryRecursive(Deployment deployment, string directory, IList<Regex> fileExclusionExpressions, bool hashHiddenFiles, IList<FileHash> hashResults)
        {
            var directoryInfo = new DirectoryInfo(directory);
            if ((directoryInfo.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden && !hashHiddenFiles)
            {
                return;
            }

            foreach (var file in Directory.EnumerateFiles(directory, "*", SearchOption.AllDirectories))
            {
                if (!fileExclusionExpressions.Any(f => f.IsMatch(file)))
                {
                    await HashFile(deployment, file, hashHiddenFiles, hashResults);
                }
            }
        }
 private async Task HashDirectory(Deployment deployment, IList<Regex> fileExclusionExpressions, bool hashHiddenFiles, IList<FileHash> hashResults)
 {
     foreach (var file in Directory.GetFiles(deployment.NetworkPath, "*"))
     {
         if (!fileExclusionExpressions.Any(f => f.IsMatch(file)))
         {
             await HashFile(deployment, file, hashHiddenFiles, hashResults);
         }
     }
     foreach (var directory in Directory.GetDirectories(deployment.NetworkPath).Where(d=>!d.EndsWith("RECYCLE.BIN") && !d.EndsWith("System Volume Information")))
     {
         await HashSubDirectoryRecursive(deployment, directory, fileExclusionExpressions, hashHiddenFiles, hashResults);
     }
 }
        private async Task<DeploymentAudit> HashDeployment(Deployment deployment, IList<Regex> fileExclusionExpressions, bool hashHiddenFiles)
        {
            DeploymentAudit audit;
            try
            {
                var hashResults = new List<FileHash>();
                IList<FileHashMismatch> hashDifferences = new List<FileHashMismatch>();
                var sw = Stopwatch.StartNew();
                await HashDirectory(deployment, fileExclusionExpressions, hashHiddenFiles, hashResults);
                var hash = HashTheHashResults(hashResults);
                
                if (deployment.Hash == Deployment.EmptyHash)
                {
                    deployment.Hash = hash;
                    deployment.FileHashes = hashResults;
                }
                bool validHash = deployment.Hash == hash;
                if (!validHash)
                {
                    hashDifferences = DetermineHashDifferences(deployment.FileHashes, hashResults);
                }
                sw.Stop();
                audit = new DeploymentAudit
                {
                    DeploymentId = deployment.DeploymentId,
                    Hash = hash,
                    ValidHash = validHash,
                    FileHashMismatches = hashDifferences
                };
                if (deployment.MostRecentAudit == Guid.Empty)
                {
                    deployment.MostRecentAudit = audit.DeploymentAuditId;
                }

                if (log.IsDebugEnabled)
                {
                    log.Debug($"Completed audit for application {deployment.ApplicationName} on server {deployment.ServerName} with hash {hash} in {sw.Elapsed.TotalSeconds} seconds. \r\n Results: {audit.ValidHash} \r\n List of files included in hash: \r\n {string.Join("\r\n", hashResults)}");
                }
                else
                {
                    log.Info($"Completed audit for application {deployment.ApplicationName} on server {deployment.ServerName} with hash {hash} in {sw.Elapsed.TotalSeconds} seconds. \r\n Results: {audit.ValidHash}");
                }
            }
            catch (Exception ex)
            {
                log.Error("Error while running audit for application {deployment.ApplicationName} on server {deployment.ServerName}.", ex);
                audit = new DeploymentAudit
                {
                    DeploymentId = deployment.DeploymentId,
                    Error = ex.Message
                };
            }
            return audit;
        }
 public async Task<long> ReplaceDeployment(Deployment deployment)
 {
     var updateResult = await collection.ReplaceOneAsync(d => d.DeploymentId == deployment.DeploymentId, deployment);
     return updateResult.ModifiedCount;
 }
        public async Task<IHttpActionResult> Post(string name, string serverName, [FromBody] NewDeployment payload)
        {
            try
            {
                if (string.IsNullOrWhiteSpace(payload?.NetworkPath))
                {
                    return BadRequest("You must include the field `NetworkPath` in the body.");
                }
                var application = await applicationRepository.GetApplicationAsync(name);
                if (application == null)
                {
                    return BadRequest("The application " + name + " does not exist.");
                }

                if (!Directory.Exists(payload.NetworkPath))
                {
                    return BadRequest("The path `" + payload.NetworkPath + "` is invalid or inaccessible.");
                }

                var deployment = new Deployment
                {
                    ApplicationName = name,
                    ServerName = serverName,
                    NetworkPath = payload.NetworkPath
                };

                var deploymentAudit = await applicationHashingService.HashDeployment(deployment, application.GetRegularExpressions(), application.HashHiddenFiles, true);

                await deploymentRepository.InsertDeploymentAsync(deployment);
                await auditRepository.CreateAuditAsync(deploymentAudit);
                deployment.FileHashes = null;
                return Ok(deployment);
            }
            catch (Exception ex)
            {
                Log.Error("Error in deployment controller:", ex);
                return InternalServerError();
            }
        }