Example #1
0
        private void VerifyFiles()
        {
            while (true)
            {
                IndexedRemoteFileInfo indexedFileInfo;

                if (stopped)
                {
                    break;
                }

                lock (locker)
                {
                    indexedFileInfo = filesToCheck[0];
                }

                RemoteFileInfo fileInfo = indexedFileInfo.FileInfo;

                bool checkFileHash = true;

                if (fileInfo.Compressed)
                {
                    try
                    {
                        CompressionHelper.DecompressFile(downloadDirectory + fileInfo.GetFilePathWithCompression(),
                                                         downloadDirectory + fileInfo.FilePath);
                        File.Delete(downloadDirectory + fileInfo.GetFilePathWithCompression());
                    }
                    catch (Exception ex)
                    {
                        // The SevenZip compressor doesn't define what exceptions
                        // it might throw, so we'll just catch them all

                        UpdaterLogger.Log("Decompressing file " + fileInfo.FilePath + " failed! Message: " + ex.Message);
                        VerificationFailed?.Invoke(this, new IndexEventArgs(indexedFileInfo.Index));
                        queueReady    = false;
                        checkFileHash = false;
                    }
                }

                if (checkFileHash)
                {
                    if (!HashHelper.FileHashMatches(downloadDirectory + fileInfo.FilePath, fileInfo.UncompressedHash))
                    {
                        UpdaterLogger.Log("File " + fileInfo.FilePath + " failed verification!");
                        VerificationFailed?.Invoke(this, new IndexEventArgs(indexedFileInfo.Index));
                        queueReady = false;
                    }
                    else
                    {
                        UpdaterLogger.Log("File " + fileInfo.FilePath + " passed verification.");
                    }
                }

                bool waitingForWork = false;

                lock (locker)
                {
                    filesToCheck.RemoveAt(0);

                    waitingForWork = filesToCheck.Count == 0;

                    waitHandle.Reset();

                    if (queueReady && waitingForWork)
                    {
                        Completed?.Invoke(this, EventArgs.Empty);
                        break;
                    }

                    //if (stopped)
                    //{
                    //    filesToCheck.Clear();
                    //    break;
                    //}
                }

                if (waitingForWork)
                {
                    waitHandle.WaitOne();
                }
            }

            lock (locker)
            {
                waitHandle.Dispose();
                waitHandle = null;
            }

            // We could also dispose of verifierTask, but it sounds like we don't need to bother
            // https://blogs.msdn.microsoft.com/pfxteam/2012/03/25/do-i-need-to-dispose-of-tasks/
            // In case we'd still want to do it, it'd be safest for this class to have a function
            // for disposing the task (maybe this class could implement IDisposable), and the
            // user of this class would then call it
        }
        /// <summary>
        /// Starts downloading an update.
        /// </summary>
        /// <param name="buildPath">The local build path.</param>
        /// <param name="downloadDirectory">The download directory.</param>
        /// <param name="filesToDownload">A list of files to download.</param>
        /// <param name="updateMirror">The update mirror to use.</param>
        public UpdateDownloadResult DownloadUpdates(string buildPath, string downloadDirectory,
                                                    List <RemoteFileInfo> filesToDownload, UpdateMirror updateMirror)
        {
            if (filesToDownload.Count == 0)
            {
                return(new UpdateDownloadResult(UpdateDownloadResultType.COMPLETED));
            }

            cancelled = false;

            this.filesToDownload   = filesToDownload;
            this.downloadDirectory = downloadDirectory;
            this.updateMirror      = updateMirror;

            foreach (var fileInfo in filesToDownload)
            {
                totalUpdateSize += fileInfo.GetDownloadSize();
            }

            verifierWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset);

            verifier = new Verifier(downloadDirectory);
            verifier.VerificationFailed += Verifier_VerificationFailed;
            verifier.Completed          += Verifier_Completed;

            webClient = new WebClient()
            {
                CachePolicy = new RequestCachePolicy(RequestCacheLevel.NoCacheNoStore),
                Encoding    = Encoding.GetEncoding(1252)
            };
            webClient.DownloadProgressChanged += WebClient_DownloadProgressChanged;

            fileIndexesToDownload = new List <int>();
            for (int i = 0; i < filesToDownload.Count; i++)
            {
                fileIndexesToDownload.Add(i);
            }
            fileIndexesToDownload.Reverse();
            fileIndexErrorCounts = new int[filesToDownload.Count];

            while (true)
            {
                int fileIndex;

                lock (fileListLocker)
                {
                    // If we have downloaded all files and the verifier has
                    // succesfully verified them, the list of file indexes to
                    // download has no entries -> we don't have any work left
                    if (fileIndexesToDownload.Count == 0)
                    {
                        break;
                    }

                    fileIndex = fileIndexesToDownload[fileIndexesToDownload.Count - 1];
                    currentlyDownloadedFile = filesToDownload[fileIndex];

                    if (fileIndexErrorCounts[fileIndex] > MAX_ERROR_COUNT)
                    {
                        return(CleanUpAndReturnResult(UpdateDownloadResultType.FAILED,
                                                      "Failed to download file " + currentlyDownloadedFile.FilePath));
                    }
                }

                Task downloadTask = null;

                try
                {
                    // By both checking for cancellation and starting a new download
                    // task in the same lock block that's also used in Cancel() we're
                    // making sure that a call to Cancel() will take effect right
                    // away - either we're executing the code above meaning we'll
                    // check for cancellation soon, or we're waiting for a download to
                    // finish - and Cancel() cancels the download operation in case
                    // one is going on
                    lock (cancellationLocker)
                    {
                        if (cancelled)
                        {
                            return(CleanUpAndReturnResult(UpdateDownloadResultType.CANCELLED));
                        }

                        string fileSavePath = downloadDirectory + currentlyDownloadedFile.GetFilePathWithCompression();

                        Directory.CreateDirectory(Path.GetDirectoryName(fileSavePath));

                        downloadTask = webClient.DownloadFileTaskAsync(
                            new Uri(updateMirror.URL + currentlyDownloadedFile.GetFilePathWithCompression().Replace('\\', '/')),
                            fileSavePath);
                    }

                    downloadTask.Wait();
                    downloadTask.Dispose();

                    lock (downloadedBytesLocker)
                        downloadedBytes += currentlyDownloadedFile.GetDownloadSize();

                    verifier.VerifyFile(new IndexedRemoteFileInfo(currentlyDownloadedFile, fileIndex));
                }
                catch (AggregateException ex)
                {
                    downloadTask.Dispose();

                    if (cancelled)
                    {
                        return(CleanUpAndReturnResult(UpdateDownloadResultType.CANCELLED));
                    }

                    UpdaterLogger.Log("Exception while downloading file " +
                                      currentlyDownloadedFile.FilePath);

                    LogInnerExceptionRecursive(ex);

                    lock (fileListLocker)
                    {
                        fileIndexErrorCounts[fileIndex]++;
                    }

                    continue;
                }

                bool waitingForVerifier = false;

                lock (fileListLocker)
                {
                    // Remove the downloaded file from the download queue
                    fileIndexesToDownload.Remove(fileIndex);

                    waitingForVerifier = fileIndexesToDownload.Count == 0;
                }

                if (waitingForVerifier)
                {
                    // We have downloaded all the files, wait for the verifier
                    // to finish verifying them
                    verifier.SetQueueReady();
                    verifierWaitHandle.WaitOne();
                }
            }

            return(CleanUpAndReturnResult(UpdateDownloadResultType.COMPLETED));
        }