aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core/Common.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/WixToolset.Core/Common.cs')
-rw-r--r--src/WixToolset.Core/Common.cs665
1 files changed, 665 insertions, 0 deletions
diff --git a/src/WixToolset.Core/Common.cs b/src/WixToolset.Core/Common.cs
new file mode 100644
index 00000000..0404de5e
--- /dev/null
+++ b/src/WixToolset.Core/Common.cs
@@ -0,0 +1,665 @@
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.Diagnostics;
7 using System.Globalization;
8 using System.IO;
9 using System.Linq;
10 using System.Security.Cryptography;
11 using System.Text;
12 using System.Text.RegularExpressions;
13 using System.Xml;
14 using System.Xml.Linq;
15 using WixToolset.Data;
16
17 /// <summary>
18 /// Common Wix utility methods and types.
19 /// </summary>
20 internal static class Common
21 {
22 //-------------------------------------------------------------------------------------------------
23 // Layout of an Access Mask (from http://technet.microsoft.com/en-us/library/cc783530(WS.10).aspx)
24 //
25 // -------------------------------------------------------------------------------------------------
26 // |31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10|09|08|07|06|05|04|03|02|01|00|
27 // -------------------------------------------------------------------------------------------------
28 // |GR|GW|GE|GA| Reserved |AS|StandardAccessRights| Object-Specific Access Rights |
29 //
30 // Key
31 // GR = Generic Read
32 // GW = Generic Write
33 // GE = Generic Execute
34 // GA = Generic All
35 // AS = Right to access SACL
36 //
37 // TODO: what is the expected decompile behavior if a bit is found that is not explicitly enumerated
38 //
39 //-------------------------------------------------------------------------------------------------
40 // Generic Access Rights (per WinNT.h)
41 // ---------------------
42 // GENERIC_ALL (0x10000000L)
43 // GENERIC_EXECUTE (0x20000000L)
44 // GENERIC_WRITE (0x40000000L)
45 // GENERIC_READ (0x80000000L)
46 internal static readonly string[] GenericPermissions = { "GenericAll", "GenericExecute", "GenericWrite", "GenericRead" };
47
48 // Standard Access Rights (per WinNT.h)
49 // ----------------------
50 // DELETE (0x00010000L)
51 // READ_CONTROL (0x00020000L)
52 // WRITE_DAC (0x00040000L)
53 // WRITE_OWNER (0x00080000L)
54 // SYNCHRONIZE (0x00100000L)
55 internal static readonly string[] StandardPermissions = { "Delete", "ReadPermission", "ChangePermission", "TakeOwnership", "Synchronize" };
56
57 // Object-Specific Access Rights
58 // =============================
59 // Directory Access Rights (per WinNT.h)
60 // -----------------------
61 // FILE_LIST_DIRECTORY ( 0x0001 )
62 // FILE_ADD_FILE ( 0x0002 )
63 // FILE_ADD_SUBDIRECTORY ( 0x0004 )
64 // FILE_READ_EA ( 0x0008 )
65 // FILE_WRITE_EA ( 0x0010 )
66 // FILE_TRAVERSE ( 0x0020 )
67 // FILE_DELETE_CHILD ( 0x0040 )
68 // FILE_READ_ATTRIBUTES ( 0x0080 )
69 // FILE_WRITE_ATTRIBUTES ( 0x0100 )
70 internal static readonly string[] FolderPermissions = { "Read", "CreateFile", "CreateChild", "ReadExtendedAttributes", "WriteExtendedAttributes", "Traverse", "DeleteChild", "ReadAttributes", "WriteAttributes" };
71
72 // Registry Access Rights (per TODO)
73 // ----------------------
74 internal static readonly string[] RegistryPermissions = { "Read", "Write", "CreateSubkeys", "EnumerateSubkeys", "Notify", "CreateLink" };
75
76 // File Access Rights (per WinNT.h)
77 // ------------------
78 // FILE_READ_DATA ( 0x0001 )
79 // FILE_WRITE_DATA ( 0x0002 )
80 // FILE_APPEND_DATA ( 0x0004 )
81 // FILE_READ_EA ( 0x0008 )
82 // FILE_WRITE_EA ( 0x0010 )
83 // FILE_EXECUTE ( 0x0020 )
84 // via mask FILE_ALL_ACCESS ( 0x0040 )
85 // FILE_READ_ATTRIBUTES ( 0x0080 )
86 // FILE_WRITE_ATTRIBUTES ( 0x0100 )
87 //
88 // STANDARD_RIGHTS_REQUIRED (0x000F0000L)
89 // FILE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF)
90 internal static readonly string[] FilePermissions = { "Read", "Write", "Append", "ReadExtendedAttributes", "WriteExtendedAttributes", "Execute", "FileAllRights", "ReadAttributes", "WriteAttributes" };
91
92 internal static readonly string[] ReservedFileNames = { "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9" };
93
94 internal static readonly Regex WixVariableRegex = new Regex(@"(\!|\$)\((?<namespace>loc|wix|bind|bindpath)\.(?<fullname>(?<name>[_A-Za-z][0-9A-Za-z_]+)(\.(?<scope>[_A-Za-z][0-9A-Za-z_\.]*))?)(\=(?<value>.+?))?\)", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.ExplicitCapture);
95
96 internal const char CustomRowFieldSeparator = '\x85';
97
98 private static readonly Regex PropertySearch = new Regex(@"\[[#$!]?[a-zA-Z_][a-zA-Z0-9_\.]*]", RegexOptions.Singleline);
99 private static readonly Regex AddPrefix = new Regex(@"^[^a-zA-Z_]", RegexOptions.Compiled);
100 private static readonly Regex LegalIdentifierCharacters = new Regex(@"^[_A-Za-z][0-9A-Za-z_\.]*$", RegexOptions.Compiled);
101 private static readonly Regex IllegalIdentifierCharacters = new Regex(@"[^A-Za-z0-9_\.]|\.{2,}", RegexOptions.Compiled); // non 'words' and assorted valid characters
102
103 private const string LegalShortFilenameCharacters = @"[^\\\?|><:/\*""\+,;=\[\]\. ]"; // illegal: \ ? | > < : / * " + , ; = [ ] . (space)
104 private static readonly Regex LegalShortFilename = new Regex(String.Concat("^", LegalShortFilenameCharacters, @"{1,8}(\.", LegalShortFilenameCharacters, "{0,3})?$"), RegexOptions.Compiled);
105
106 private const string LegalWildcardShortFilenameCharacters = @"[^\\|><:/""\+,;=\[\]\. ]"; // illegal: \ | > < : / " + , ; = [ ] . (space)
107 private static readonly Regex LegalWildcardShortFilename = new Regex(String.Concat("^", LegalWildcardShortFilenameCharacters, @"{1,16}(\.", LegalWildcardShortFilenameCharacters, "{0,6})?$"));
108
109 /// <summary>
110 /// Cleans up the temp files.
111 /// </summary>
112 /// <param name="path">The temporary directory to delete.</param>
113 /// <param name="messageHandler">The message handler.</param>
114 /// <returns>True if all files were deleted, false otherwise.</returns>
115 internal static bool DeleteTempFiles(string path, IMessageHandler messageHandler)
116 {
117 // try three times and give up with a warning if the temp files aren't gone by then
118 int retryLimit = 3;
119 bool removedReadOnly = false;
120
121 for (int i = 0; i < retryLimit; i++)
122 {
123 try
124 {
125 Directory.Delete(path, true); // toast the whole temp directory
126 break; // no exception means we got success the first time
127 }
128 catch (UnauthorizedAccessException)
129 {
130 if (!removedReadOnly) // should only need to unmark readonly once - there's no point in doing it again and again
131 {
132 removedReadOnly = true;
133 RecursiveFileAttributes(path, FileAttributes.ReadOnly, false, messageHandler); // toasting will fail if any files are read-only. Try changing them to not be.
134 }
135 else
136 {
137 messageHandler.OnMessage(WixWarnings.AccessDeniedForDeletion(null, path));
138 return false;
139 }
140 }
141 catch (DirectoryNotFoundException)
142 {
143 // if the path doesn't exist, then there is nothing for us to worry about
144 break;
145 }
146 catch (IOException) // directory in use
147 {
148 if (i == (retryLimit - 1)) // last try failed still, give up
149 {
150 messageHandler.OnMessage(WixWarnings.DirectoryInUse(null, path));
151 return false;
152 }
153
154 System.Threading.Thread.Sleep(300); // sleep a bit before trying again
155 }
156 }
157
158 return true;
159 }
160
161 /// <summary>
162 /// Gets a valid code page from the given web name or integer value.
163 /// </summary>
164 /// <param name="value">A code page web name or integer value as a string.</param>
165 /// <param name="allowNoChange">Whether to allow -1 which does not change the database code pages. This may be the case with wxl files.</param>
166 /// <param name="onlyAnsi">Whether to allow Unicode (UCS) or UTF code pages.</param>
167 /// <param name="sourceLineNumbers">Source line information for the current authoring.</param>
168 /// <returns>A valid code page number.</returns>
169 /// <exception cref="ArgumentOutOfRangeException">The value is an integer less than 0 or greater than 65535.</exception>
170 /// <exception cref="ArgumentNullException"><paramref name="value"/> is null.</exception>
171 /// <exception cref="NotSupportedException">The value doesn't not represent a valid code page name or integer value.</exception>
172 /// <exception cref="WixException">The code page is invalid for summary information.</exception>
173 internal static int GetValidCodePage(string value, bool allowNoChange = false, bool onlyAnsi = false, SourceLineNumber sourceLineNumbers = null)
174 {
175 int codePage;
176 Encoding encoding;
177
178 try
179 {
180 // check if a integer as a string was passed
181 if (Int32.TryParse(value, out codePage))
182 {
183 if (0 == codePage)
184 {
185 // 0 represents a neutral database
186 return 0;
187 }
188 else if (allowNoChange && -1 == codePage)
189 {
190 // -1 means no change to the database code page
191 return -1;
192 }
193
194 encoding = Encoding.GetEncoding(codePage);
195 }
196 else
197 {
198 encoding = Encoding.GetEncoding(value);
199 }
200
201 // Windows Installer parses some code page references
202 // as unsigned shorts which fail to open the database.
203 if (onlyAnsi)
204 {
205 codePage = encoding.CodePage;
206 if (0 > codePage || Int16.MaxValue < codePage)
207 {
208 throw new WixException(WixErrors.InvalidSummaryInfoCodePage(sourceLineNumbers, codePage));
209 }
210 }
211
212 return encoding.CodePage;
213 }
214 catch (ArgumentException ex)
215 {
216 // rethrow as NotSupportedException since either can be thrown
217 // if the system does not support the specified code page
218 throw new NotSupportedException(ex.Message, ex);
219 }
220 }
221
222 /// <summary>
223 /// Verifies if a filename is a valid short filename.
224 /// </summary>
225 /// <param name="filename">Filename to verify.</param>
226 /// <param name="allowWildcards">true if wildcards are allowed in the filename.</param>
227 /// <returns>True if the filename is a valid short filename</returns>
228 internal static bool IsValidShortFilename(string filename, bool allowWildcards)
229 {
230 if (String.IsNullOrEmpty(filename))
231 {
232 return false;
233 }
234
235 if (allowWildcards)
236 {
237 if (Common.LegalWildcardShortFilename.IsMatch(filename))
238 {
239 bool foundPeriod = false;
240 int beforePeriod = 0;
241 int afterPeriod = 0;
242
243 // count the number of characters before and after the period
244 // '*' is not counted because it may represent zero characters
245 foreach (char character in filename)
246 {
247 if ('.' == character)
248 {
249 foundPeriod = true;
250 }
251 else if ('*' != character)
252 {
253 if (foundPeriod)
254 {
255 afterPeriod++;
256 }
257 else
258 {
259 beforePeriod++;
260 }
261 }
262 }
263
264 if (8 >= beforePeriod && 3 >= afterPeriod)
265 {
266 return true;
267 }
268 }
269
270 return false;
271 }
272 else
273 {
274 return Common.LegalShortFilename.IsMatch(filename);
275 }
276 }
277
278 /// <summary>
279 /// Verifies if an identifier is a valid binder variable name.
280 /// </summary>
281 /// <param name="name">Binder variable name to verify.</param>
282 /// <returns>True if the identifier is a valid binder variable name.</returns>
283 public static bool IsValidBinderVariable(string name)
284 {
285 if (String.IsNullOrEmpty(name))
286 {
287 return false;
288 }
289
290 Match match = Common.WixVariableRegex.Match(name);
291
292 return (match.Success && ("bind" == match.Groups["namespace"].Value || "wix" == match.Groups["namespace"].Value) && 0 == match.Index && name.Length == match.Length);
293 }
294
295 /// <summary>
296 /// Verifies if a string contains a valid binder variable name.
297 /// </summary>
298 /// <param name="name">String to verify.</param>
299 /// <returns>True if the string contains a valid binder variable name.</returns>
300 public static bool ContainsValidBinderVariable(string name)
301 {
302 if (String.IsNullOrEmpty(name))
303 {
304 return false;
305 }
306
307 Match match = Common.WixVariableRegex.Match(name);
308
309 return match.Success && ("bind" == match.Groups["namespace"].Value || "wix" == match.Groups["namespace"].Value);
310 }
311
312 /// <summary>
313 /// Get the value of an attribute with type YesNoType.
314 /// </summary>
315 /// <param name="sourceLineNumbers">Source information for the value.</param>
316 /// <param name="elementName">Name of the element for this attribute, used for a possible exception.</param>
317 /// <param name="attributeName">Name of the attribute.</param>
318 /// <param name="value">Value to process.</param>
319 /// <returns>Returns true for a value of 'yes' and false for a value of 'no'.</returns>
320 /// <exception cref="WixException">Thrown when the attribute's value is not 'yes' or 'no'.</exception>
321 internal static bool IsYes(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string value)
322 {
323 switch (value)
324 {
325 case "no":
326 case "false":
327 return false;
328 case "yes":
329 case "true":
330 return true;
331 default:
332 throw new WixException(WixErrors.IllegalAttributeValue(sourceLineNumbers, elementName, attributeName, value, "no", "yes"));
333 }
334 }
335
336 /// <summary>
337 /// Verifies the given string is a valid module or bundle version.
338 /// </summary>
339 /// <param name="version">The version to verify.</param>
340 /// <returns>True if version is a valid module or bundle version.</returns>
341 public static bool IsValidModuleOrBundleVersion(string version)
342 {
343 if (!Common.IsValidBinderVariable(version))
344 {
345 Version ver = null;
346
347 try
348 {
349 ver = new Version(version);
350 }
351 catch (ArgumentException)
352 {
353 return false;
354 }
355
356 if (65535 < ver.Major || 65535 < ver.Minor || 65535 < ver.Build || 65535 < ver.Revision)
357 {
358 return false;
359 }
360 }
361
362 return true;
363 }
364
365 /// <summary>
366 /// Generate a new Windows Installer-friendly guid.
367 /// </summary>
368 /// <returns>A new guid.</returns>
369 internal static string GenerateGuid()
370 {
371 return Guid.NewGuid().ToString("B").ToUpper(CultureInfo.InvariantCulture);
372 }
373
374 /// <summary>
375 /// Generate an identifier by hashing data from the row.
376 /// </summary>
377 /// <param name="prefix">Three letter or less prefix for generated row identifier.</param>
378 /// <param name="args">Information to hash.</param>
379 /// <returns>The generated identifier.</returns>
380 public static string GenerateIdentifier(string prefix, params string[] args)
381 {
382 string stringData = String.Join("|", args);
383 byte[] data = Encoding.UTF8.GetBytes(stringData);
384
385 // hash the data
386 byte[] hash;
387 using (SHA1 sha1 = new SHA1CryptoServiceProvider())
388 {
389 hash = sha1.ComputeHash(data);
390 }
391
392 // Build up the identifier.
393 StringBuilder identifier = new StringBuilder(35, 35);
394 identifier.Append(prefix);
395 identifier.Append(Convert.ToBase64String(hash).TrimEnd('='));
396 identifier.Replace('+', '.').Replace('/', '_');
397
398 return identifier.ToString();
399 }
400
401 /// <summary>
402 /// Return an identifier based on provided file or directory name
403 /// </summary>
404 /// <param name="name">File/directory name to generate identifer from</param>
405 /// <returns>A version of the name that is a legal identifier.</returns>
406 internal static string GetIdentifierFromName(string name)
407 {
408 string result = IllegalIdentifierCharacters.Replace(name, "_"); // replace illegal characters with "_".
409
410 // MSI identifiers must begin with an alphabetic character or an
411 // underscore. Prefix all other values with an underscore.
412 if (AddPrefix.IsMatch(name))
413 {
414 result = String.Concat("_", result);
415 }
416
417 return result;
418 }
419
420 /// <summary>
421 /// Checks if the string contains a property (i.e. "foo[Property]bar")
422 /// </summary>
423 /// <param name="possibleProperty">String to evaluate for properties.</param>
424 /// <returns>True if a property is found in the string.</returns>
425 internal static bool ContainsProperty(string possibleProperty)
426 {
427 return PropertySearch.IsMatch(possibleProperty);
428 }
429
430 /// <summary>
431 /// Recursively loops through a directory, changing an attribute on all of the underlying files.
432 /// An example is to add/remove the ReadOnly flag from each file.
433 /// </summary>
434 /// <param name="path">The directory path to start deleting from.</param>
435 /// <param name="fileAttribute">The FileAttribute to change on each file.</param>
436 /// <param name="messageHandler">The message handler.</param>
437 /// <param name="markAttribute">If true, add the attribute to each file. If false, remove it.</param>
438 private static void RecursiveFileAttributes(string path, FileAttributes fileAttribute, bool markAttribute, IMessageHandler messageHandler)
439 {
440 foreach (string subDirectory in Directory.GetDirectories(path))
441 {
442 RecursiveFileAttributes(subDirectory, fileAttribute, markAttribute, messageHandler);
443 }
444
445 foreach (string filePath in Directory.GetFiles(path))
446 {
447 FileAttributes attributes = File.GetAttributes(filePath);
448 if (markAttribute)
449 {
450 attributes = attributes | fileAttribute; // add to list of attributes
451 }
452 else if (fileAttribute == (attributes & fileAttribute)) // if attribute set
453 {
454 attributes = attributes ^ fileAttribute; // remove from list of attributes
455 }
456
457 try
458 {
459 File.SetAttributes(filePath, attributes);
460 }
461 catch (UnauthorizedAccessException)
462 {
463 messageHandler.OnMessage(WixWarnings.AccessDeniedForSettingAttributes(null, filePath));
464 }
465 }
466 }
467
468 internal static string GetFileHash(string path)
469 {
470 using (SHA1Managed managed = new SHA1Managed())
471 {
472 using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.Read))
473 {
474 byte[] hash = managed.ComputeHash(stream);
475 return BitConverter.ToString(hash).Replace("-", String.Empty);
476 }
477 }
478 }
479
480 /// <summary>
481 /// Get an attribute value.
482 /// </summary>
483 /// <param name="sourceLineNumbers">Source line information about the owner element.</param>
484 /// <param name="attribute">The attribute containing the value to get.</param>
485 /// <param name="emptyRule">A rule for the contents of the value. If the contents do not follow the rule, an error is thrown.</param>
486 /// <param name="messageHandler">A delegate that receives error messages.</param>
487 /// <returns>The attribute's value.</returns>
488 internal static string GetAttributeValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, EmptyRule emptyRule)
489 {
490 string value = attribute.Value;
491
492 if ((emptyRule == EmptyRule.MustHaveNonWhitespaceCharacters && String.IsNullOrEmpty(value.Trim())) ||
493 (emptyRule == EmptyRule.CanBeWhitespaceOnly && String.IsNullOrEmpty(value)))
494 {
495 Messaging.Instance.OnMessage(WixErrors.IllegalEmptyAttributeValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName));
496 return String.Empty;
497 }
498
499 return value;
500 }
501
502 /// <summary>
503 /// Verifies that a value is a legal identifier.
504 /// </summary>
505 /// <param name="value">The value to verify.</param>
506 /// <returns>true if the value is an identifier; false otherwise.</returns>
507 public static bool IsIdentifier(string value)
508 {
509 if (!String.IsNullOrEmpty(value))
510 {
511 if (LegalIdentifierCharacters.IsMatch(value))
512 {
513 return true;
514 }
515 }
516
517 return false;
518 }
519
520 /// <summary>
521 /// Get an identifier attribute value and displays an error for an illegal identifier value.
522 /// </summary>
523 /// <param name="sourceLineNumbers">Source line information about the owner element.</param>
524 /// <param name="attribute">The attribute containing the value to get.</param>
525 /// <param name="messageHandler">A delegate that receives error messages.</param>
526 /// <returns>The attribute's identifier value or a special value if an error occurred.</returns>
527 internal static string GetAttributeIdentifierValue(SourceLineNumber sourceLineNumbers, XAttribute attribute)
528 {
529 string value = Common.GetAttributeValue(sourceLineNumbers, attribute, EmptyRule.CanBeWhitespaceOnly);
530
531 if (Common.IsIdentifier(value))
532 {
533 if (72 < value.Length)
534 {
535 Messaging.Instance.OnMessage(WixWarnings.IdentifierTooLong(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
536 }
537
538 return value;
539 }
540 else
541 {
542 if (value.StartsWith("[", StringComparison.Ordinal) && value.EndsWith("]", StringComparison.Ordinal))
543 {
544 Messaging.Instance.OnMessage(WixErrors.IllegalIdentifierLooksLikeFormatted(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
545 }
546 else
547 {
548 Messaging.Instance.OnMessage(WixErrors.IllegalIdentifier(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
549 }
550
551 return String.Empty;
552 }
553 }
554
555 /// <summary>
556 /// Get an integer attribute value and displays an error for an illegal integer value.
557 /// </summary>
558 /// <param name="sourceLineNumbers">Source line information about the owner element.</param>
559 /// <param name="attribute">The attribute containing the value to get.</param>
560 /// <param name="minimum">The minimum legal value.</param>
561 /// <param name="maximum">The maximum legal value.</param>
562 /// <param name="messageHandler">A delegate that receives error messages.</param>
563 /// <returns>The attribute's integer value or a special value if an error occurred during conversion.</returns>
564 public static int GetAttributeIntegerValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, int minimum, int maximum)
565 {
566 Debug.Assert(minimum > CompilerConstants.IntegerNotSet && minimum > CompilerConstants.IllegalInteger, "The legal values for this attribute collide with at least one sentinel used during parsing.");
567
568 string value = Common.GetAttributeValue(sourceLineNumbers, attribute, EmptyRule.CanBeWhitespaceOnly);
569 int integer = CompilerConstants.IllegalInteger;
570
571 if (0 < value.Length)
572 {
573 if (Int32.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture.NumberFormat, out integer))
574 {
575 if (CompilerConstants.IntegerNotSet == integer || CompilerConstants.IllegalInteger == integer)
576 {
577 Messaging.Instance.OnMessage(WixErrors.IntegralValueSentinelCollision(sourceLineNumbers, integer));
578 }
579 else if (minimum > integer || maximum < integer)
580 {
581 Messaging.Instance.OnMessage(WixErrors.IntegralValueOutOfRange(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, integer, minimum, maximum));
582 integer = CompilerConstants.IllegalInteger;
583 }
584 }
585 else
586 {
587 Messaging.Instance.OnMessage(WixErrors.IllegalIntegerValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
588 }
589 }
590
591 return integer;
592 }
593
594 /// <summary>
595 /// Gets a yes/no value and displays an error for an illegal yes/no value.
596 /// </summary>
597 /// <param name="sourceLineNumbers">Source line information about the owner element.</param>
598 /// <param name="attribute">The attribute containing the value to get.</param>
599 /// <param name="messageHandler">A delegate that receives error messages.</param>
600 /// <returns>The attribute's YesNoType value.</returns>
601 internal static YesNoType GetAttributeYesNoValue(SourceLineNumber sourceLineNumbers, XAttribute attribute)
602 {
603 string value = Common.GetAttributeValue(sourceLineNumbers, attribute, EmptyRule.CanBeWhitespaceOnly);
604 YesNoType yesNo = YesNoType.IllegalValue;
605
606 if ("yes".Equals(value) || "true".Equals(value))
607 {
608 yesNo = YesNoType.Yes;
609 }
610 else if ("no".Equals(value) || "false".Equals(value))
611 {
612 yesNo = YesNoType.No;
613 }
614 else
615 {
616 Messaging.Instance.OnMessage(WixErrors.IllegalYesNoValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
617 }
618
619 return yesNo;
620 }
621
622 /// <summary>
623 /// Gets the text of an XElement.
624 /// </summary>
625 /// <param name="sourceLineNumbers">Source line information about the owner element.</param>
626 /// <param name="attribute">The attribute containing the value to get.</param>
627 /// <param name="messageHandler">A delegate that receives error messages.</param>
628 /// <returns>The attribute's YesNoType value.</returns>
629 internal static string GetInnerText(XElement node)
630 {
631 XText text = node.Nodes().Where(n => XmlNodeType.Text == n.NodeType || XmlNodeType.CDATA == n.NodeType).Cast<XText>().FirstOrDefault();
632 return (null == text) ? null : text.Value;
633 }
634
635 /// <summary>
636 /// Display an unexpected attribute error.
637 /// </summary>
638 /// <param name="sourceLineNumbers">Source line information about the owner element.</param>
639 /// <param name="attribute">The attribute.</param>
640 public static void UnexpectedAttribute(SourceLineNumber sourceLineNumbers, XAttribute attribute)
641 {
642 // Ignore elements defined by the W3C because we'll assume they are always right.
643 if (!((String.IsNullOrEmpty(attribute.Name.NamespaceName) && attribute.Name.LocalName.Equals("xmlns", StringComparison.Ordinal)) ||
644 attribute.Name.NamespaceName.StartsWith(CompilerCore.W3SchemaPrefix.NamespaceName, StringComparison.Ordinal)))
645 {
646 Messaging.Instance.OnMessage(WixErrors.UnexpectedAttribute(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName));
647 }
648 }
649
650 /// <summary>
651 /// Display an unsupported extension attribute error.
652 /// </summary>
653 /// <param name="sourceLineNumbers">Source line information about the owner element.</param>
654 /// <param name="extensionAttribute">The extension attribute.</param>
655 internal static void UnsupportedExtensionAttribute(SourceLineNumber sourceLineNumbers, XAttribute extensionAttribute)
656 {
657 // Ignore elements defined by the W3C because we'll assume they are always right.
658 if (!((String.IsNullOrEmpty(extensionAttribute.Name.NamespaceName) && extensionAttribute.Name.LocalName.Equals("xmlns", StringComparison.Ordinal)) ||
659 extensionAttribute.Name.NamespaceName.StartsWith(CompilerCore.W3SchemaPrefix.NamespaceName, StringComparison.Ordinal)))
660 {
661 Messaging.Instance.OnMessage(WixErrors.UnsupportedExtensionAttribute(sourceLineNumbers, extensionAttribute.Parent.Name.LocalName, extensionAttribute.Name.LocalName));
662 }
663 }
664 }
665}