aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core.WindowsInstaller/Bind/PatchTransform.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/PatchTransform.cs246
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
3namespace 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}