diff options
Diffstat (limited to '')
-rw-r--r-- | src/WixToolset.Core.WindowsInstaller/Bind/PatchTransform.cs | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/PatchTransform.cs b/src/WixToolset.Core.WindowsInstaller/Bind/PatchTransform.cs new file mode 100644 index 00000000..5ada29de --- /dev/null +++ b/src/WixToolset.Core.WindowsInstaller/Bind/PatchTransform.cs | |||
@@ -0,0 +1,246 @@ | |||
1 | // 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. | ||
2 | |||
3 | namespace WixToolset.Core.WindowsInstaller.Bind | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections; | ||
7 | using System.Globalization; | ||
8 | using System.Text; | ||
9 | using System.Text.RegularExpressions; | ||
10 | using WixToolset.Data; | ||
11 | using WixToolset.Data.WindowsInstaller; | ||
12 | using WixToolset.Extensibility; | ||
13 | |||
14 | internal class PatchTransform | ||
15 | { | ||
16 | public PatchTransform(string baseline, WindowsInstallerData transform) | ||
17 | { | ||
18 | this.Baseline = baseline; | ||
19 | this.Transform = transform; | ||
20 | } | ||
21 | |||
22 | public string Baseline { get; } | ||
23 | |||
24 | public WindowsInstallerData Transform { get; } | ||
25 | |||
26 | /// <summary> | ||
27 | /// Validates that the differences in the transform are valid for patch transforms. | ||
28 | /// </summary> | ||
29 | public void Validate() | ||
30 | { | ||
31 | #if TODO_PATCHING | ||
32 | // Changing the ProdocutCode in a patch transform is not recommended. | ||
33 | Table propertyTable = this.Transform.Tables["Property"]; | ||
34 | if (null != propertyTable) | ||
35 | { | ||
36 | foreach (Row row in propertyTable.Rows) | ||
37 | { | ||
38 | // Only interested in modified rows; fast check. | ||
39 | if (RowOperation.Modify == row.Operation) | ||
40 | { | ||
41 | if (0 == String.CompareOrdinal("ProductCode", (string)row[0])) | ||
42 | { | ||
43 | this.OnMessage(WixWarnings.MajorUpgradePatchNotRecommended()); | ||
44 | } | ||
45 | } | ||
46 | } | ||
47 | } | ||
48 | |||
49 | // If there is nothing in the component table we can return early because the remaining checks are component based. | ||
50 | Table componentTable = this.Transform.Tables["Component"]; | ||
51 | if (null == componentTable) | ||
52 | { | ||
53 | return; | ||
54 | } | ||
55 | |||
56 | // Index Feature table row operations | ||
57 | Table featureTable = this.Transform.Tables["Feature"]; | ||
58 | Table featureComponentsTable = this.Transform.Tables["FeatureComponents"]; | ||
59 | Hashtable featureOps = null; | ||
60 | if (null != featureTable) | ||
61 | { | ||
62 | int capacity = featureTable.Rows.Count; | ||
63 | featureOps = new Hashtable(capacity); | ||
64 | |||
65 | foreach (Row row in featureTable.Rows) | ||
66 | { | ||
67 | featureOps[(string)row[0]] = row.Operation; | ||
68 | } | ||
69 | } | ||
70 | else | ||
71 | { | ||
72 | featureOps = new Hashtable(); | ||
73 | } | ||
74 | |||
75 | // Index Component table and check for keypath modifications | ||
76 | Hashtable deletedComponent = new Hashtable(); | ||
77 | Hashtable componentKeyPath = new Hashtable(); | ||
78 | foreach (Row row in componentTable.Rows) | ||
79 | { | ||
80 | string id = row.Fields[0].Data.ToString(); | ||
81 | string keypath = (null == row.Fields[5].Data) ? String.Empty : row.Fields[5].Data.ToString(); | ||
82 | |||
83 | componentKeyPath.Add(id, keypath); | ||
84 | if (RowOperation.Delete == row.Operation) | ||
85 | { | ||
86 | deletedComponent.Add(id, row); | ||
87 | } | ||
88 | else if (RowOperation.Modify == row.Operation) | ||
89 | { | ||
90 | if (row.Fields[1].Modified) | ||
91 | { | ||
92 | // Changing the guid of a component is equal to deleting the old one and adding a new one. | ||
93 | deletedComponent.Add(id, row); | ||
94 | } | ||
95 | |||
96 | // If the keypath is modified its an error | ||
97 | if (row.Fields[5].Modified) | ||
98 | { | ||
99 | this.OnMessage(WixErrors.InvalidKeypathChange(row.SourceLineNumbers, id, this.transformPath)); | ||
100 | } | ||
101 | } | ||
102 | } | ||
103 | |||
104 | // Verify changes in the file table | ||
105 | Table fileTable = this.Transform.Tables["File"]; | ||
106 | if (null != fileTable) | ||
107 | { | ||
108 | Hashtable componentWithChangedKeyPath = new Hashtable(); | ||
109 | foreach (Row row in fileTable.Rows) | ||
110 | { | ||
111 | if (RowOperation.None != row.Operation) | ||
112 | { | ||
113 | string fileId = row.Fields[0].Data.ToString(); | ||
114 | string componentId = row.Fields[1].Data.ToString(); | ||
115 | |||
116 | // If this file is the keypath of a component | ||
117 | if (String.Equals((string)componentKeyPath[componentId], fileId, StringComparison.Ordinal)) | ||
118 | { | ||
119 | if (row.Fields[2].Modified) | ||
120 | { | ||
121 | // You cant change the filename of a file that is the keypath of a component. | ||
122 | this.OnMessage(WixErrors.InvalidKeypathChange(row.SourceLineNumbers, componentId, this.transformPath)); | ||
123 | } | ||
124 | |||
125 | if (!componentWithChangedKeyPath.ContainsKey(componentId)) | ||
126 | { | ||
127 | componentWithChangedKeyPath.Add(componentId, fileId); | ||
128 | } | ||
129 | } | ||
130 | |||
131 | if (RowOperation.Delete == row.Operation) | ||
132 | { | ||
133 | // If the file is removed from a component that is not deleted. | ||
134 | if (!deletedComponent.ContainsKey(componentId)) | ||
135 | { | ||
136 | bool foundRemoveFileEntry = false; | ||
137 | string filename = Common.GetName((string)row[2], false, true); | ||
138 | |||
139 | Table removeFileTable = this.Transform.Tables["RemoveFile"]; | ||
140 | if (null != removeFileTable) | ||
141 | { | ||
142 | foreach (Row removeFileRow in removeFileTable.Rows) | ||
143 | { | ||
144 | if (RowOperation.Delete == removeFileRow.Operation) | ||
145 | { | ||
146 | continue; | ||
147 | } | ||
148 | |||
149 | if (componentId == (string)removeFileRow[1]) | ||
150 | { | ||
151 | // Check if there is a RemoveFile entry for this file | ||
152 | if (null != removeFileRow[2]) | ||
153 | { | ||
154 | string removeFileName = Common.GetName((string)removeFileRow[2], false, true); | ||
155 | |||
156 | // Convert the MSI format for a wildcard string to Regex format. | ||
157 | removeFileName = removeFileName.Replace('.', '|').Replace('?', '.').Replace("*", ".*").Replace("|", "\\."); | ||
158 | |||
159 | Regex regex = new Regex(removeFileName, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); | ||
160 | if (regex.IsMatch(filename)) | ||
161 | { | ||
162 | foundRemoveFileEntry = true; | ||
163 | break; | ||
164 | } | ||
165 | } | ||
166 | } | ||
167 | } | ||
168 | } | ||
169 | |||
170 | if (!foundRemoveFileEntry) | ||
171 | { | ||
172 | this.OnMessage(WixWarnings.InvalidRemoveFile(row.SourceLineNumbers, fileId, componentId)); | ||
173 | } | ||
174 | } | ||
175 | } | ||
176 | } | ||
177 | } | ||
178 | } | ||
179 | |||
180 | if (0 < deletedComponent.Count) | ||
181 | { | ||
182 | // Index FeatureComponents table. | ||
183 | Hashtable featureComponents = new Hashtable(); | ||
184 | |||
185 | if (null != featureComponentsTable) | ||
186 | { | ||
187 | foreach (Row row in featureComponentsTable.Rows) | ||
188 | { | ||
189 | ArrayList features; | ||
190 | string componentId = row.Fields[1].Data.ToString(); | ||
191 | |||
192 | if (featureComponents.Contains(componentId)) | ||
193 | { | ||
194 | features = (ArrayList)featureComponents[componentId]; | ||
195 | } | ||
196 | else | ||
197 | { | ||
198 | features = new ArrayList(); | ||
199 | featureComponents.Add(componentId, features); | ||
200 | } | ||
201 | features.Add(row.Fields[0].Data.ToString()); | ||
202 | } | ||
203 | } | ||
204 | |||
205 | // Check to make sure if a component was deleted, the feature was too. | ||
206 | foreach (DictionaryEntry entry in deletedComponent) | ||
207 | { | ||
208 | if (featureComponents.Contains(entry.Key.ToString())) | ||
209 | { | ||
210 | ArrayList features = (ArrayList)featureComponents[entry.Key.ToString()]; | ||
211 | foreach (string featureId in features) | ||
212 | { | ||
213 | if (!featureOps.ContainsKey(featureId) || RowOperation.Delete != (RowOperation)featureOps[featureId]) | ||
214 | { | ||
215 | // The feature was not deleted. | ||
216 | this.OnMessage(WixErrors.InvalidRemoveComponent(((Row)entry.Value).SourceLineNumbers, entry.Key.ToString(), featureId, this.transformPath)); | ||
217 | } | ||
218 | } | ||
219 | } | ||
220 | } | ||
221 | } | ||
222 | |||
223 | // Warn if new components are added to existing features | ||
224 | if (null != featureComponentsTable) | ||
225 | { | ||
226 | foreach (Row row in featureComponentsTable.Rows) | ||
227 | { | ||
228 | if (RowOperation.Add == row.Operation) | ||
229 | { | ||
230 | // Check if the feature is in the Feature table | ||
231 | string feature_ = (string)row[0]; | ||
232 | string component_ = (string)row[1]; | ||
233 | |||
234 | // Features may not be present if not referenced | ||
235 | if (!featureOps.ContainsKey(feature_) || RowOperation.Add != (RowOperation)featureOps[feature_]) | ||
236 | { | ||
237 | this.OnMessage(WixWarnings.NewComponentAddedToExistingFeature(row.SourceLineNumbers, component_, feature_, this.transformPath)); | ||
238 | } | ||
239 | } | ||
240 | } | ||
241 | } | ||
242 | #endif | ||
243 | throw new NotImplementedException(); | ||
244 | } | ||
245 | } | ||
246 | } | ||