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