Example #1
0
        public static ReleaseEntry ParseReleaseEntry(string entry)
        {
            Contract.Requires(entry != null);

            entry = commentRegex.Replace(entry, "");
            if (String.IsNullOrWhiteSpace(entry))
            {
                return(null);
            }

            var m = entryRegex.Match(entry);

            if (!m.Success)
            {
                throw new Exception("Invalid release entry: " + entry);
            }

            if (m.Groups.Count != 4)
            {
                throw new Exception("Invalid release entry: " + entry);
            }

            string filename = m.Groups[2].Value;

            // Split the base URL and the filename if an URI is provided,
            // throws if a path is provided
            string baseUrl = null;
            string query   = null;

            if (Utility.IsHttpUrl(filename))
            {
                var uri       = new Uri(filename);
                var path      = uri.LocalPath;
                var authority = uri.GetLeftPart(UriPartial.Authority);

                if (String.IsNullOrEmpty(path) || String.IsNullOrEmpty(authority))
                {
                    throw new Exception("Invalid URL");
                }

                var indexOfLastPathSeparator = path.LastIndexOf("/") + 1;
                baseUrl  = authority + path.Substring(0, indexOfLastPathSeparator);
                filename = path.Substring(indexOfLastPathSeparator);

                if (!String.IsNullOrEmpty(uri.Query))
                {
                    query = uri.Query;
                }
            }

            if (filename.IndexOfAny(Path.GetInvalidFileNameChars()) > -1)
            {
                throw new Exception("Filename can either be an absolute HTTP[s] URL, *or* a file name");
            }

            long size    = Int64.Parse(m.Groups[3].Value);
            bool isDelta = filenameIsDeltaFile(filename);

            return(new ReleaseEntry(m.Groups[1].Value, filename, size, isDelta, baseUrl, query));
        }
Example #2
0
            public async Task DownloadReleases(string updateUrlOrPath, IEnumerable <ReleaseEntry> releasesToDownload, Action <int> progress = null, IFileDownloader urlDownloader = null)
            {
                progress      = progress ?? (_ => { });
                urlDownloader = urlDownloader ?? new FileDownloader();

                int current     = 0;
                int toIncrement = (int)(100.0 / releasesToDownload.Count());

                if (Utility.IsHttpUrl(updateUrlOrPath))
                {
                    await releasesToDownload.ForEachAsync(async x => {
                        await urlDownloader.DownloadFile(
                            String.Format("{0}/{1}", updateUrlOrPath, x.Filename),
                            Path.Combine(rootAppDirectory, "packages", x.Filename));
                        lock (progress) progress(current += toIncrement);
                    });
                }
                else
                {
                    await releasesToDownload.ForEachAsync(x => {
                        File.Copy(
                            Path.Combine(updateUrlOrPath, x.Filename),
                            Path.Combine(rootAppDirectory, "packages", x.Filename));
                        lock (progress) progress(current += toIncrement);
                    });
                }
            }
            public async Task DownloadReleases(string updateUrlOrPath, IEnumerable <ReleaseEntry> releasesToDownload, Action <int> progress = null, IFileDownloader urlDownloader = null)
            {
                progress      = progress ?? (_ => { });
                urlDownloader = urlDownloader ?? new FileDownloader();
                var packagesDirectory = Path.Combine(rootAppDirectory, "packages");

                int current     = 0;
                int toIncrement = (int)(100.0 / releasesToDownload.Count());

                if (Utility.IsHttpUrl(updateUrlOrPath))
                {
                    // From Internet
                    await releasesToDownload.ForEachAsync(async x => {
                        var targetFile = Path.Combine(packagesDirectory, x.Filename);
                        await downloadRelease(updateUrlOrPath, x, urlDownloader, targetFile);

                        lock (progress) progress(current += toIncrement);
                    });
                }
                else
                {
                    // From Disk
                    await releasesToDownload.ForEachAsync(x => {
                        var targetFile = Path.Combine(packagesDirectory, x.Filename);

                        File.Copy(
                            Path.Combine(updateUrlOrPath, x.Filename),
                            targetFile,
                            true);

                        lock (progress) progress(current += toIncrement);
                    });
                }
            }
