aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core/ExtensibilityServices/ParseHelper.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/WixToolset.Core/ExtensibilityServices/ParseHelper.cs')
-rw-r--r--src/WixToolset.Core/ExtensibilityServices/ParseHelper.cs824
1 files changed, 824 insertions, 0 deletions
diff --git a/src/WixToolset.Core/ExtensibilityServices/ParseHelper.cs b/src/WixToolset.Core/ExtensibilityServices/ParseHelper.cs
new file mode 100644
index 00000000..87ad0da8
--- /dev/null
+++ b/src/WixToolset.Core/ExtensibilityServices/ParseHelper.cs
@@ -0,0 +1,824 @@
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.ExtensibilityServices
4{
5 using System;
6 using System.Collections.Generic;
7 using System.Diagnostics;
8 using System.Globalization;
9 using System.IO;
10 using System.Security.Cryptography;
11 using System.Text;
12 using System.Text.RegularExpressions;
13 using System.Xml.Linq;
14 using WixToolset.Data;
15 using WixToolset.Data.Tuples;
16 using WixToolset.Extensibility;
17 using WixToolset.Extensibility.Services;
18
19 internal class ParseHelper : IParseHelper
20 {
21 private const string LegalLongFilenameCharacters = @"[^\\\?|><:/\*""]"; // opposite of illegal above.
22 private static readonly Regex LegalLongFilename = new Regex(String.Concat("^", LegalLongFilenameCharacters, @"{1,259}$"), RegexOptions.Compiled);
23
24 private const string LegalRelativeLongFilenameCharacters = @"[^\?|><:/\*""]"; // (like legal long, but we allow '\') illegal: ? | > < : / * "
25 private static readonly Regex LegalRelativeLongFilename = new Regex(String.Concat("^", LegalRelativeLongFilenameCharacters, @"{1,259}$"), RegexOptions.Compiled);
26
27 private const string LegalWildcardLongFilenameCharacters = @"[^\\|><:/""]"; // illegal: \ | > < : / "
28 private static readonly Regex LegalWildcardLongFilename = new Regex(String.Concat("^", LegalWildcardLongFilenameCharacters, @"{1,259}$"));
29
30 private static readonly Regex LegalIdentifierWithAccess = new Regex(@"^((?<access>public|internal|protected|private)\s+)?(?<id>[_A-Za-z][0-9A-Za-z_\.]*)$", RegexOptions.Compiled | RegexOptions.ExplicitCapture);
31
32 private static readonly Regex PutGuidHere = new Regex(@"PUT\-GUID\-(?:\d+\-)?HERE", RegexOptions.Singleline);
33
34 public ParseHelper(IServiceProvider serviceProvider)
35 {
36 this.ServiceProvider = serviceProvider;
37 }
38
39 private IServiceProvider ServiceProvider { get; }
40
41 private ITupleDefinitionCreator Creator { get; set; }
42
43 public bool ContainsProperty(string possibleProperty)
44 {
45 return Common.ContainsProperty(possibleProperty);
46 }
47
48 public void CreateComplexReference(IntermediateSection section, SourceLineNumber sourceLineNumbers, ComplexReferenceParentType parentType, string parentId, string parentLanguage, ComplexReferenceChildType childType, string childId, bool isPrimary)
49 {
50 var wixComplexReferenceRow = (WixComplexReferenceTuple)this.CreateRow(section, sourceLineNumbers, TupleDefinitionType.WixComplexReference);
51 wixComplexReferenceRow.Parent = parentId;
52 wixComplexReferenceRow.ParentType = parentType;
53 wixComplexReferenceRow.ParentLanguage = parentLanguage;
54 wixComplexReferenceRow.Child = childId;
55 wixComplexReferenceRow.ChildType = childType;
56 wixComplexReferenceRow.IsPrimary = isPrimary;
57
58 this.CreateWixGroupRow(section, sourceLineNumbers, parentType, parentId, childType, childId);
59 }
60
61 public Identifier CreateDirectoryRow(IntermediateSection section, SourceLineNumber sourceLineNumbers, Identifier id, string parentId, string name, string shortName = null, string sourceName = null, string shortSourceName = null, ISet<string> sectionInlinedDirectoryIds = null)
62 {
63 string defaultDir = null;
64
65 if (name.Equals("SourceDir") || this.IsValidShortFilename(name, false))
66 {
67 defaultDir = name;
68 }
69 else
70 {
71 if (String.IsNullOrEmpty(shortName))
72 {
73 shortName = this.CreateShortName(name, false, false, "Directory", parentId);
74 }
75
76 defaultDir = String.Concat(shortName, "|", name);
77 }
78
79 if (!String.IsNullOrEmpty(sourceName))
80 {
81 if (this.IsValidShortFilename(sourceName, false))
82 {
83 defaultDir = String.Concat(defaultDir, ":", sourceName);
84 }
85 else
86 {
87 if (String.IsNullOrEmpty(shortSourceName))
88 {
89 shortSourceName = this.CreateShortName(sourceName, false, false, "Directory", parentId);
90 }
91
92 defaultDir = String.Concat(defaultDir, ":", shortSourceName, "|", sourceName);
93 }
94 }
95
96 // For anonymous directories, create the identifier. If this identifier already exists in the
97 // active section, bail so we don't add duplicate anonymous directory rows (which are legal
98 // but bloat the intermediate and ultimately make the linker do "busy work").
99 if (null == id)
100 {
101 id = this.CreateIdentifier("dir", parentId, name, shortName, sourceName, shortSourceName);
102
103 if (!sectionInlinedDirectoryIds.Add(id.Id))
104 {
105 return id;
106 }
107 }
108
109 var row = this.CreateRow(section, sourceLineNumbers, TupleDefinitionType.Directory, id);
110 row.Set(1, parentId);
111 row.Set(2, defaultDir);
112 return id;
113 }
114
115 public string CreateDirectoryReferenceFromInlineSyntax(IntermediateSection section, SourceLineNumber sourceLineNumbers, XAttribute attribute, string parentId)
116 {
117 string id = null;
118 string[] inlineSyntax = this.GetAttributeInlineDirectorySyntax(sourceLineNumbers, attribute, true);
119
120 if (null != inlineSyntax)
121 {
122 // Special case the single entry in the inline syntax since it is the most common case
123 // and needs no extra processing. It's just a reference to an existing directory.
124 if (1 == inlineSyntax.Length)
125 {
126 id = inlineSyntax[0];
127 this.CreateSimpleReference(section, sourceLineNumbers, "Directory", id);
128 }
129 else // start creating rows for the entries in the inline syntax
130 {
131 id = parentId;
132
133 int pathStartsAt = 0;
134 if (inlineSyntax[0].EndsWith(":"))
135 {
136 // TODO: should overriding the parent identifier with a specific id be an error or a warning or just let it slide?
137 //if (null != parentId)
138 //{
139 // this.core.OnMessage(WixErrors.Xxx(sourceLineNumbers));
140 //}
141
142 id = inlineSyntax[0].TrimEnd(':');
143 this.CreateSimpleReference(section, sourceLineNumbers, "Directory", id);
144
145 pathStartsAt = 1;
146 }
147
148 for (int i = pathStartsAt; i < inlineSyntax.Length; ++i)
149 {
150 Identifier inlineId = this.CreateDirectoryRow(section, sourceLineNumbers, null, id, inlineSyntax[i]);
151 id = inlineId.Id;
152 }
153 }
154 }
155
156 return id;
157 }
158
159 public string CreateGuid(Guid namespaceGuid, string value)
160 {
161 return Uuid.NewUuid(namespaceGuid, value).ToString("B").ToUpperInvariant();
162 }
163
164 public Identifier CreateIdentifier(string prefix, params string[] args)
165 {
166 var id = Common.GenerateIdentifier(prefix, args);
167 return new Identifier(id, AccessModifier.Private);
168 }
169
170 public Identifier CreateIdentifierFromFilename(string filename)
171 {
172 var id = Common.GetIdentifierFromName(filename);
173 return new Identifier(id, AccessModifier.Private);
174 }
175
176 public Identifier CreateRegistryRow(IntermediateSection section, SourceLineNumber sourceLineNumbers, int root, string key, string name, string value, string componentId, bool escapeLeadingHash)
177 {
178 Identifier id = null;
179
180 if (-1 > root || 3 < root)
181 {
182 throw new ArgumentOutOfRangeException("root");
183 }
184
185 if (null == key)
186 {
187 throw new ArgumentNullException("key");
188 }
189
190 if (null == componentId)
191 {
192 throw new ArgumentNullException("componentId");
193 }
194
195 // Escape the leading '#' character for string registry values.
196 if (escapeLeadingHash && null != value && value.StartsWith("#", StringComparison.Ordinal))
197 {
198 value = String.Concat("#", value);
199 }
200
201 id = this.CreateIdentifier("reg", componentId, root.ToString(CultureInfo.InvariantCulture.NumberFormat), key.ToLowerInvariant(), (null != name ? name.ToLowerInvariant() : name));
202
203 var row = this.CreateRow(section, sourceLineNumbers, TupleDefinitionType.Registry, id);
204 row.Set(1, root);
205 row.Set(2, key);
206 row.Set(3, name);
207 row.Set(4, value);
208 row.Set(5, componentId);
209
210 return id;
211 }
212
213 public void CreateSimpleReference(IntermediateSection section, SourceLineNumber sourceLineNumbers, string tableName, params string[] primaryKeys)
214 {
215 var joinedKeys = String.Join("/", primaryKeys);
216 var id = String.Concat(tableName, ":", joinedKeys);
217
218 var wixSimpleReferenceRow = (WixSimpleReferenceTuple)this.CreateRow(section, sourceLineNumbers, TupleDefinitionType.WixSimpleReference);
219 wixSimpleReferenceRow.Table = tableName;
220 wixSimpleReferenceRow.PrimaryKeys = joinedKeys;
221 }
222
223 public void CreateWixGroupRow(IntermediateSection section, SourceLineNumber sourceLineNumbers, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType childType, string childId)
224 {
225 if (null == parentId || ComplexReferenceParentType.Unknown == parentType)
226 {
227 return;
228 }
229
230 if (null == childId)
231 {
232 throw new ArgumentNullException("childId");
233 }
234
235 var row = (WixGroupTuple)this.CreateRow(section, sourceLineNumbers, TupleDefinitionType.WixGroup);
236 row.ParentId = parentId;
237 row.ParentType = parentType;
238 row.ChildId = childId;
239 row.ChildType = childType;
240 }
241
242 public IntermediateTuple CreateRow(IntermediateSection section, SourceLineNumber sourceLineNumbers, string tableName, Identifier identifier = null)
243 {
244 if (this.Creator == null)
245 {
246 this.CreateTupleDefinitionCreator();
247 }
248
249 if (!this.Creator.TryGetTupleDefinitionByName(tableName, out var tupleDefinition))
250 {
251 throw new ArgumentException(nameof(tableName));
252 }
253
254 return CreateRow(section, sourceLineNumbers, tupleDefinition, identifier);
255 }
256
257 public IntermediateTuple CreateRow(IntermediateSection section, SourceLineNumber sourceLineNumbers, TupleDefinitionType tupleType, Identifier identifier = null)
258 {
259 var tupleDefinition = TupleDefinitions.ByType(tupleType);
260
261 return CreateRow(section, sourceLineNumbers, tupleDefinition, identifier);
262 }
263
264 public string CreateShortName(string longName, bool keepExtension, bool allowWildcards, params string[] args)
265 {
266 // canonicalize the long name if its not a localization identifier (they are case-sensitive)
267 if (!this.IsValidLocIdentifier(longName))
268 {
269 longName = longName.ToLowerInvariant();
270 }
271
272 // collect all the data
273 List<string> strings = new List<string>(1 + args.Length);
274 strings.Add(longName);
275 strings.AddRange(args);
276
277 // prepare for hashing
278 string stringData = String.Join("|", strings);
279 byte[] data = Encoding.UTF8.GetBytes(stringData);
280
281 // hash the data
282 byte[] hash;
283 using (var sha1 = new SHA1CryptoServiceProvider())
284 {
285 hash = sha1.ComputeHash(data);
286 }
287
288 // generate the short file/directory name without an extension
289 StringBuilder shortName = new StringBuilder(Convert.ToBase64String(hash));
290 shortName.Remove(8, shortName.Length - 8).Replace('+', '-').Replace('/', '_');
291
292 if (keepExtension)
293 {
294 string extension = Path.GetExtension(longName);
295
296 if (4 < extension.Length)
297 {
298 extension = extension.Substring(0, 4);
299 }
300
301 shortName.Append(extension);
302
303 // check the generated short name to ensure its still legal (the extension may not be legal)
304 if (!this.IsValidShortFilename(shortName.ToString(), allowWildcards))
305 {
306 // remove the extension (by truncating the generated file name back to the generated characters)
307 shortName.Length -= extension.Length;
308 }
309 }
310
311 return shortName.ToString().ToLowerInvariant();
312 }
313
314 public void EnsureTable(IntermediateSection section, SourceLineNumber sourceLineNumbers, string tableName)
315 {
316 var row = this.CreateRow(section, sourceLineNumbers, TupleDefinitionType.WixEnsureTable);
317 row.Set(0, tableName);
318
319 if (this.Creator == null)
320 {
321 this.CreateTupleDefinitionCreator();
322 }
323
324 // We don't add custom table definitions to the tableDefinitions collection,
325 // so if it's not in there, it better be a custom table. If the Id is just wrong,
326 // instead of a custom table, we get an unresolved reference at link time.
327 if (!this.Creator.TryGetTupleDefinitionByName(tableName, out var ignored))
328 {
329 this.CreateSimpleReference(section, sourceLineNumbers, "WixCustomTable", tableName);
330 }
331 }
332
333 public string GetAttributeGuidValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, bool generatable = false, bool canBeEmpty = false)
334 {
335 if (null == attribute)
336 {
337 throw new ArgumentNullException("attribute");
338 }
339
340 EmptyRule emptyRule = canBeEmpty ? EmptyRule.CanBeEmpty : EmptyRule.CanBeWhitespaceOnly;
341 string value = this.GetAttributeValue(sourceLineNumbers, attribute, emptyRule);
342
343 if (String.IsNullOrEmpty(value) && canBeEmpty)
344 {
345 return String.Empty;
346 }
347 else if (!String.IsNullOrEmpty(value))
348 {
349 // If the value starts and ends with braces or parenthesis, accept that and strip them off.
350 if ((value.StartsWith("{", StringComparison.Ordinal) && value.EndsWith("}", StringComparison.Ordinal))
351 || (value.StartsWith("(", StringComparison.Ordinal) && value.EndsWith(")", StringComparison.Ordinal)))
352 {
353 value = value.Substring(1, value.Length - 2);
354 }
355
356 if (generatable && "*".Equals(value, StringComparison.Ordinal))
357 {
358 return value;
359 }
360
361 if (ParseHelper.PutGuidHere.IsMatch(value))
362 {
363 Messaging.Instance.OnMessage(WixErrors.ExampleGuid(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
364 return CompilerConstants.IllegalGuid;
365 }
366 else if (value.StartsWith("!(loc", StringComparison.Ordinal) || value.StartsWith("$(loc", StringComparison.Ordinal) || value.StartsWith("!(wix", StringComparison.Ordinal))
367 {
368 return value;
369 }
370 else if (Guid.TryParse(value, out var guid))
371 {
372 var uppercaseGuid = guid.ToString().ToUpperInvariant();
373
374 // TODO: This used to be a pedantic error, what should it be now?
375 //if (uppercaseGuid != value)
376 //{
377 // Messaging.Instance.OnMessage(WixErrors.GuidContainsLowercaseLetters(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
378 //}
379
380 return String.Concat("{", uppercaseGuid, "}");
381 }
382 else
383 {
384 Messaging.Instance.OnMessage(WixErrors.IllegalGuidValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
385 }
386 }
387
388 return CompilerConstants.IllegalGuid;
389 }
390
391 public Identifier GetAttributeIdentifier(SourceLineNumber sourceLineNumbers, XAttribute attribute)
392 {
393 var access = AccessModifier.Public;
394 var value = Common.GetAttributeValue(sourceLineNumbers, attribute, EmptyRule.CanBeEmpty);
395
396 var match = ParseHelper.LegalIdentifierWithAccess.Match(value);
397 if (!match.Success)
398 {
399 return null;
400 }
401 else if (match.Groups["access"].Success)
402 {
403 access = (AccessModifier)Enum.Parse(typeof(AccessModifier), match.Groups["access"].Value, true);
404 }
405
406 value = match.Groups["id"].Value;
407
408 if (Common.IsIdentifier(value) && 72 < value.Length)
409 {
410 Messaging.Instance.OnMessage(WixWarnings.IdentifierTooLong(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
411 }
412
413 return new Identifier(value, access);
414 }
415
416 public string GetAttributeIdentifierValue(SourceLineNumber sourceLineNumbers, XAttribute attribute)
417 {
418 return Common.GetAttributeIdentifierValue(sourceLineNumbers, attribute);
419 }
420
421 public string[] GetAttributeInlineDirectorySyntax(SourceLineNumber sourceLineNumbers, XAttribute attribute, bool resultUsedToCreateReference = false)
422 {
423 string[] result = null;
424 string value = this.GetAttributeValue(sourceLineNumbers, attribute);
425
426 if (!String.IsNullOrEmpty(value))
427 {
428 int pathStartsAt = 0;
429 result = value.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
430 if (result[0].EndsWith(":", StringComparison.Ordinal))
431 {
432 string id = result[0].TrimEnd(':');
433 if (1 == result.Length)
434 {
435 Messaging.Instance.OnMessage(WixErrors.InlineDirectorySyntaxRequiresPath(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value, id));
436 return null;
437 }
438 else if (!this.IsValidIdentifier(id))
439 {
440 Messaging.Instance.OnMessage(WixErrors.IllegalIdentifier(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value, id));
441 return null;
442 }
443
444 pathStartsAt = 1;
445 }
446 else if (resultUsedToCreateReference && 1 == result.Length)
447 {
448 if (value.EndsWith("\\"))
449 {
450 if (!this.IsValidLongFilename(result[0], false, false))
451 {
452 Messaging.Instance.OnMessage(WixErrors.IllegalLongFilename(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value, result[0]));
453 return null;
454 }
455 }
456 else if (!this.IsValidIdentifier(result[0]))
457 {
458 Messaging.Instance.OnMessage(WixErrors.IllegalIdentifier(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value, result[0]));
459 return null;
460 }
461
462 return result; // return early to avoid additional checks below.
463 }
464
465 // Check each part of the relative path to ensure that it is a valid directory name.
466 for (int i = pathStartsAt; i < result.Length; ++i)
467 {
468 if (!this.IsValidLongFilename(result[i], false, false))
469 {
470 Messaging.Instance.OnMessage(WixErrors.IllegalLongFilename(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value, result[i]));
471 return null;
472 }
473 }
474
475 if (1 < result.Length && !value.EndsWith("\\"))
476 {
477 Messaging.Instance.OnMessage(WixWarnings.BackslashTerminateInlineDirectorySyntax(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
478 }
479 }
480
481 return result;
482 }
483
484 public int GetAttributeIntegerValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, int minimum, int maximum)
485 {
486 return Common.GetAttributeIntegerValue(sourceLineNumbers, attribute, minimum, maximum);
487 }
488
489 public string GetAttributeLongFilename(SourceLineNumber sourceLineNumbers, XAttribute attribute, bool allowWildcards, bool allowRelative)
490 {
491 if (null == attribute)
492 {
493 throw new ArgumentNullException("attribute");
494 }
495
496 string value = this.GetAttributeValue(sourceLineNumbers, attribute);
497
498 if (0 < value.Length)
499 {
500 if (!this.IsValidLongFilename(value, allowWildcards, allowRelative) && !this.IsValidLocIdentifier(value))
501 {
502 if (allowRelative)
503 {
504 Messaging.Instance.OnMessage(WixErrors.IllegalRelativeLongFilename(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
505 }
506 else
507 {
508 Messaging.Instance.OnMessage(WixErrors.IllegalLongFilename(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
509 }
510 }
511 else if (allowRelative)
512 {
513 string normalizedPath = value.Replace('\\', '/');
514 if (normalizedPath.StartsWith("../", StringComparison.Ordinal) || normalizedPath.Contains("/../"))
515 {
516 Messaging.Instance.OnMessage(WixErrors.PayloadMustBeRelativeToCache(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
517 }
518 }
519 else if (CompilerCore.IsAmbiguousFilename(value))
520 {
521 Messaging.Instance.OnMessage(WixWarnings.AmbiguousFileOrDirectoryName(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
522 }
523 }
524
525 return value;
526 }
527
528 public long GetAttributeLongValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, long minimum, long maximum)
529 {
530 Debug.Assert(minimum > CompilerConstants.LongNotSet && minimum > CompilerConstants.IllegalLong, "The legal values for this attribute collide with at least one sentinel used during parsing.");
531
532 string value = this.GetAttributeValue(sourceLineNumbers, attribute);
533
534 if (0 < value.Length)
535 {
536 try
537 {
538 long longValue = Convert.ToInt64(value, CultureInfo.InvariantCulture.NumberFormat);
539
540 if (CompilerConstants.LongNotSet == longValue || CompilerConstants.IllegalLong == longValue)
541 {
542 Messaging.Instance.OnMessage(WixErrors.IntegralValueSentinelCollision(sourceLineNumbers, longValue));
543 }
544 else if (minimum > longValue || maximum < longValue)
545 {
546 Messaging.Instance.OnMessage(WixErrors.IntegralValueOutOfRange(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, longValue, minimum, maximum));
547 longValue = CompilerConstants.IllegalLong;
548 }
549
550 return longValue;
551 }
552 catch (FormatException)
553 {
554 Messaging.Instance.OnMessage(WixErrors.IllegalLongValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
555 }
556 catch (OverflowException)
557 {
558 Messaging.Instance.OnMessage(WixErrors.IllegalLongValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
559 }
560 }
561
562 return CompilerConstants.IllegalLong;
563 }
564
565 public string GetAttributeValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, EmptyRule emptyRule = EmptyRule.CanBeWhitespaceOnly)
566 {
567 return Common.GetAttributeValue(sourceLineNumbers, attribute, emptyRule);
568 }
569
570 public string GetAttributeVersionValue(SourceLineNumber sourceLineNumbers, XAttribute attribute)
571 {
572 var value = this.GetAttributeValue(sourceLineNumbers, attribute);
573
574 if (!String.IsNullOrEmpty(value))
575 {
576 if (Version.TryParse(value, out var version))
577 {
578 return version.ToString();
579 }
580
581 // Allow versions to contain binder variables.
582 if (Common.ContainsValidBinderVariable(value))
583 {
584 return value;
585 }
586
587 Messaging.Instance.OnMessage(WixErrors.IllegalVersionValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
588 }
589
590 return null;
591 }
592
593 public YesNoDefaultType GetAttributeYesNoDefaultValue(SourceLineNumber sourceLineNumbers, XAttribute attribute)
594 {
595 var value = this.GetAttributeValue(sourceLineNumbers, attribute);
596
597 switch (value)
598 {
599 case "yes":
600 case "true":
601 return YesNoDefaultType.Yes;
602
603 case "no":
604 case "false":
605 return YesNoDefaultType.No;
606
607 case "default":
608 return YesNoDefaultType.Default;
609
610 default:
611 Messaging.Instance.OnMessage(WixErrors.IllegalYesNoDefaultValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
612 return YesNoDefaultType.IllegalValue;
613 }
614 }
615
616 public YesNoType GetAttributeYesNoValue(SourceLineNumber sourceLineNumbers, XAttribute attribute)
617 {
618 var value = this.GetAttributeValue(sourceLineNumbers, attribute);
619
620 switch (value)
621 {
622 case "yes":
623 case "true":
624 return YesNoType.Yes;
625
626 case "no":
627 case "false":
628 return YesNoType.No;
629
630 default:
631 Messaging.Instance.OnMessage(WixErrors.IllegalYesNoValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
632 return YesNoType.IllegalValue;
633 }
634 }
635
636 public string GetConditionInnerText(XElement element)
637 {
638 var value = Common.GetInnerText(element)?.Trim().Replace('\t', ' ').Replace('\r', ' ').Replace('\n', ' ');
639
640 // Return null for a non-existant condition.
641 return String.IsNullOrEmpty(value) ? null : value;
642 }
643
644 public string GetTrimmedInnerText(XElement element)
645 {
646 var value = Common.GetInnerText(element);
647 return value?.Trim();
648 }
649
650 public bool IsValidIdentifier(string value)
651 {
652 return Common.IsIdentifier(value);
653 }
654
655 public bool IsValidLocIdentifier(string identifier)
656 {
657 if (String.IsNullOrEmpty(identifier))
658 {
659 return false;
660 }
661
662 var match = Common.WixVariableRegex.Match(identifier);
663
664 return (match.Success && "loc" == match.Groups["namespace"].Value && 0 == match.Index && identifier.Length == match.Length);
665 }
666
667 public bool IsValidLongFilename(string filename, bool allowWildcards, bool allowRelative)
668 {
669 if (String.IsNullOrEmpty(filename))
670 {
671 return false;
672 }
673
674 // Check for a non-period character (all periods is not legal)
675 bool nonPeriodFound = false;
676 foreach (char character in filename)
677 {
678 if ('.' != character)
679 {
680 nonPeriodFound = true;
681 break;
682 }
683 }
684
685 if (allowWildcards)
686 {
687 return (nonPeriodFound && ParseHelper.LegalWildcardLongFilename.IsMatch(filename));
688 }
689 else if (allowRelative)
690 {
691 return (nonPeriodFound && ParseHelper.LegalRelativeLongFilename.IsMatch(filename));
692 }
693 else
694 {
695 return (nonPeriodFound && ParseHelper.LegalLongFilename.IsMatch(filename));
696 }
697 }
698
699 public bool IsValidShortFilename(string filename, bool allowWildcards = false)
700 {
701 return Common.IsValidShortFilename(filename, allowWildcards);
702 }
703
704 public void ParseExtensionAttribute(IEnumerable<ICompilerExtension> extensions, Intermediate intermediate, IntermediateSection section, XElement element, XAttribute attribute, IDictionary<string, string> context = null)
705 {
706 // Ignore attributes defined by the W3C because we'll assume they are always right.
707 if ((String.IsNullOrEmpty(attribute.Name.NamespaceName) && attribute.Name.LocalName.Equals("xmlns", StringComparison.Ordinal)) ||
708 attribute.Name.NamespaceName.StartsWith(CompilerCore.W3SchemaPrefix.NamespaceName, StringComparison.Ordinal))
709 {
710 return;
711 }
712
713 if (ParseHelper.TryFindExtension(extensions, attribute.Name.NamespaceName, out var extension))
714 {
715 extension.ParseAttribute(intermediate, section, element, attribute, context);
716 }
717 else
718 {
719 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(element);
720 Messaging.Instance.OnMessage(WixErrors.UnhandledExtensionAttribute(sourceLineNumbers, element.Name.LocalName, attribute.Name.LocalName, attribute.Name.NamespaceName));
721 }
722 }
723
724 public void ParseExtensionElement(IEnumerable<ICompilerExtension> extensions, Intermediate intermediate, IntermediateSection section, XElement parentElement, XElement element, IDictionary<string, string> context = null)
725 {
726 if (ParseHelper.TryFindExtension(extensions, element.Name.Namespace, out var extension))
727 {
728 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(parentElement);
729 extension.ParseElement(intermediate, section, parentElement, element, context);
730 }
731 else
732 {
733 var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(element);
734 Messaging.Instance.OnMessage(WixErrors.UnhandledExtensionElement(childSourceLineNumbers, parentElement.Name.LocalName, element.Name.LocalName, element.Name.NamespaceName));
735 }
736 }
737
738 public ComponentKeyPath ParsePossibleKeyPathExtensionElement(IEnumerable<ICompilerExtension> extensions, Intermediate intermediate, IntermediateSection section, XElement parentElement, XElement element, IDictionary<string, string> context)
739 {
740 ComponentKeyPath keyPath = null;
741
742 if (ParseHelper.TryFindExtension(extensions, element.Name.Namespace, out var extension))
743 {
744 keyPath = extension.ParsePossibleKeyPathElement(intermediate, section, parentElement, element, context);
745 }
746 else
747 {
748 var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(element);
749 Messaging.Instance.OnMessage(WixErrors.UnhandledExtensionElement(childSourceLineNumbers, parentElement.Name.LocalName, element.Name.LocalName, element.Name.NamespaceName));
750 }
751
752 return keyPath;
753 }
754
755 public void ParseForExtensionElements(IEnumerable<ICompilerExtension> extensions, Intermediate intermediate, IntermediateSection section, XElement element)
756 {
757 foreach (XElement child in element.Elements())
758 {
759 if (element.Name.Namespace == child.Name.Namespace)
760 {
761 this.UnexpectedElement(element, child);
762 }
763 else
764 {
765 this.ParseExtensionElement(extensions, intermediate, section, element, child);
766 }
767 }
768 }
769
770 public void UnexpectedAttribute(XElement element, XAttribute attribute)
771 {
772 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(element);
773 Common.UnexpectedAttribute(sourceLineNumbers, attribute);
774 }
775
776 public void UnexpectedElement(XElement parentElement, XElement childElement)
777 {
778 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(childElement);
779 Messaging.Instance.OnMessage(WixErrors.UnexpectedElement(sourceLineNumbers, parentElement.Name.LocalName, childElement.Name.LocalName));
780 }
781
782 private void CreateTupleDefinitionCreator()
783 {
784 this.Creator = (ITupleDefinitionCreator)this.ServiceProvider.GetService(typeof(ITupleDefinitionCreator));
785 }
786
787 private static IntermediateTuple CreateRow(IntermediateSection section, SourceLineNumber sourceLineNumbers, IntermediateTupleDefinition tupleDefinition, Identifier identifier)
788 {
789 var row = tupleDefinition.CreateTuple(sourceLineNumbers, identifier);
790
791 if (null != identifier)
792 {
793 if (row.Definition.FieldDefinitions[0].Type == IntermediateFieldType.Number)
794 {
795 row.Set(0, Convert.ToInt32(identifier.Id));
796 }
797 else
798 {
799 row.Set(0, identifier.Id);
800 }
801 }
802
803 section.Tuples.Add(row);
804
805 return row;
806 }
807
808 private static bool TryFindExtension(IEnumerable<ICompilerExtension> extensions, XNamespace ns, out ICompilerExtension extension)
809 {
810 extension = null;
811
812 foreach (var ext in extensions)
813 {
814 if (ext.Namespace == ns)
815 {
816 extension = ext;
817 break;
818 }
819 }
820
821 return extension != null;
822 }
823 }
824}