aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core.WindowsInstaller/Bind/CabinetResolver.cs
blob: 79b1c6192bfd0b300dd65b9ebf7e620ed8bc4ed5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.

namespace WixToolset.Core.WindowsInstaller.Bind
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using WixToolset.Core.Bind;
    using WixToolset.Core.Native;
    using WixToolset.Data;
    using WixToolset.Extensibility;
    using WixToolset.Extensibility.Data;
    using WixToolset.Extensibility.Services;

    public class CabinetResolver
    {
        public CabinetResolver(IServiceProvider serviceProvider, string cabCachePath, IEnumerable<IWindowsInstallerBackendBinderExtension> backendExtensions)
        {
            this.ServiceProvider = serviceProvider;

            this.CabCachePath = cabCachePath;

            this.BackendExtensions = backendExtensions;
        }

        private IServiceProvider ServiceProvider { get; }

        private string CabCachePath { get; }

        private IEnumerable<IWindowsInstallerBackendBinderExtension> BackendExtensions { get; }

        public IResolvedCabinet ResolveCabinet(string cabinetPath, IEnumerable<FileFacade> fileFacades)
        {
            var filesWithPath = fileFacades.Select(this.CreateBindFileWithPath).ToList();

            IResolvedCabinet resolved = null;

            foreach (var extension in this.BackendExtensions)
            {
                resolved = extension.ResolveCabinet(cabinetPath, filesWithPath);

                if (null != resolved)
                {
                    return resolved;
                }
            }

            // By default cabinet should be built and moved to the suggested location.
            resolved = this.ServiceProvider.GetService<IResolvedCabinet>();
            resolved.BuildOption = CabinetBuildOption.BuildAndMove;
            resolved.Path = cabinetPath;

            // If a cabinet cache path was provided, change the location for the cabinet
            // to be built to and check if there is a cabinet that can be reused.
            if (!String.IsNullOrEmpty(this.CabCachePath))
            {
                var cabinetName = Path.GetFileName(cabinetPath);
                resolved.Path = Path.Combine(this.CabCachePath, cabinetName);

                if (CheckFileExists(resolved.Path))
                {
                    // Assume that none of the following are true:
                    // 1. any files are added or removed
                    // 2. order of files changed or names changed
                    // 3. modified time changed
                    var cabinetValid = true;

                    var cabinet = new Cabinet(resolved.Path);
                    var fileList = cabinet.Enumerate();

                    if (filesWithPath.Count() != fileList.Count)
                    {
                        cabinetValid = false;
                    }
                    else
                    {
                        var i = 0;
                        foreach (var file in filesWithPath)
                        {
                            // First check that the file identifiers match because that is quick and easy.
                            var cabFileInfo = fileList[i];
                            cabinetValid = (cabFileInfo.FileId == file.Id);
                            if (cabinetValid)
                            {
                                // Still valid so ensure the file sizes are the same.
                                var fileInfo = new FileInfo(file.Path);
                                cabinetValid = (cabFileInfo.Size == fileInfo.Length);
                                if (cabinetValid)
                                {
                                    // Still valid so ensure the source time stamp hasn't changed.
                                    cabinetValid = cabFileInfo.SameAsDateTime(fileInfo.LastWriteTime);
                                }
                            }

                            if (!cabinetValid)
                            {
                                break;
                            }

                            i++;
                        }
                    }

                    resolved.BuildOption = cabinetValid ? CabinetBuildOption.Copy : CabinetBuildOption.BuildAndCopy;
                }
            }

            return resolved;
        }

        private IBindFileWithPath CreateBindFileWithPath(FileFacade facade)
        {
            var result = this.ServiceProvider.GetService<IBindFileWithPath>();
            result.Id = facade.Id;
            result.Path = facade.SourcePath;

            return result;
        }

        private static bool CheckFileExists(string path)
        {
            try
            {
                return File.Exists(path);
            }
            catch (ArgumentException)
            {
                throw new WixException(ErrorMessages.IllegalCharactersInPath(path));
            }
        }
    }
}