Example #4
0
        public static ReleasePackage GetPreviousRelease(IEnumerable <ReleaseEntry> releaseEntries, IReleasePackage package, string targetDir, string prevReleasePath = null, IFullLogger log = null)
        {
            {
                if (releaseEntries == null || !releaseEntries.Any())
                {
                    return(null);
                }
                var first = releaseEntries
                            .Where(x => x.IsDelta == false).Where(x => x.IsDelta == false)
                            .Where(x => x.Version < package.ToSemanticVersion()).Where(x => x.Version < package.ToSemanticVersion())
                            .OrderByDescending(x => x.Version).OrderByDescending(x => x.Version)
                            .Select(x => new ReleasePackage(Path.Combine(targetDir, x.Filename), true))
                            .FirstOrDefault();

                if (first != null)
                {
                    var prevReleaseFilePath = Path.Combine(targetDir, first.SuggestedReleaseFileName);
                    if (!File.Exists(prevReleaseFilePath) && !string.IsNullOrEmpty(prevReleasePath))
                    {
                        IFileDownloader downloader;
                        if (Utility.IsHttpUrl(prevReleasePath))
                        {
                            downloader = new FileDownloader();
                        }
                        else if (Utility.IsFtpUrl(prevReleasePath))
                        {
                            downloader = new FtpFileDownloader();
                        }
                        else
                        {
                            return(null);
                        }

                        try
                        {
                            downloader.DownloadFile(Path.Combine(prevReleasePath, first.SuggestedReleaseFileName), prevReleaseFilePath, null);
                        }
                        catch (Exception)
                        {
                            return(null);
                        }
                    }

                    if (File.Exists(prevReleaseFilePath))
                    {
                        return(new ReleasePackage(prevReleaseFilePath, true));
                    }
                }

                return(null);
            }
        }
            public async Task DownloadReleases(string updateUrlOrPath, IEnumerable <ReleaseEntry> releasesToDownload, Action <int> progress = null, IFileDownloader urlDownloader = null)
            {
                progress = progress ?? (_ => { });
                if (urlDownloader == null && Utility.IsHttpUrl(updateUrlOrPath))
                {
                    urlDownloader = new FileDownloader();
                }
                else if (urlDownloader == null && Utility.IsFtpUrl(updateUrlOrPath))
                {
                    urlDownloader = new FtpFileDownloader();
                }
                var packagesDirectory = Path.Combine(rootAppDirectory, "packages");

                double current     = 0;
                double toIncrement = 100.0 / releasesToDownload.Count();

                if (Utility.IsHttpUrl(updateUrlOrPath) || Utility.IsFtpUrl(updateUrlOrPath))
                {
                    // From Internet
                    await releasesToDownload.ForEachAsync(async x => {
                        var targetFile   = Path.Combine(packagesDirectory, x.Filename);
                        double component = 0;
                        await downloadRelease(updateUrlOrPath, x, urlDownloader, targetFile, p => {
                            lock (progress) {
                                current  -= component;
                                component = toIncrement / 100.0 * p;
                                progress((int)Math.Round(current += component));
                            }
                        });

                        checksumPackage(x);
                    });
                }
                else
                {
                    // From Disk
                    await releasesToDownload.ForEachAsync(x => {
                        var targetFile = Path.Combine(packagesDirectory, x.Filename);

                        File.Copy(
                            Path.Combine(updateUrlOrPath, x.Filename),
                            targetFile,
                            true);

                        lock (progress) progress((int)Math.Round(current += toIncrement));
                        checksumPackage(x);
                    });
                }
            }
Example #6
0
        public static ReleaseEntry ParseReleaseEntry(string entry)
        {
            entry = commentRegex.Replace(entry, "");
            if (string.IsNullOrWhiteSpace(entry))
            {
                return(null);
            }
            Match match1 = entryRegex.Match(entry);

            if (!match1.Success)
            {
                throw new Exception("Invalid release entry: " + entry);
            }
            Match local1 = match1;

            if (local1.Groups.Count != 4)
            {
                throw new Exception("Invalid release entry: " + entry);
            }
            Match  local2    = local1;
            string urlOrPath = local2.Groups[2].Value;
            string baseUrl   = null;
            string query     = null;

            if (Utility.IsHttpUrl(urlOrPath))
            {
                Uri    uri       = new Uri(urlOrPath);
                string localPath = uri.LocalPath;
                string leftPart  = uri.GetLeftPart(UriPartial.Authority);
                if (string.IsNullOrEmpty(localPath) || string.IsNullOrEmpty(leftPart))
                {
                    throw new Exception("Invalid URL");
                }
                int length = localPath.LastIndexOf("/") + 1;
                baseUrl   = leftPart + localPath.Substring(0, length);
                urlOrPath = localPath.Substring(length);
                if (!string.IsNullOrEmpty(uri.Query))
                {
                    query = uri.Query;
                }
            }
            if (urlOrPath.IndexOfAny(Path.GetInvalidFileNameChars()) > -1)
            {
                throw new Exception("Filename can either be an absolute HTTP[s] URL, *or* a file name");
            }
            Match local3 = local2;

            return(new ReleaseEntry(local3.Groups[1].Value, urlOrPath, long.Parse(local3.Groups[3].Value), filenameIsDeltaFile(urlOrPath), baseUrl, query));
        }
Example #7
0
            public async Task <UpdateInfo> CheckForUpdate(
                string localReleaseFile,
                string updateUrlOrPath,
                bool ignoreDeltaUpdates       = false,
                Action <int> progress         = null,
                IFileDownloader urlDownloader = null)
            {
                progress = progress ?? (_ => { });

                var localReleases = Enumerable.Empty <ReleaseEntry>();

                bool shouldInitialize = false;

                try {
                    localReleases = Utility.LoadLocalReleases(localReleaseFile);
                } catch (Exception ex) {
                    // Something has gone pear-shaped, let's start from scratch
                    this.Log().WarnException("Failed to load local releases, starting from scratch", ex);
                    shouldInitialize = true;
                }

                if (shouldInitialize)
                {
                    await initializeClientAppDirectory();
                }

                string releaseFile;

                var latestLocalRelease = localReleases.Count() > 0 ?
                                         localReleases.MaxBy(x => x.Version).First() :
                                         default(ReleaseEntry);

                // Fetch the remote RELEASES file, whether it's a local dir or an
                // HTTP URL
                if (Utility.IsHttpUrl(updateUrlOrPath))
                {
                    if (updateUrlOrPath.EndsWith("/"))
                    {
                        updateUrlOrPath = updateUrlOrPath.Substring(0, updateUrlOrPath.Length - 1);
                    }

                    this.Log().Info("Downloading RELEASES file from {0}", updateUrlOrPath);

                    int retries = 3;

retry:

                    try {
                        var url = String.Format("{0}/{1}", updateUrlOrPath, "RELEASES");
                        if (latestLocalRelease != null)
                        {
                            url = String.Format("{0}/RELEASES?id={1}&localVersion={2}&arch={3}",
                                                updateUrlOrPath,
                                                Uri.EscapeUriString(latestLocalRelease.PackageName),
                                                Uri.EscapeUriString(latestLocalRelease.Version.ToString()),
                                                Environment.Is64BitOperatingSystem ? "amd64" : "x86");
                        }

                        var data = await urlDownloader.DownloadUrl(url);

                        releaseFile = Encoding.UTF8.GetString(data);
                    } catch (WebException ex) {
                        this.Log().InfoException("Download resulted in WebException (returning blank release list)", ex);

                        if (retries <= 0)
                        {
                            throw;
                        }
                        retries--;
                        goto retry;
                    }

                    progress(33);
                }
                else
                {
                    this.Log().Info("Reading RELEASES file from {0}", updateUrlOrPath);

                    if (!Directory.Exists(updateUrlOrPath))
                    {
                        var message = String.Format(
                            "The directory {0} does not exist, something is probably broken with your application",
                            updateUrlOrPath);

                        throw new Exception(message);
                    }

                    var fi = new FileInfo(Path.Combine(updateUrlOrPath, "RELEASES"));
                    if (!fi.Exists)
                    {
                        var message = String.Format(
                            "The file {0} does not exist, something is probably broken with your application",
                            fi.FullName);

                        this.Log().Warn(message);

                        var packages = (new DirectoryInfo(updateUrlOrPath)).GetFiles("*.nupkg");
                        if (packages.Length == 0)
                        {
                            throw new Exception(message);
                        }

                        // NB: Create a new RELEASES file since we've got a directory of packages
                        ReleaseEntry.WriteReleaseFile(
                            packages.Select(x => ReleaseEntry.GenerateFromFile(x.FullName)), fi.FullName);
                    }

                    releaseFile = File.ReadAllText(fi.FullName, Encoding.UTF8);
                    progress(33);
                }

                var ret            = default(UpdateInfo);
                var remoteReleases = ReleaseEntry.ParseReleaseFile(releaseFile);

                progress(66);

                if (!remoteReleases.Any())
                {
                    throw new Exception("Remote release File is empty or corrupted");
                }

                ret = determineUpdateInfo(localReleases, remoteReleases, ignoreDeltaUpdates);

                progress(100);
                return(ret);
            }
Example #8
0
            public async Task <UpdateInfo> CheckForUpdate(
                string localReleaseFile,
                string updateUrlOrPath,
                bool ignoreDeltaUpdates       = false,
                Action <int> progress         = null,
                IFileDownloader urlDownloader = null,
                bool startOverIfNone          = false)
            {
                progress = progress ?? (_ => { });

                var localReleases = Enumerable.Empty <ReleaseEntry>();

                bool shouldInitialize = false;

                try {
                    localReleases = Utility.LoadLocalReleases(localReleaseFile);
                } catch (Exception ex) {
                    // Something has gone pear-shaped, let's start from scratch
                    this.Log().WarnException("Failed to load local releases, starting from scratch", ex);
                    shouldInitialize = true;
                }

restart:

                if (shouldInitialize)
                {
                    await initializeClientAppDirectory();
                }

                string releaseFile;

                var latestLocalRelease = localReleases.Count() > 0 ?
                                         localReleases.MaxBy(x => x.Version).First() :
                                         default(ReleaseEntry);

                // Fetch the remote RELEASES file, whether it's a local dir or an
                // HTTP URL
                if (Utility.IsHttpUrl(updateUrlOrPath))
                {
                    if (updateUrlOrPath.EndsWith("/"))
                    {
                        updateUrlOrPath = updateUrlOrPath.Substring(0, updateUrlOrPath.Length - 1);
                    }

                    this.Log().Info("Downloading RELEASES file from {0}", updateUrlOrPath);

                    int retries = 3;

retry:

                    try {
                        var uri = Utility.AppendPathToUri(new Uri(updateUrlOrPath), "RELEASES");

                        if (latestLocalRelease != null)
                        {
                            uri = Utility.AddQueryParamsToUri(uri, new Dictionary <string, string> {
                                { "id", latestLocalRelease.PackageName },
                                { "localVersion", latestLocalRelease.Version.ToString() },
                                { "arch", Environment.Is64BitOperatingSystem ? "amd64" : "x86" }
                            });
                        }

                        var data = await urlDownloader.DownloadUrl(uri.ToString());

                        releaseFile = Encoding.UTF8.GetString(data);
                    } catch (WebException ex) {
                        this.Log().InfoException("Download resulted in WebException (returning blank release list)", ex);

                        if (retries <= 0)
                        {
                            throw;
                        }
                        retries--;
                        goto retry;
                    }

                    progress(33);
                }
                else
                {
                    this.Log().Info("Reading RELEASES file from {0}", updateUrlOrPath);

                    if (!Directory.Exists(updateUrlOrPath))
                    {
                        var message = String.Format(
                            "The directory {0} does not exist, something is probably broken with your application",
                            updateUrlOrPath);

                        throw new Exception(message);
                    }

                    var fi = new FileInfo(Path.Combine(updateUrlOrPath, "RELEASES"));
                    if (!fi.Exists)
                    {
                        var message = String.Format(
                            "The file {0} does not exist, something is probably broken with your application",
                            fi.FullName);

                        this.Log().Warn(message);

                        var packages = (new DirectoryInfo(updateUrlOrPath)).GetFiles("*.nupkg");
                        if (packages.Length == 0)
                        {
                            throw new Exception(message);
                        }

                        // NB: Create a new RELEASES file since we've got a directory of packages
                        ReleaseEntry.WriteReleaseFile(
                            packages.Select(x => ReleaseEntry.GenerateFromFile(x.FullName)), fi.FullName);
                    }

                    releaseFile = File.ReadAllText(fi.FullName, Encoding.UTF8);
                    progress(33);
                }

                var ret            = default(UpdateInfo);
                var remoteReleases = ReleaseEntry.ParseReleaseFile(releaseFile);

                progress(66);

                if (!remoteReleases.Any())
                {
                    throw new Exception("Remote release File is empty or corrupted");
                }

                ret = determineUpdateInfo(localReleases, remoteReleases, ignoreDeltaUpdates);
                progress(100);
                if (startOverIfNone && !ret.ReleasesToApply.Any())
                {
                    // User has apparently re-run the installer for the version already installed.
                    // Assume the intent is to repair a broken installation.
                    // These rather awkward steps cause it to erase the installed-version directory
                    // and re-create it, much like a first-time install (though it won't run the app
                    // with the special arguments for first-time).
                    shouldInitialize = true;
                    localReleases    = Enumerable.Empty <ReleaseEntry>();
                    goto restart;
                }
                return(ret);
            }
Example #9
0
            public async Task DownloadReleases(string updateUrlOrPath, IEnumerable <ReleaseEntry> releasesToDownload1, Action <int> progress = null, IFileDownloader urlDownloader = null)
            {
                progress      = progress ?? (_ => { });
                urlDownloader = urlDownloader ?? new FileDownloader();
                var packagesDirectory  = Path.Combine(rootAppDirectory, "packages");
                var releasesToDownload = new List <ReleaseEntry>(releasesToDownload1);

                double current     = 0;
                double toIncrement = 100.0 / releasesToDownload.Count();

                if (Utility.IsHttpUrl(updateUrlOrPath))
                {
                    // From Internet
                    Exception lastException = null;
                    for (int attempts = 0; ; attempts++)
                    {
                        // Filter out ones we downloaded successfully (perhaps on an earlier run of the program).
                        // We don't want to waste time and bandwidth downloading anything we already have.
                        releasesToDownload = releasesToDownload.Where(x => !isPackageOk(x)).ToList();
                        if (releasesToDownload.Count == 0 || attempts >= 4) // we got them all (or exceeded max attempt limit)
                        {
                            break;
                        }
                        try
                        {
                            await releasesToDownload.ForEachAsync(async x => {
                                var targetFile   = Path.Combine(packagesDirectory, x.Filename);
                                double component = 0;
                                await downloadRelease(updateUrlOrPath, x, urlDownloader, targetFile, p => {
                                    lock (progress) {
                                        current  -= component;
                                        component = toIncrement / 100.0 * p;
                                        progress((int)Math.Round(current += component));
                                    }
                                });
                                // With a lot of small updates, and since notifications are only sent every half second
                                // for each one, we can easily miss half or more of the progress on each download.
                                // To make sure we eventually get to 100% of the whole process, we need to update
                                // progress (and especially the total in current) to indicate that this one is complete.
                                lock (progress)
                                {
                                    current  -= component;
                                    component = toIncrement;
                                    progress((int)Math.Round(current += component));
                                }
                            });
                        }
                        catch (WebException ex) {
                            lastException = ex;
                        }
                    }
                    // If we failed to get all the files, throw the last exception we got; it may provide some clue what went wrong.
                    if (releasesToDownload.Count > 0)
                    {
                        throw lastException ?? new ApplicationException("Download somehow failed to get a full set of valid deltas though no exception was thrown");
                    }
                }
                else
                {
                    // From Disk
                    await releasesToDownload.ForEachAsync(x => {
                        var targetFile = Path.Combine(packagesDirectory, x.Filename);

                        File.Copy(
                            Path.Combine(updateUrlOrPath, x.Filename),
                            targetFile,
                            true);

                        lock (progress) progress((int)Math.Round(current += toIncrement));
                    });
                }
            }
Example #10
0
            public async Task <UpdateInfo> CheckForUpdate(
                string localReleaseFile,
                string updateUrlOrPath,
                bool ignoreDeltaUpdates       = false,
                Action <int> progress         = null,
                IFileDownloader urlDownloader = null)
            {
                progress = progress ?? (_ => { });

                var localReleases = Enumerable.Empty <ReleaseEntry>();

                bool shouldInitialize = false;

                try {
                    localReleases = LoadLocalReleases(localReleaseFile);
                } catch (Exception ex) {
                    // Something has gone pear-shaped, let's start from scratch
                    this.Log().WarnException("Failed to load local releases, starting from scratch", ex);
                    shouldInitialize = true;
                }

                if (shouldInitialize)
                {
                    await initializeClientAppDirectory();
                }

                string releaseFile;

                // Fetch the remote RELEASES file, whether it's a local dir or an
                // HTTP URL
                if (Utility.IsHttpUrl(updateUrlOrPath))
                {
                    this.Log().Info("Downloading RELEASES file from {0}", updateUrlOrPath);

                    try {
                        var data = await urlDownloader.DownloadUrl(String.Format("{0}/{1}", updateUrlOrPath, "RELEASES"));

                        releaseFile = Encoding.UTF8.GetString(data);
                    } catch (WebException ex) {
                        this.Log().InfoException("Download resulted in WebException (returning blank release list)", ex);
                        releaseFile = String.Empty;
                    }

                    progress(33);
                }
                else
                {
                    this.Log().Info("Reading RELEASES file from {0}", updateUrlOrPath);

                    if (!Directory.Exists(updateUrlOrPath))
                    {
                        var message = String.Format(
                            "The directory {0} does not exist, something is probably broken with your application",
                            updateUrlOrPath);

                        throw new Exception(message);
                    }

                    var fi = new FileInfo(Path.Combine(updateUrlOrPath, "RELEASES"));
                    if (!fi.Exists)
                    {
                        var message = String.Format(
                            "The file {0} does not exist, something is probably broken with your application",
                            fi.FullName);

                        this.Log().Warn(message);

                        var packages = (new DirectoryInfo(updateUrlOrPath)).GetFiles("*.nupkg");
                        if (packages.Length == 0)
                        {
                            throw new Exception(message);
                        }

                        // NB: Create a new RELEASES file since we've got a directory of packages
                        ReleaseEntry.WriteReleaseFile(
                            packages.Select(x => ReleaseEntry.GenerateFromFile(x.FullName)), fi.FullName);
                    }

                    releaseFile = File.ReadAllText(fi.FullName, Encoding.UTF8);
                    progress(33);
                }

                var ret            = default(UpdateInfo);
                var remoteReleases = ReleaseEntry.ParseReleaseFile(releaseFile);

                progress(66);

                if (remoteReleases.Any())
                {
                    ret = determineUpdateInfo(localReleases, remoteReleases, ignoreDeltaUpdates);
                }

                progress(100);
                return(ret);
            }