1 // Copyright (c) Microsoft. All rights reserved.
2 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3 
4 using Microsoft.Build.Shared;
5 using System;
6 using System.Collections;
7 using System.Collections.Generic;
8 using System.Diagnostics;
9 using System.Diagnostics.CodeAnalysis;
10 using System.Globalization;
11 using System.IO;
12 using System.Reflection;
13 using System.Runtime.InteropServices;
14 using System.Security.Cryptography.X509Certificates;
15 using System.Text;
16 using System.Xml;
17 using System.Xml.XPath;
18 using System.Xml.Xsl;
19 
20 namespace Microsoft.Build.Tasks.Deployment.Bootstrapper
21 {
22     /// <summary>
23     /// This class is the top-level object for the bootstrapper system.
24     /// </summary>
25     [ComVisibleAttribute(true), GuidAttribute("1D9FE38A-0226-4b95-9C6B-6DFFA2236270"), ClassInterface(ClassInterfaceType.None)]
26     public class BootstrapperBuilder : IBootstrapperBuilder
27     {
28         private static readonly bool s_logging = !String.IsNullOrEmpty(Environment.GetEnvironmentVariable("VSPLOG"));
29         private static readonly string s_logPath = GetLogPath();
30 
31         private string _path;
32         private XmlDocument _document;
33 
34         private XmlNamespaceManager _xmlNamespaceManager;
35         private ProductCollection _products = new ProductCollection();
36         private Hashtable _cultures = new Hashtable();
37         private Hashtable _validationResults = new Hashtable();
38         private BuildResults _results;
39         private BuildResults _loopDependenciesWarnings;
40         private bool _fValidate = true;
41         private bool _fInitialized = false;
42 
43         private const string SETUP_EXE = "setup.exe";
44         private const string SETUP_BIN = "setup.bin";
45         private const string SETUP_RESOURCES_FILE = "setup.xml";
46 
47         private const string ENGINE_PATH = "Engine"; // relative to bootstrapper path
48         private const string SCHEMA_PATH = "Schemas"; // relative to bootstrapper path
49         private const string PACKAGE_PATH = "Packages"; // relative to bootstrapper path
50         private const string RESOURCES_PATH = "";
51 
52         private const string BOOTSTRAPPER_NAMESPACE = "http://schemas.microsoft.com/developer/2004/01/bootstrapper";
53 
54         private const string BOOTSTRAPPER_PREFIX = "bootstrapper";
55 
56         private const string ROOT_MANIFEST_FILE = "product.xml";
57         private const string CHILD_MANIFEST_FILE = "package.xml";
58         private const string MANIFEST_FILE_SCHEMA = "package.xsd";
59         private const string CONFIG_TRANSFORM = "xmltoconfig.xsl";
60 
61         private const string EULA_ATTRIBUTE = "LicenseAgreement";
62         private const string HOMESITE_ATTRIBUTE = "HomeSite";
63         private const string PUBLICKEY_ATTRIBUTE = "PublicKey";
64         private const string URLNAME_ATTRIBUTE = "UrlName";
65         private const string HASH_ATTRIBUTE = "Hash";
66 
67         private const int MESSAGE_TABLE = 43;
68         private const int RESOURCE_TABLE = 45;
69 
70         /// <summary>
71         /// Creates a new BootstrapperBuilder.
72         /// </summary>
BootstrapperBuilder()73         public BootstrapperBuilder()
74         {
75             _path = Util.DefaultPath;
76         }
77 
78         /// <summary>
79         /// Creates a new BootstrapperBuilder.
80         /// </summary>
81         /// <param name="visualStudioVersion">The version of Visual Studio that is used to build this bootstrapper.</param>
BootstrapperBuilder(string visualStudioVersion)82         public BootstrapperBuilder(string visualStudioVersion)
83         {
84             _path = Util.GetDefaultPath(visualStudioVersion);
85         }
86 
87         #region IBootstrapperBuilder Members
88 
89         /// <summary>
90         /// Specifies the location of the required bootstrapper files.
91         /// </summary>
92         /// <value>Path to bootstrapper files.</value>
93         public string Path
94         {
95             get { return _path; }
96             set
97             {
98                 if (!_fInitialized || string.Compare(_path, value, StringComparison.OrdinalIgnoreCase) != 0)
99                 {
100                     _path = value;
101                     Refresh();
102                 }
103             }
104         }
105 
106         /// <summary>
107         /// Returns all products available at the current bootstrapper Path
108         /// </summary>
109         public ProductCollection Products
110         {
111             get
112             {
113                 if (!_fInitialized)
114                     Refresh();
115 
116                 return _products;
117             }
118         }
119 
120         /// <summary>
121         /// Generates a bootstrapper based on the specified settings.
122         /// </summary>
123         /// <param name="settings">The properties used to build this bootstrapper.</param>
124         /// <returns>The results of the bootstrapper generation</returns>
Build(BuildSettings settings)125         public BuildResults Build(BuildSettings settings)
126         {
127             _results = new BuildResults();
128             try
129             {
130                 if (settings.ApplicationFile == null && (settings.ProductBuilders == null || settings.ProductBuilders.Count == 0))
131                 {
132                     _results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Error, "GenerateBootstrapper.InvalidInput"));
133                     return _results;
134                 }
135 
136                 if (String.IsNullOrEmpty(settings.OutputPath))
137                 {
138                     _results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Error, "GenerateBootstrapper.NoOutputPath"));
139                     return _results;
140                 }
141 
142                 if (!_fInitialized)
143                     Refresh();
144 
145                 if (String.IsNullOrEmpty(settings.Culture))
146                     settings.Culture = MapLCIDToCultureName(settings.LCID);
147                 if (String.IsNullOrEmpty(settings.FallbackCulture))
148                     settings.FallbackCulture = MapLCIDToCultureName(settings.FallbackLCID);
149 
150                 if (String.IsNullOrEmpty(settings.Culture) || settings.Culture == "*")
151                 {
152                     settings.Culture = settings.FallbackCulture;
153                 }
154 
155                 AddBuiltProducts(settings);
156 
157                 ArrayList componentFilesCopied = new ArrayList();
158 
159                 // Copy setup.bin to the output directory
160                 string strOutputExe = System.IO.Path.Combine(settings.OutputPath, SETUP_EXE);
161                 if (!CopySetupToOutputDirectory(settings, strOutputExe))
162                 {
163                     // Appropriate messages should have been stuffed into the results already
164                     return _results;
165                 }
166 
167                 ResourceUpdater resourceUpdater = new ResourceUpdater();
168 
169                 // Build up the String table for setup.exe
170                 if (!BuildResources(settings, resourceUpdater))
171                 {
172                     // Appropriate messages should have been stuffed into the results already
173                     return _results;
174                 }
175 
176                 AddStringResourceForUrl(resourceUpdater, "BASEURL", settings.ApplicationUrl, "ApplicationUrl");
177                 AddStringResourceForUrl(resourceUpdater, "COMPONENTSURL", settings.ComponentsUrl, "ComponentsUrl");
178                 AddStringResourceForUrl(resourceUpdater, "SUPPORTURL", settings.SupportUrl, "SupportUrl");
179                 if (settings.ComponentsLocation == ComponentsLocation.HomeSite)
180                 {
181                     resourceUpdater.AddStringResource(40, "HOMESITE", true.ToString());
182                 }
183 
184                 XmlElement configElement = _document.CreateElement("Configuration");
185                 XmlElement applicationElement = CreateApplicationElement(configElement, settings);
186                 if (applicationElement != null)
187                 {
188                     configElement.AppendChild(applicationElement);
189                 }
190 
191                 // Key: File hash, Value: A DictionaryEntry whose Key is "EULAx" and value is a
192                 // fully qualified path to a eula. It can be any eula that matches the hash.
193                 Hashtable eulas = new Hashtable();
194 
195                 // Copy package files, add each Package config info to the config file
196                 if (!BuildPackages(settings, configElement, resourceUpdater, componentFilesCopied, eulas))
197                     return _results;
198 
199                 // Transform the configuration xml into something the bootstrapper will understand
200                 DumpXmlToFile(configElement, "bootstrapper.cfg.xml");
201                 string config = XmlToConfigurationFile(configElement);
202                 resourceUpdater.AddStringResource(41, "SETUPCFG", config);
203                 DumpStringToFile(config, "bootstrapper.cfg", false);
204 
205                 // Put eulas in the resource stream
206                 foreach (object obj in eulas.Values)
207                 {
208                     DictionaryEntry de = (DictionaryEntry)obj;
209                     string data;
210                     FileInfo fi = new System.IO.FileInfo(de.Value.ToString());
211                     using (FileStream fs = fi.OpenRead())
212                     {
213                         data = new StreamReader(fs).ReadToEnd();
214                     }
215 
216                     resourceUpdater.AddStringResource(44, de.Key.ToString(), data);
217                 }
218 
219                 resourceUpdater.AddStringResource(44, "COUNT", eulas.Count.ToString(CultureInfo.InvariantCulture));
220                 if (!resourceUpdater.UpdateResources(strOutputExe, _results))
221                 {
222                     return _results;
223                 }
224 
225                 _results.SetKeyFile(strOutputExe);
226                 string[] componentFiles = new string[componentFilesCopied.Count];
227                 componentFilesCopied.CopyTo(componentFiles);
228                 _results.AddComponentFiles(componentFiles);
229                 _results.BuildSucceeded();
230             }
231             catch (Exception ex)
232             {
233                 _results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Error, "GenerateBootstrapper.General", ex.Message));
234             }
235             return _results;
236         }
237 
Merge(Dictionary<string, Product> output, Dictionary<string, Product> input)238         private void Merge(Dictionary<string, Product> output, Dictionary<string, Product> input)
239         {
240             foreach (Product product in input.Values)
241             {
242                 AddProduct(output, product);
243             }
244         }
245 
AddProduct(Dictionary<string, Product> output, Product product)246         private void AddProduct(Dictionary<string, Product> output, Product product)
247         {
248             if (!output.ContainsKey(product.ProductCode.ToLowerInvariant()))
249                 output.Add(product.ProductCode.ToLowerInvariant(), product);
250         }
251 
AddBuiltProducts(BuildSettings settings)252         private void AddBuiltProducts(BuildSettings settings)
253         {
254             Dictionary<string, ProductBuilder> builtProducts = new Dictionary<string, ProductBuilder>();
255             Dictionary<string, Product> productsAndIncludes = new Dictionary<string, Product>();
256 
257             if (_loopDependenciesWarnings != null && _loopDependenciesWarnings.Messages != null)
258             {
259                 foreach (BuildMessage message in _loopDependenciesWarnings.Messages)
260                 {
261                     _results.AddMessage(message);
262                 }
263             }
264 
265             foreach (ProductBuilder builder in settings.ProductBuilders)
266             {
267                 builtProducts.Add(builder.Product.ProductCode.ToLowerInvariant(), builder);
268                 Merge(productsAndIncludes, GetIncludedProducts(builder.Product));
269                 AddProduct(productsAndIncludes, builder.Product);
270             }
271 
272             foreach (ProductBuilder builder in settings.ProductBuilders)
273             {
274                 Dictionary<string, Product> includes = GetIncludedProducts(builder.Product);
275                 foreach (Product p in includes.Values)
276                 {
277                     if (builtProducts.ContainsKey(p.ProductCode.ToLowerInvariant()))
278                     {
279                         _results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Warning, "GenerateBootstrapper.IncludedProductIncluded", builder.Name, p.Name));
280                     }
281                 }
282 
283                 foreach (List<Product> productDependency in builder.Product.Dependencies)
284                 {
285                     bool foundDependency = false;
286                     foreach (Product p in productDependency)
287                     {
288                         if (productsAndIncludes.ContainsKey(p.ProductCode.ToLowerInvariant()))
289                         {
290                             foundDependency = true;
291                             break;
292                         }
293                     }
294 
295                     if (!foundDependency)
296                     {
297                         if (productDependency.Count == 1)
298                         {
299                             _results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Warning, "GenerateBootstrapper.MissingDependency", productDependency[0].Name, builder.Name));
300                         }
301                         else
302                         {
303                             StringBuilder missingProductCodes = new StringBuilder();
304                             foreach (Product product in productDependency)
305                             {
306                                 missingProductCodes.Append(product.Name);
307                                 missingProductCodes.Append(", ");
308                             }
309 
310                             string productCodes = missingProductCodes.ToString();
311                             productCodes = productCodes.Substring(0, productCodes.Length - 2);
312                             _results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Warning, "GenerateBootstrapper.MissingDependencyMultiple", productCodes, builder.Name));
313                         }
314                     }
315                 }
316 
317                 foreach (ArrayList missingDependecies in builder.Product.MissingDependencies)
318                 {
319                     if (missingDependecies.Count == 1)
320                     {
321                         _results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Warning, "GenerateBootstrapper.DependencyNotFound", builder.Name, missingDependecies[0]));
322                     }
323                     else
324                     {
325                         StringBuilder missingProductCodes = new StringBuilder();
326                         foreach (string productCode in missingDependecies)
327                         {
328                             missingProductCodes.Append(productCode);
329                             missingProductCodes.Append(", ");
330                         }
331 
332                         string productCodes = missingProductCodes.ToString();
333                         productCodes = productCodes.Substring(0, productCodes.Length - 2);
334                         _results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Warning, "GenerateBootstrapper.MultipleDependeciesNotFound", builder.Name, productCodes));
335                     }
336                 }
337             }
338         }
339 
CopySetupToOutputDirectory(BuildSettings settings, string strOutputExe)340         private bool CopySetupToOutputDirectory(BuildSettings settings, string strOutputExe)
341         {
342             string bootstrapperPath = BootstrapperPath;
343             string setupSourceFile = System.IO.Path.Combine(bootstrapperPath, SETUP_BIN);
344 
345             if (!File.Exists(setupSourceFile))
346             {
347                 _results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Error, "GenerateBootstrapper.MissingSetupBin", SETUP_BIN, bootstrapperPath));
348                 return false;
349             }
350 
351             try
352             {
353                 EnsureFolderExists(settings.OutputPath);
354                 File.Copy(setupSourceFile, strOutputExe, true);
355                 ClearReadOnlyAttribute(strOutputExe);
356             }
357             catch (IOException ex)
358             {
359                 _results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Error, "GenerateBootstrapper.CopyError", setupSourceFile, strOutputExe, ex.Message));
360                 return false;
361             }
362             catch (UnauthorizedAccessException ex)
363             {
364                 _results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Error, "GenerateBootstrapper.CopyError", setupSourceFile, strOutputExe, ex.Message));
365                 return false;
366             }
367             catch (ArgumentException ex)
368             {
369                 _results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Error, "GenerateBootstrapper.CopyError", setupSourceFile, strOutputExe, ex.Message));
370                 return false;
371             }
372             catch (NotSupportedException ex)
373             {
374                 _results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Error, "GenerateBootstrapper.CopyError", setupSourceFile, strOutputExe, ex.Message));
375                 return false;
376             }
377 
378             return true;
379         }
380 
AddStringResourceForUrl(ResourceUpdater resourceUpdater, string name, string url, string nameToUseInLog)381         private void AddStringResourceForUrl(ResourceUpdater resourceUpdater, string name, string url, string nameToUseInLog)
382         {
383             if (!String.IsNullOrEmpty(url))
384             {
385                 resourceUpdater.AddStringResource(40, name, url);
386                 if (!Util.IsWebUrl(url) && !Util.IsUncPath(url))
387                 {
388                     _results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Warning, "GenerateBootstrapper.InvalidUrl", nameToUseInLog, url));
389                 }
390             }
391         }
392 
393         #endregion
394 
395         /// <summary>
396         /// Returns the directories bootstrapper component files would be copied to when built given the specified settings
397         /// </summary>
398         /// <param name="productCodes">The productCodes of the selected components</param>
399         /// <param name="culture">The culture used to build the bootstrapper</param>
400         /// <param name="fallbackCulture">The fallback culture used to build the bootstrapper</param>
401         /// <param name="componentsLocation">How the bootstrapper would package the selected components</param>
402         /// <returns></returns>
GetOutputFolders(string[] productCodes, string culture, string fallbackCulture, ComponentsLocation componentsLocation)403         public string[] GetOutputFolders(string[] productCodes, string culture, string fallbackCulture, ComponentsLocation componentsLocation)
404         {
405             if (!_fInitialized)
406             {
407                 Refresh();
408             }
409 
410             Hashtable folders = new Hashtable();
411             BuildSettings settings = new BuildSettings();
412             string invariantPath = PackagePath.ToLowerInvariant();
413             invariantPath = Util.AddTrailingChar(invariantPath, System.IO.Path.DirectorySeparatorChar);
414             settings.CopyComponents = false;
415             settings.Culture = culture;
416             settings.FallbackCulture = fallbackCulture;
417             settings.ComponentsLocation = componentsLocation;
418             if (String.IsNullOrEmpty(settings.Culture) || settings.Culture == "*")
419             {
420                 settings.Culture = settings.FallbackCulture;
421             }
422 
423             foreach (string productCode in productCodes)
424             {
425                 Product product = Products.Product(productCode);
426                 if (product == null)
427                     continue;
428                 settings.ProductBuilders.Add(product.ProductBuilder);
429             }
430             ArrayList files = new ArrayList();
431 
432             BuildPackages(settings, null, null, files, null);
433 
434             foreach (string file in files)
435             {
436                 string folder = System.IO.Path.GetDirectoryName(file);
437                 if (folder.Substring(0, invariantPath.Length).ToLowerInvariant().CompareTo(invariantPath) == 0)
438                 {
439                     string relPath = folder.Substring(invariantPath.Length).ToLowerInvariant();
440                     if (!folders.Contains(relPath))
441                         folders.Add(relPath, relPath);
442                 }
443             }
444 
445             ArrayList list = new ArrayList(folders.Values);
446             string[] a = new string[list.Count];
447             list.CopyTo(a, 0);
448             return a;
449         }
450 
ContainsCulture(string culture)451         internal bool ContainsCulture(string culture)
452         {
453             if (!_fInitialized)
454                 Refresh();
455             return _cultures.Contains(culture);
456         }
457 
458         internal string[] Cultures
459         {
460             get
461             {
462                 if (!_fInitialized)
463                     Refresh();
464 
465                 ArrayList list = new ArrayList(_cultures.Values);
466                 list.Sort();
467                 string[] a = new string[list.Count];
468                 list.CopyTo(a, 0);
469                 return a;
470             }
471         }
472 
473         internal bool Validate
474         {
475             get { return _fValidate; }
476             set { _fValidate = value; }
477         }
478 
479         private string BootstrapperPath
480         {
481             get { return System.IO.Path.Combine(Path, ENGINE_PATH); }
482         }
483 
484         private string PackagePath
485         {
486             get { return System.IO.Path.Combine(Path, PACKAGE_PATH); }
487         }
488 
489         private string SchemaPath
490         {
491             get { return System.IO.Path.Combine(Path, SCHEMA_PATH); }
492         }
493 
Refresh()494         private void Refresh()
495         {
496             RefreshResources();
497             RefreshProducts();
498             _fInitialized = true;
499 
500             if (s_logging)
501             {
502                 StringBuilder productsOrder = new StringBuilder();
503                 foreach (Product p in Products)
504                 {
505                     productsOrder.Append(p.ProductCode + Environment.NewLine);
506                 }
507                 DumpStringToFile(productsOrder.ToString(), "BootstrapperInstallOrder.txt", false);
508             }
509         }
510 
RefreshResources()511         private void RefreshResources()
512         {
513             string startDirectory = System.IO.Path.Combine(BootstrapperPath, RESOURCES_PATH);
514             _cultures.Clear();
515 
516             if (Directory.Exists(startDirectory))
517             {
518                 foreach (string subDirectory in Directory.GetDirectories(startDirectory))
519                 {
520                     string resourceDirectory = System.IO.Path.Combine(startDirectory, subDirectory);
521                     string resourceFile = System.IO.Path.Combine(resourceDirectory, SETUP_RESOURCES_FILE);
522                     if (File.Exists(resourceFile))
523                     {
524                         XmlDocument resourceDoc = new XmlDocument();
525                         try
526                         {
527                             XmlReaderSettings xrs = new XmlReaderSettings();
528                             xrs.DtdProcessing = DtdProcessing.Ignore;
529                             using (XmlReader xr = XmlReader.Create(resourceFile, xrs))
530                             {
531                                 resourceDoc.Load(xr);
532                             }
533                         }
534                         catch (XmlException ex)
535                         {
536                             // UNDONE: Log exception due to bad resource file
537                             Debug.Fail(ex.Message);
538                             continue;
539                         }
540 
541                         XmlNode rootNode = resourceDoc.SelectSingleNode("Resources");
542                         if (rootNode != null)
543                         {
544                             XmlAttribute cultureAttribute = (XmlAttribute)rootNode.Attributes.GetNamedItem("Culture");
545                             if (cultureAttribute != null)
546                             {
547                                 XmlNode stringsNode = rootNode.SelectSingleNode("Strings");
548                                 if (stringsNode != null)
549                                 {
550                                     XmlNode stringNode = stringsNode.SelectSingleNode(string.Format(CultureInfo.InvariantCulture, "String[@Name='{0}']", cultureAttribute.Value));
551                                     if (stringNode != null)
552                                     {
553                                         string culture = stringNode.InnerText;
554 
555                                         XmlNode resourcesNode = rootNode.OwnerDocument.ImportNode(rootNode, true);
556                                         resourcesNode.Attributes.RemoveNamedItem("Culture");
557                                         XmlAttribute newAttribute = (XmlAttribute)rootNode.OwnerDocument.ImportNode(cultureAttribute, false);
558                                         newAttribute.Value = stringNode.InnerText;
559                                         resourcesNode.Attributes.Append(newAttribute);
560                                         if (!_cultures.Contains(culture.ToLowerInvariant()))
561                                             _cultures.Add(culture.ToLowerInvariant(), resourcesNode);
562                                         else
563                                             Debug.Fail("Already found resources for culture " + stringNode.InnerText);
564                                     }
565                                 }
566                             }
567                         }
568                     }
569                 }
570             }
571         }
572 
RefreshProducts()573         private void RefreshProducts()
574         {
575             _products.Clear();
576             _validationResults.Clear();
577             _document = new XmlDocument();
578             _xmlNamespaceManager = new XmlNamespaceManager(_document.NameTable);
579 
580             _xmlNamespaceManager.AddNamespace(BOOTSTRAPPER_PREFIX, BOOTSTRAPPER_NAMESPACE);
581 
582             XmlElement rootElement = _document.CreateElement("Products", BOOTSTRAPPER_NAMESPACE);
583             string packagePath = PackagePath;
584 
585             if (Directory.Exists(packagePath))
586             {
587                 foreach (string strSubDirectory in Directory.GetDirectories(packagePath))
588                 {
589                     int nStartIndex = packagePath.Length;
590                     if ((strSubDirectory.ToCharArray())[nStartIndex] == System.IO.Path.DirectorySeparatorChar)
591                     {
592                         nStartIndex = nStartIndex + 1;
593                     }
594                     ExploreDirectory(strSubDirectory.Substring(nStartIndex), rootElement);
595                 }
596             }
597 
598             _document.AppendChild(rootElement);
599 
600             Hashtable availableProducts = new Hashtable();
601             // A second copy of all the project which will get destroyed during the generation of the build order
602             Hashtable buildQueue = new Hashtable();
603 
604             XmlNodeList productsFound = rootElement.SelectNodes(BOOTSTRAPPER_PREFIX + ":Product", _xmlNamespaceManager);
605             foreach (XmlNode productNode in productsFound)
606             {
607                 Product p = CreateProduct(productNode);
608                 if (p != null)
609                 {
610                     availableProducts.Add(p.ProductCode, p);
611                     buildQueue.Add(p.ProductCode, CreateProduct(productNode));
612                 }
613             }
614 
615             // Set the product and included products for each product
616             foreach (Product p in availableProducts.Values)
617             {
618                 AddDependencies(p, availableProducts);
619                 AddIncludes(p, availableProducts);
620             }
621 
622             // We need only the dependencies to generate the bulid order
623             foreach (Product p in buildQueue.Values)
624             {
625                 AddDependencies(p, buildQueue);
626             }
627 
628             // Scan the products and their dependencies to calculate install order
629             OrderProducts(availableProducts, buildQueue);
630         }
631 
AddDependencies(Product p, Hashtable availableProducts)632         private void AddDependencies(Product p, Hashtable availableProducts)
633         {
634             foreach (string relatedProductCode in SelectRelatedProducts(p, "DependsOnProduct"))
635             {
636                 if (availableProducts.Contains(relatedProductCode))
637                 {
638                     p.AddDependentProduct((Product)availableProducts[relatedProductCode]);
639                 }
640                 else
641                 {
642                     ArrayList missingDependencies = new ArrayList();
643                     missingDependencies.Add(relatedProductCode);
644                     p.AddMissingDependency(missingDependencies);
645                 }
646             }
647 
648             foreach (XmlNode eitherProductNode in SelectEitherProducts(p))
649             {
650                 List<Product> foundDependencies = new List<Product>();
651                 ArrayList allDependencies = new ArrayList();
652 
653                 foreach (XmlNode relatedProductNode in eitherProductNode.SelectNodes(String.Format(CultureInfo.InvariantCulture, "{0}:DependsOnProduct", BOOTSTRAPPER_PREFIX), _xmlNamespaceManager))
654                 {
655                     XmlAttribute relatedProductAttribute = (XmlAttribute)(relatedProductNode.Attributes.GetNamedItem("Code"));
656                     if (relatedProductAttribute != null)
657                     {
658                         string dependency = relatedProductAttribute.Value;
659                         if (availableProducts.Contains(dependency))
660                         {
661                             foundDependencies.Add((Product)availableProducts[dependency]);
662                         }
663                         allDependencies.Add(dependency);
664                     }
665                 }
666 
667                 if (foundDependencies.Count > 0)
668                 {
669                     if (!p.ContainsDependencies(foundDependencies))
670                     {
671                         p.Dependencies.Add(foundDependencies);
672                     }
673                 }
674                 else if (allDependencies.Count > 0)
675                 {
676                     p.AddMissingDependency(allDependencies);
677                 }
678             }
679         }
680 
AddIncludes(Product p, Hashtable availableProducts)681         private void AddIncludes(Product p, Hashtable availableProducts)
682         {
683             foreach (string relatedProductCode in SelectRelatedProducts(p, "IncludesProduct"))
684             {
685                 if (availableProducts.Contains(relatedProductCode))
686                 {
687                     p.Includes.Add((Product)availableProducts[relatedProductCode]);
688                 }
689             }
690         }
691 
SelectRelatedProducts(Product p, string nodeName)692         private string[] SelectRelatedProducts(Product p, string nodeName)
693         {
694             ArrayList list = new ArrayList();
695 
696             XmlNodeList relatedProducts = p.Node.SelectNodes(string.Format(CultureInfo.InvariantCulture, "{0}:Package/{1}:RelatedProducts/{2}:{3}", BOOTSTRAPPER_PREFIX, BOOTSTRAPPER_PREFIX, BOOTSTRAPPER_PREFIX, nodeName), _xmlNamespaceManager);
697             if (relatedProducts != null)
698             {
699                 foreach (XmlNode relatedProduct in relatedProducts)
700                 {
701                     XmlAttribute relatedProductAttribute = (XmlAttribute)(relatedProduct.Attributes.GetNamedItem("Code"));
702                     if (relatedProductAttribute != null)
703                     {
704                         list.Add(relatedProductAttribute.Value);
705                     }
706                 }
707             }
708 
709             string[] a = new string[list.Count];
710             list.CopyTo(a, 0);
711             return a;
712         }
713 
SelectEitherProducts(Product p)714         private XmlNodeList SelectEitherProducts(Product p)
715         {
716             XmlNodeList eitherProducts = p.Node.SelectNodes(string.Format(CultureInfo.InvariantCulture, "{0}:Package/{1}:RelatedProducts/{2}:EitherProducts", BOOTSTRAPPER_PREFIX, BOOTSTRAPPER_PREFIX, BOOTSTRAPPER_PREFIX), _xmlNamespaceManager);
717             return eitherProducts;
718         }
719 
OrderProducts(Hashtable availableProducts, Hashtable buildQueue)720         private void OrderProducts(Hashtable availableProducts, Hashtable buildQueue)
721         {
722             bool loopDetected = false;
723             _loopDependenciesWarnings = new BuildResults();
724             StringBuilder productsInLoop = new StringBuilder();
725             while (buildQueue.Count > 0)
726             {
727                 List<string> productsToRemove = new List<string>();
728                 foreach (Product p in buildQueue.Values)
729                 {
730                     if (p.Dependencies.Count == 0)
731                     {
732                         _products.Add((Product)availableProducts[p.ProductCode]);
733                         RemoveDependency(buildQueue, p);
734                         productsToRemove.Add(p.ProductCode);
735                     }
736                 }
737 
738                 foreach (string productCode in productsToRemove)
739                 {
740                     buildQueue.Remove(productCode);
741                     if (loopDetected)
742                     {
743                         productsInLoop.Append(productCode);
744                         productsInLoop.Append(", ");
745                     }
746                 }
747 
748                 // If we could not remove any products and there are still products in the queue
749                 // there must be a loop in it. We'll break the loop by removing the dependencies
750                 // of the first project in the queue;
751                 if (buildQueue.Count > 0 && productsToRemove.Count == 0)
752                 {
753                     IDictionaryEnumerator enumerator = buildQueue.GetEnumerator();
754                     enumerator.MoveNext();
755                     ((Product)enumerator.Value).Dependencies.RemoveAll(m => true);
756                     loopDetected = true;
757                 }
758 
759                 // If we've been in a loop and there are no more products left
760                 // or no more products can be installed, we have completely walked that loop
761                 // and now is a good time to show the warning message for the loop
762                 if (productsInLoop.Length > 0 && (buildQueue.Count == 0 || productsToRemove.Count == 0))
763                 {
764                     productsInLoop.Remove(productsInLoop.Length - 2, 2);
765                     _loopDependenciesWarnings.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Warning, "GenerateBootstrapper.CircularDependency", productsInLoop.ToString()));
766                     productsInLoop.Remove(0, productsInLoop.Length);
767                 }
768             }
769         }
770 
RemoveDependency(Hashtable availableProducts, Product product)771         private void RemoveDependency(Hashtable availableProducts, Product product)
772         {
773             foreach (Product p in availableProducts.Values)
774             {
775                 foreach (List<Product> dependency in p.Dependencies)
776                 {
777                     dependency.RemoveAll(m => m == product);
778                 }
779                 p.Dependencies.RemoveAll(m => m.Count == 0);
780             }
781         }
782 
LoadAndValidateXmlDocument(string filePath, bool validateFilePresent, string schemaPath, string schemaNamespace, XmlValidationResults results)783         private XmlDocument LoadAndValidateXmlDocument(string filePath, bool validateFilePresent, string schemaPath, string schemaNamespace, XmlValidationResults results)
784         {
785             XmlDocument xmlDocument = null;
786 
787             Debug.Assert(filePath != null, "null filePath?");
788             Debug.Assert(schemaPath != null, "null schemaPath?");
789             Debug.Assert(schemaNamespace != null, "null schemaNamespace?");
790 
791             if ((filePath != null) && (schemaPath != null) && (schemaNamespace != null))
792             {
793                 // set up our validation logic by detecting the trace-switch enabled and whether or
794                 //   not our files exist.
795                 bool validate = true;
796                 bool fileExists = File.Exists(filePath);
797                 bool schemaExists = File.Exists(schemaPath);
798 
799                 // if we're being asked to validate but we can't find the schema file, then
800                 //   output something useful to tell user that we can't find the schema.
801                 if (!schemaExists)
802                 {
803                     Debug.Fail("Could not locate schema '" + schemaPath + "', so no validation of '" + filePath + "' is possible.");
804                     validate = false;
805                 }
806 
807                 // if we're being asked to validate but we can't find the data file, then
808                 //   output something useful to tell user that we can't find the file and that we
809                 //   can't do anything useful.
810                 if (validate && (!fileExists) && validateFilePresent)
811                 {
812                     Debug.Fail("Could not locate data file '" + filePath + "'.");
813                     validate = false;
814                 }
815 
816                 if (fileExists)
817                 {
818                     XmlTextReader xmlTextReader = new XmlTextReader(filePath);
819                     xmlTextReader.DtdProcessing = DtdProcessing.Ignore;
820 
821                     XmlReader xmlReader = xmlTextReader;
822 
823                     if (validate)
824                     {
825 #pragma warning disable 618 // Using XmlValidatingReader. TODO: We need to switch to using XmlReader.Create() with validation.
826                         var validatingReader = new XmlValidatingReader(xmlReader);
827 #pragma warning restore 618
828                         XmlReaderSettings xrSettings = new XmlReaderSettings();
829                         xrSettings.DtdProcessing = DtdProcessing.Ignore;
830                         using (XmlReader xr = XmlReader.Create(schemaPath, xrSettings))
831                         {
832                             try
833                             {
834                                 // first, add our schema to the validating reader's collection of schemas
835                                 var xmlSchema = validatingReader.Schemas.Add(null, xr);
836 
837                                 // if our schema namespace gets out of sync,
838                                 //   then all of our calls to SelectNodes and SelectSingleNode will fail
839                                 Debug.Assert((xmlSchema != null) &&
840                                     string.Equals(schemaNamespace, xmlSchema.TargetNamespace, StringComparison.Ordinal),
841                                     System.IO.Path.GetFileName(schemaPath) + " and BootstrapperBuilder.vb have mismatched namespaces, so the BootstrapperBuilder will fail to work.");
842 
843                                 // if we're supposed to be validating, then hook up our handler
844                                 validatingReader.ValidationEventHandler += results.SchemaValidationEventHandler;
845 
846                                 // switch readers so the doc does the actual read over the validating
847                                 //   reader so we get validation events as we load the document
848                                 xmlReader = validatingReader;
849                             }
850                             catch (XmlException ex)
851                             {
852                                 Debug.Fail("Failed to load schema '" + schemaPath + "' due to the following exception:\r\n" + ex.Message);
853                                 validate = false;
854                             }
855                             catch (System.Xml.Schema.XmlSchemaException ex)
856                             {
857                                 Debug.Fail("Failed to load schema '" + schemaPath + "' due to the following exception:\r\n" + ex.Message);
858                                 validate = false;
859                             }
860                         }
861                     }
862 
863                     try
864                     {
865                         Debug.Assert(_document != null, "our document should have been created by now!");
866                         xmlDocument = new XmlDocument(_document.NameTable);
867                         xmlDocument.Load(xmlReader);
868                     }
869                     catch (XmlException ex)
870                     {
871                         Debug.Fail("Failed to load document '" + filePath + "' due to the following exception:\r\n" + ex.Message);
872                         return null;
873                     }
874                     catch (System.Xml.Schema.XmlSchemaException ex)
875                     {
876                         Debug.Fail("Failed to load document '" + filePath + "' due to the following exception:\r\n" + ex.Message);
877                         return null;
878                     }
879                     finally
880                     {
881                         xmlReader.Close();
882                     }
883 
884                     // Note that the xml document's default namespace must match the schema namespace
885                     //   or none of our SelectNodes/SelectSingleNode calls will succeed
886                     Debug.Assert(xmlDocument.DocumentElement != null &&
887                     string.Equals(xmlDocument.DocumentElement.NamespaceURI, schemaNamespace, StringComparison.Ordinal),
888                         "'" + xmlDocument.DocumentElement.NamespaceURI + "' is not '" + schemaNamespace + "'...");
889 
890                     if ((xmlDocument.DocumentElement == null) ||
891                        (!string.Equals(xmlDocument.DocumentElement.NamespaceURI, schemaNamespace, StringComparison.Ordinal)))
892                     {
893                     }
894                 }
895             }
896 
897             return xmlDocument;
898         }
899 
ExploreDirectory(string strSubDirectory, XmlElement rootElement)900         private void ExploreDirectory(string strSubDirectory, XmlElement rootElement)
901         {
902             try
903             {
904                 string packagePath = PackagePath;
905                 string strSubDirectoryFullPath = System.IO.Path.Combine(packagePath, strSubDirectory);
906 
907                 // figure out our product file paths based on the directory full path
908                 string strBaseManifestFilename = System.IO.Path.Combine(strSubDirectoryFullPath, ROOT_MANIFEST_FILE);
909                 string strBaseManifestSchemaFileName = System.IO.Path.Combine(SchemaPath, MANIFEST_FILE_SCHEMA);
910 
911                 ProductValidationResults productValidationResults = new ProductValidationResults(strBaseManifestFilename);
912 
913                 // open the XmlDocument for this product.xml
914                 XmlDocument productDoc = LoadAndValidateXmlDocument(strBaseManifestFilename, false, strBaseManifestSchemaFileName, BOOTSTRAPPER_NAMESPACE, productValidationResults);
915                 if (productDoc != null)
916                 {
917                     bool packageAdded = false;
918 
919                     XmlNode baseNode = productDoc.SelectSingleNode(BOOTSTRAPPER_PREFIX + ":Product", _xmlNamespaceManager);
920                     if (baseNode != null)
921                     {
922                         // Get the ProductCode attribute for this product
923                         XmlAttribute productCodeAttribute = (XmlAttribute)(baseNode.Attributes.GetNamedItem("ProductCode"));
924                         if (productCodeAttribute != null)
925                         {
926                             // now add it to our full document if it's not already present
927                             XmlNode productNode = rootElement.SelectSingleNode(BOOTSTRAPPER_PREFIX + ":Product[@ProductCode='" + productCodeAttribute.Value + "']", _xmlNamespaceManager);
928                             if (productNode == null)
929                             {
930                                 productNode = CreateProductNode(baseNode);
931                             }
932                             else
933                             {
934                                 productValidationResults = (ProductValidationResults)_validationResults[productCodeAttribute];
935                             }
936 
937                             // Fix-up the <PackageFiles> of the base node to include the SourcePath and TargetPath
938                             XmlNode packageFilesNode = baseNode.SelectSingleNode(BOOTSTRAPPER_PREFIX + ":PackageFiles", _xmlNamespaceManager);
939                             XmlNode checksNode = baseNode.SelectSingleNode(BOOTSTRAPPER_PREFIX + ":InstallChecks", _xmlNamespaceManager);
940                             XmlNode commandsNode = baseNode.SelectSingleNode(BOOTSTRAPPER_PREFIX + ":Commands", _xmlNamespaceManager);
941 
942                             // if there was a packageFiles node, then add it in to our full document with the rest
943                             if (packageFilesNode != null)
944                             {
945                                 UpdatePackageFileNodes(packageFilesNode, System.IO.Path.Combine(packagePath, strSubDirectory), strSubDirectory);
946 
947                                 ReplacePackageFileAttributes(checksNode, "PackageFile", packageFilesNode, "PackageFile", "OldName", "Name");
948                                 ReplacePackageFileAttributes(commandsNode, "PackageFile", packageFilesNode, "PackageFile", "OldName", "Name");
949                                 ReplacePackageFileAttributes(baseNode, EULA_ATTRIBUTE, packageFilesNode, "PackageFile", "OldName", "SourcePath");
950                             }
951 
952                             foreach (string strLanguageDirectory in Directory.GetDirectories(strSubDirectoryFullPath))
953                             {
954                                 // The base node would get destroyed as we build-up this new node.
955                                 // Thus, we want to use a copy of the baseNode
956                                 XmlElement baseElement = (XmlElement)(_document.ImportNode(baseNode, true));
957 
958                                 string strLangManifestFilename = System.IO.Path.Combine(strLanguageDirectory, CHILD_MANIFEST_FILE);
959                                 string strLangManifestSchemaFileName = System.IO.Path.Combine(SchemaPath, MANIFEST_FILE_SCHEMA);
960 
961                                 if (File.Exists(strLangManifestFilename))
962                                 {
963                                     // Load Package.xml
964                                     XmlValidationResults packageValidationResults = new XmlValidationResults(strLangManifestFilename);
965                                     XmlDocument langDoc = LoadAndValidateXmlDocument(strLangManifestFilename, false, strLangManifestSchemaFileName, BOOTSTRAPPER_NAMESPACE, packageValidationResults);
966 
967                                     Debug.Assert(langDoc != null, "we couldn't load package.xml in '" + strLangManifestFilename + "'...?");
968                                     if (langDoc == null)
969                                         continue;
970 
971                                     XmlNode langNode = langDoc.SelectSingleNode(BOOTSTRAPPER_PREFIX + ":Package", _xmlNamespaceManager);
972                                     Debug.Assert(langNode != null, string.Format(CultureInfo.CurrentCulture, "Unable to find a package node in {0}", strLangManifestFilename));
973                                     if (langNode != null)
974                                     {
975                                         XmlElement langElement = (XmlElement)(_document.ImportNode(langNode, true));
976                                         XmlElement mergeElement = _document.CreateElement("Package", BOOTSTRAPPER_NAMESPACE);
977 
978                                         // Update the "PackageFiles" section to reflect this language subdirectory
979                                         XmlNode packageFilesNodePackage = langElement.SelectSingleNode(BOOTSTRAPPER_PREFIX + ":PackageFiles", _xmlNamespaceManager);
980                                         checksNode = langElement.SelectSingleNode(BOOTSTRAPPER_PREFIX + ":InstallChecks", _xmlNamespaceManager);
981                                         commandsNode = langElement.SelectSingleNode(BOOTSTRAPPER_PREFIX + ":Commands", _xmlNamespaceManager);
982 
983                                         if (packageFilesNodePackage != null)
984                                         {
985                                             int nStartIndex = packagePath.Length;
986 
987                                             if ((strLanguageDirectory.ToCharArray())[nStartIndex] == System.IO.Path.DirectorySeparatorChar)
988                                                 nStartIndex++;
989                                             UpdatePackageFileNodes(packageFilesNodePackage, strLanguageDirectory, strSubDirectory);
990 
991                                             ReplacePackageFileAttributes(checksNode, "PackageFile", packageFilesNodePackage, "PackageFile", "OldName", "Name");
992                                             ReplacePackageFileAttributes(commandsNode, "PackageFile", packageFilesNodePackage, "PackageFile", "OldName", "Name");
993                                             ReplacePackageFileAttributes(langElement, EULA_ATTRIBUTE, packageFilesNodePackage, "PackageFile", "OldName", "SourcePath");
994                                         }
995 
996                                         if (packageFilesNode != null)
997                                         {
998                                             ReplacePackageFileAttributes(checksNode, "PackageFile", packageFilesNode, "PackageFile", "OldName", "Name");
999                                             ReplacePackageFileAttributes(commandsNode, "PackageFile", packageFilesNode, "PackageFile", "OldName", "Name");
1000                                             ReplacePackageFileAttributes(langElement, EULA_ATTRIBUTE, packageFilesNode, "PackageFile", "OldName", "SourcePath");
1001                                         }
1002 
1003                                         // in general, we prefer the attributes of the language document over the
1004                                         //  attributes of the base document.  Copy attributes from the lang to the merged,
1005                                         //  and then merge all unique elements into merge
1006                                         foreach (XmlAttribute attribute in langElement.Attributes)
1007                                         {
1008                                             mergeElement.Attributes.Append((XmlAttribute)(mergeElement.OwnerDocument.ImportNode(attribute, false)));
1009                                         }
1010 
1011                                         foreach (XmlAttribute attribute in baseElement.Attributes)
1012                                         {
1013                                             XmlAttribute convertedAttribute = (XmlAttribute)(mergeElement.OwnerDocument.ImportNode(attribute, false));
1014                                             MergeAttribute(mergeElement, convertedAttribute);
1015                                         }
1016 
1017                                         // And append all of the nodes
1018                                         //  There is a well-known set of nodes which may have inherit children
1019                                         //  When merging these nodes, there may be subnodes taken from both the lang element and the base element.
1020                                         //  There will never be multiple nodes with the same name in the same manifest
1021                                         //  The function which performs this action is CombineElements(...)
1022                                         CombineElements(langElement, baseElement, "Commands", "PackageFile", mergeElement);
1023                                         CombineElements(langElement, baseElement, "InstallChecks", "Property", mergeElement);
1024                                         CombineElements(langElement, baseElement, "PackageFiles", "Name", mergeElement);
1025                                         CombineElements(langElement, baseElement, "Schedules", "Name", mergeElement);
1026                                         CombineElements(langElement, baseElement, "Strings", "Name", mergeElement);
1027 
1028                                         ReplaceStrings(mergeElement);
1029                                         CorrectPackageFiles(mergeElement);
1030 
1031                                         AppendNode(baseElement, "RelatedProducts", mergeElement);
1032 
1033                                         // Create a unique identifier for this package
1034                                         XmlAttribute cultureAttribute = (XmlAttribute)mergeElement.Attributes.GetNamedItem("Culture");
1035                                         if (cultureAttribute != null && !String.IsNullOrEmpty(cultureAttribute.Value))
1036                                         {
1037                                             string packageCode = productCodeAttribute.Value + "." + cultureAttribute.Value;
1038                                             AddAttribute(mergeElement, "PackageCode", packageCode);
1039 
1040                                             if (productValidationResults != null && packageValidationResults != null)
1041                                             {
1042                                                 productValidationResults.AddPackageResults(cultureAttribute.Value, packageValidationResults);
1043                                             }
1044 
1045                                             // Only add this package if there is a culture apecified.
1046                                             productNode.AppendChild(mergeElement);
1047                                             packageAdded = true;
1048                                         }
1049                                     }
1050                                 }
1051                             }
1052                             if (packageAdded)
1053                             {
1054                                 rootElement.AppendChild(productNode);
1055                                 if (!_validationResults.Contains(productCodeAttribute.Value))
1056                                 {
1057                                     _validationResults.Add(productCodeAttribute.Value, productValidationResults);
1058                                 }
1059                                 else
1060                                 {
1061                                     Debug.WriteLine(String.Format(CultureInfo.CurrentCulture, "Validation results already added for Product Code '{0}'", productCodeAttribute));
1062                                 }
1063                             }
1064                         }
1065                     }
1066                 }
1067             }
1068             catch (XmlException ex)
1069             {
1070                 Debug.Fail(ex.Message);
1071             }
1072             catch (System.IO.IOException ex)
1073             {
1074                 Debug.Fail(ex.Message);
1075             }
1076             catch (ArgumentException ex)
1077             {
1078                 Debug.Fail(ex.Message);
1079             }
1080         }
1081 
CreateProduct(XmlNode node)1082         private Product CreateProduct(XmlNode node)
1083         {
1084             bool fPackageAdded = false;
1085             string productCode = ReadAttribute(node, "ProductCode");
1086             Product product = null;
1087             if (!String.IsNullOrEmpty(productCode))
1088             {
1089                 ProductValidationResults results = (ProductValidationResults)_validationResults[productCode];
1090 
1091                 XmlNode packageFilesNode = node.SelectSingleNode(BOOTSTRAPPER_PREFIX + ":Package/" + BOOTSTRAPPER_PREFIX + ":PackageFiles", _xmlNamespaceManager);
1092                 string copyAllPackageFiles = String.Empty;
1093 
1094                 if (packageFilesNode != null) copyAllPackageFiles = ReadAttribute(packageFilesNode, "CopyAllPackageFiles");
1095 
1096                 product = new Product(node, productCode, results, copyAllPackageFiles);
1097                 XmlNodeList packageNodeList = node.SelectNodes(BOOTSTRAPPER_PREFIX + ":Package", _xmlNamespaceManager);
1098 
1099                 foreach (XmlNode packageNode in packageNodeList)
1100                 {
1101                     Package package = CreatePackage(packageNode, product);
1102                     if (package != null)
1103                     {
1104                         product.AddPackage(package);
1105                         fPackageAdded = true;
1106                     }
1107                 }
1108             }
1109 
1110             if (fPackageAdded)
1111                 return product;
1112             return null;
1113         }
1114 
CreatePackage(XmlNode node, Product product)1115         private Package CreatePackage(XmlNode node, Product product)
1116         {
1117             string culture = ReadAttribute(node, "Culture");
1118 
1119             XmlValidationResults results = null;
1120             if (culture != null)
1121             {
1122                 results = product.GetPackageValidationResults(culture);
1123             }
1124             else
1125             {
1126                 return null;
1127             }
1128 
1129             return new Package(product, node, results, ReadAttribute(node, "Name"), ReadAttribute(node, "Culture"));
1130         }
1131 
ReplaceAttributes(XmlNode targetNode, string attributeName, string oldValue, string newValue)1132         private void ReplaceAttributes(XmlNode targetNode, string attributeName, string oldValue, string newValue)
1133         {
1134             if (targetNode != null)
1135             {
1136                 // select all nodes where the attributeName equals the oldValue
1137                 XmlNodeList nodeList = targetNode.SelectNodes(BOOTSTRAPPER_PREFIX + string.Format(CultureInfo.InvariantCulture, ":*[@{0}='{1}']", attributeName, oldValue), _xmlNamespaceManager);
1138 
1139                 foreach (XmlNode node in nodeList)
1140                 {
1141                     ReplaceAttribute(node, attributeName, newValue);
1142                 }
1143 
1144                 // replace attributes on the node itself
1145                 XmlAttribute attrib = targetNode.Attributes[attributeName];
1146                 if (attrib != null && attrib.Value == oldValue)
1147                     attrib.Value = newValue;
1148             }
1149         }
1150 
ReplaceAttribute(XmlNode targetNode, string attributeName, string attributeValue)1151         private void ReplaceAttribute(XmlNode targetNode, string attributeName, string attributeValue)
1152         {
1153             XmlAttribute attribute = targetNode.OwnerDocument.CreateAttribute(attributeName);
1154             attribute.Value = attributeValue;
1155             targetNode.Attributes.SetNamedItem(attribute);
1156         }
1157 
MergeAttribute(XmlNode targetNode, XmlAttribute attribute)1158         private void MergeAttribute(XmlNode targetNode, XmlAttribute attribute)
1159         {
1160             XmlAttribute targetAttribute = (XmlAttribute)(targetNode.Attributes.GetNamedItem(attribute.Name));
1161             if (targetAttribute == null)
1162             {
1163                 // This node does not already contain the attribute.  Add the parameter
1164                 targetNode.Attributes.Append(attribute);
1165             }
1166         }
1167 
UpdatePackageFileNodes(XmlNode packageFilesNode, string strSourcePath, string strTargetPath)1168         private void UpdatePackageFileNodes(XmlNode packageFilesNode, string strSourcePath, string strTargetPath)
1169         {
1170             XmlNodeList packageFileNodeList = packageFilesNode.SelectNodes(BOOTSTRAPPER_PREFIX + ":PackageFile", _xmlNamespaceManager);
1171 
1172             foreach (XmlNode packageFileNode in packageFileNodeList)
1173             {
1174                 XmlAttribute nameAttribute = (XmlAttribute)(packageFileNode.Attributes.GetNamedItem("Name"));
1175 
1176                 // the name attribute is required -- we can't do anything if it's not present
1177                 if (nameAttribute != null)
1178                 {
1179                     string relativePath = nameAttribute.Value;
1180 
1181                     XmlAttribute sourcePathAttribute = packageFilesNode.OwnerDocument.CreateAttribute("SourcePath");
1182                     string strSourceFile = System.IO.Path.Combine(strSourcePath, relativePath);
1183                     sourcePathAttribute.Value = strSourceFile;
1184 
1185                     XmlAttribute targetPathAttribute = packageFilesNode.OwnerDocument.CreateAttribute("TargetPath");
1186                     targetPathAttribute.Value = System.IO.Path.Combine(strTargetPath, relativePath);
1187 
1188                     string oldNameValue = nameAttribute.Value;
1189                     string newNameValue = System.IO.Path.Combine(strTargetPath, relativePath);
1190 
1191                     XmlAttribute oldNameAttribute = packageFilesNode.OwnerDocument.CreateAttribute("OldName");
1192                     oldNameAttribute.Value = oldNameValue;
1193 
1194                     ReplaceAttribute(packageFileNode, "Name", newNameValue);
1195                     MergeAttribute(packageFileNode, sourcePathAttribute);
1196                     MergeAttribute(packageFileNode, targetPathAttribute);
1197                     MergeAttribute(packageFileNode, oldNameAttribute);
1198                 }
1199             }
1200         }
1201 
AppendNode(XmlElement element, string nodeName, XmlElement mergeElement)1202         private void AppendNode(XmlElement element, string nodeName, XmlElement mergeElement)
1203         {
1204             XmlNode node = element.SelectSingleNode(BOOTSTRAPPER_PREFIX + ":" + nodeName, _xmlNamespaceManager);
1205             if (node != null)
1206             {
1207                 mergeElement.AppendChild(node);
1208             }
1209         }
1210 
CombineElements(XmlElement langElement, XmlElement baseElement, string strNodeName, string strSubNodeKey, XmlElement mergeElement)1211         private void CombineElements(XmlElement langElement, XmlElement baseElement, string strNodeName, string strSubNodeKey, XmlElement mergeElement)
1212         {
1213             XmlNode langNode = langElement.SelectSingleNode(BOOTSTRAPPER_PREFIX + ":" + strNodeName, _xmlNamespaceManager);
1214             XmlNode baseNode = baseElement.SelectSingleNode(BOOTSTRAPPER_PREFIX + ":" + strNodeName, _xmlNamespaceManager);
1215 
1216             // There are 4 basic cases to be dealt with:
1217             // Case #    1       2       3       4
1218             // base      null    null    present present
1219             // lang      null    present null    present
1220             // Result    null    lang    base    combine
1221             //
1222             // Cases 1 - 3 are pretty trivial.
1223             if (baseNode == null)
1224             {
1225                 if (langNode != null)
1226                 {
1227                     // Case 2
1228                     mergeElement.AppendChild(langNode);
1229                 }
1230                 // Case 1 is to do nothing
1231             }
1232             else
1233             {
1234                 if (langNode == null)
1235                 {
1236                     // Case 3
1237                     mergeElement.AppendChild(baseNode);
1238                 }
1239                 else
1240                 {
1241                     XmlNode mergeSubNode = _document.CreateElement(strNodeName, BOOTSTRAPPER_NAMESPACE);
1242                     XmlNode nextNode = baseNode.FirstChild;
1243 
1244                     // Begin case 4
1245                     // Go through every element in the base node
1246                     while (nextNode != null)
1247                     {
1248                         if (nextNode.NodeType == XmlNodeType.Element)
1249                         {
1250                             XmlAttribute keyAttribute = (XmlAttribute)(nextNode.Attributes.GetNamedItem(strSubNodeKey));
1251                             if (keyAttribute != null)
1252                             {
1253                                 XmlNode queryResultNode = QueryForSubNode(langNode, strSubNodeKey, keyAttribute.Value);
1254                                 // if there is no match in the lang node, use the current base node
1255                                 //  Otherwise use that node and remove it later
1256                                 if (queryResultNode == null)
1257                                 {
1258                                     mergeSubNode.AppendChild(mergeSubNode.OwnerDocument.ImportNode(nextNode, true));
1259                                 }
1260                                 else
1261                                 {
1262                                     mergeSubNode.AppendChild(mergeSubNode.OwnerDocument.ImportNode(queryResultNode, true));
1263                                     langNode.RemoveChild(queryResultNode);
1264                                 }
1265                             }
1266                             else
1267                             {
1268                                 Debug.Assert(false, "Specified key does not exist for node " + nextNode.InnerXml);
1269                             }
1270                         }
1271                         nextNode = nextNode.NextSibling;
1272                     }
1273 
1274                     // Append all remaining lang nodes
1275                     nextNode = langNode.FirstChild;
1276 
1277                     while (nextNode != null)
1278                     {
1279                         mergeSubNode.AppendChild(mergeSubNode.OwnerDocument.ImportNode(nextNode, true));
1280                         nextNode = nextNode.NextSibling;
1281                     }
1282 
1283                     // Copy all attributes.  The langnode again has priority
1284                     foreach (XmlAttribute attribute in langNode.Attributes)
1285                     {
1286                         AddAttribute(mergeSubNode, attribute.Name, attribute.Value);
1287                     }
1288                     foreach (XmlAttribute attribute in baseNode.Attributes)
1289                     {
1290                         if (mergeSubNode.Attributes.GetNamedItem(attribute.Name) == null)
1291                         {
1292                             AddAttribute(mergeSubNode, attribute.Name, attribute.Value);
1293                         }
1294                     }
1295 
1296                     mergeElement.AppendChild(mergeSubNode);
1297                 }
1298             }
1299         }
1300 
QueryForSubNode(XmlNode subNode, string strSubNodeKey, string strTargetValue)1301         private XmlNode QueryForSubNode(XmlNode subNode, string strSubNodeKey, string strTargetValue)
1302         {
1303             string strQuery = string.Format(CultureInfo.InvariantCulture, "{0}:*[@{1}='{2}']", BOOTSTRAPPER_PREFIX, strSubNodeKey, strTargetValue);
1304             return subNode.SelectSingleNode(strQuery, _xmlNamespaceManager);
1305         }
1306 
CorrectPackageFiles(XmlNode node)1307         private void CorrectPackageFiles(XmlNode node)
1308         {
1309             XmlNode packageFilesNode = node.SelectSingleNode(BOOTSTRAPPER_PREFIX + ":PackageFiles", _xmlNamespaceManager);
1310 
1311             if (packageFilesNode != null)
1312             {
1313                 // Map all StringKey attributes to corresponding String values
1314                 XmlNodeList packageFileNodeList = node.SelectNodes("//" + BOOTSTRAPPER_PREFIX + ":*[@PackageFile]", _xmlNamespaceManager);
1315                 foreach (XmlNode currentNode in packageFileNodeList)
1316                 {
1317                     XmlAttribute attribute = (XmlAttribute)(currentNode.Attributes.GetNamedItem("PackageFile"));
1318                     string strQuery = BOOTSTRAPPER_PREFIX + ":PackageFile[@Name='" + attribute.Value + "']";
1319                     XmlNode packageFileNode = packageFilesNode.SelectSingleNode(strQuery, _xmlNamespaceManager);
1320                     if (packageFileNode != null)
1321                     {
1322                         XmlAttribute targetPathAttribute = (XmlAttribute)(packageFileNode.Attributes.GetNamedItem("TargetPath"));
1323                         attribute.Value = targetPathAttribute.Value;
1324                     }
1325                 }
1326             }
1327         }
1328 
ReplaceStrings(XmlNode node)1329         private void ReplaceStrings(XmlNode node)
1330         {
1331             XmlNode stringsNode = node.SelectSingleNode(BOOTSTRAPPER_PREFIX + ":Strings", _xmlNamespaceManager);
1332             XmlNode stringNode;
1333             XmlAttribute attribute;
1334 
1335             if (stringsNode != null)
1336             {
1337                 string stringNodeLookupTemplate = BOOTSTRAPPER_PREFIX + ":String[@Name='{0}']";
1338 
1339                 // The name attribute at the package level is an entry into the String table
1340                 ReplaceAttributeString(node, "Name", stringsNode);
1341                 ReplaceAttributeString(node, "Culture", stringsNode);
1342 
1343                 // Homesite information is also carried in the String table
1344                 XmlNode packageFilesNode = node.SelectSingleNode(BOOTSTRAPPER_PREFIX + ":PackageFiles", _xmlNamespaceManager);
1345                 if (packageFilesNode != null)
1346                 {
1347                     XmlNodeList packageFileNodeList = packageFilesNode.SelectNodes(BOOTSTRAPPER_PREFIX + ":PackageFile", _xmlNamespaceManager);
1348                     foreach (XmlNode packageFileNode in packageFileNodeList)
1349                     {
1350                         ReplaceAttributeString(packageFileNode, HOMESITE_ATTRIBUTE, stringsNode);
1351                     }
1352                 }
1353 
1354                 // Map all String attributes to corresponding String values
1355                 //  It is currently expected that these come from either an ExitCode or FailIf
1356                 XmlNodeList stringKeyNodeList = node.SelectNodes("//" + BOOTSTRAPPER_PREFIX + ":*[@String]", _xmlNamespaceManager);
1357                 foreach (XmlNode currentNode in stringKeyNodeList)
1358                 {
1359                     attribute = (XmlAttribute)(currentNode.Attributes.GetNamedItem("String"));
1360                     stringNode = stringsNode.SelectSingleNode(string.Format(CultureInfo.InvariantCulture, stringNodeLookupTemplate, attribute.Value), _xmlNamespaceManager);
1361                     if (stringNode != null)
1362                     {
1363                         AddAttribute(currentNode, "Text", stringNode.InnerText);
1364                     }
1365                     currentNode.Attributes.Remove(attribute);
1366                 }
1367 
1368                 // The Strings node is no longer necessary.  Remove it.
1369                 node.RemoveChild(stringsNode);
1370             }
1371         }
1372 
BuildPackages(BuildSettings settings, XmlElement configElement, ResourceUpdater resourceUpdater, ArrayList filesCopied, Hashtable eulas)1373         private bool BuildPackages(BuildSettings settings, XmlElement configElement, ResourceUpdater resourceUpdater, ArrayList filesCopied, Hashtable eulas)
1374         {
1375             bool fSucceeded = true;
1376 
1377             foreach (ProductBuilder builder in settings.ProductBuilders)
1378             {
1379                 if (Validate && !builder.Product.ValidationPassed)
1380                 {
1381                     if (_results != null)
1382                     {
1383                         _results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Warning, "GenerateBootstrapper.ProductValidation", builder.Name, builder.Product.ValidationResults.FilePath));
1384                         foreach (string validationMessage in builder.Product.ValidationResults.ValidationErrors)
1385                         {
1386                             _results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Warning, "GenerateBootstrapper.ValidationError", builder.Product.ValidationResults.FilePath, validationMessage));
1387                         }
1388                         foreach (string validationMessage in builder.Product.ValidationResults.ValidationWarnings)
1389                         {
1390                             _results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Warning, "GenerateBootstrapper.ValidationWarning", builder.Product.ValidationResults.FilePath, validationMessage));
1391                         }
1392                     }
1393                 }
1394                 Package package = GetPackageForSettings(settings, builder, _results);
1395                 if (package == null)
1396                 {
1397                     // GetPackage should have already added the correct message info
1398                     continue;
1399                 }
1400 
1401                 if (Validate && !package.ValidationPassed)
1402                 {
1403                     if (_results != null)
1404                     {
1405                         _results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Warning, "GenerateBootstrapper.PackageValidation", builder.Name, package.ValidationResults.FilePath));
1406                         foreach (string validationMessage in package.ValidationResults.ValidationErrors)
1407                         {
1408                             _results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Warning, "GenerateBootstrapper.ValidationError", package.ValidationResults.FilePath, validationMessage));
1409                         }
1410                         foreach (string validationMessage in package.ValidationResults.ValidationWarnings)
1411                         {
1412                             _results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Warning, "GenerateBootstrapper.ValidationWarning", package.ValidationResults.FilePath, validationMessage));
1413                         }
1414                     }
1415                 }
1416 
1417                 XmlNode node = package.Node;
1418                 // Copy the files for this package to the output directory
1419                 XmlAttribute eulaAttribute = node.Attributes[EULA_ATTRIBUTE];
1420                 XmlNodeList packageFileNodes = node.SelectNodes(BOOTSTRAPPER_PREFIX + ":PackageFiles/" + BOOTSTRAPPER_PREFIX + ":PackageFile", _xmlNamespaceManager);
1421                 XmlNode installChecksNode = node.SelectSingleNode(BOOTSTRAPPER_PREFIX + ":InstallChecks", _xmlNamespaceManager);
1422                 foreach (XmlNode packageFileNode in packageFileNodes)
1423                 {
1424                     XmlAttribute packageFileSource = (XmlAttribute)(packageFileNode.Attributes.GetNamedItem("SourcePath"));
1425                     XmlAttribute packageFileDestination = (XmlAttribute)(packageFileNode.Attributes.GetNamedItem("TargetPath"));
1426                     XmlAttribute packageFileName = (XmlAttribute)(packageFileNode.Attributes.GetNamedItem("Name"));
1427                     XmlAttribute packageFileCopy = (XmlAttribute)(packageFileNode.Attributes.GetNamedItem("CopyOnBuild"));
1428                     if (packageFileSource != null && eulaAttribute != null && !String.IsNullOrEmpty(eulaAttribute.Value) && packageFileSource.Value == eulaAttribute.Value)
1429                     {
1430                         // need to remove EULA from the package file list
1431                         XmlNode packageFilesNode = node.SelectSingleNode(BOOTSTRAPPER_PREFIX + ":PackageFiles", _xmlNamespaceManager);
1432                         packageFilesNode.RemoveChild(packageFileNode);
1433                         continue;
1434                     }
1435 
1436                     if ((packageFileSource != null) && (packageFileDestination != null) &&
1437                         (packageFileName != null))
1438                     {
1439                         // Calculate the hash of this file and add it to the PackageFileNode
1440                         if (!AddVerificationInformation(packageFileNode, packageFileSource.Value, packageFileName.Value, builder, settings, _results))
1441                             fSucceeded = false;
1442                     }
1443 
1444                     if ((packageFileSource != null) && (packageFileDestination != null) &&
1445                         ((packageFileCopy == null) || (String.Compare(packageFileCopy.Value, "False", StringComparison.InvariantCulture) != 0)))
1446                     {
1447                         // if this is the key for an external check, we will add it to the Resource Updater instead of copying the file
1448                         XmlNode subNode = null;
1449                         if ((installChecksNode != null) && (packageFileName != null))
1450                         {
1451                             subNode = QueryForSubNode(installChecksNode, "PackageFile", packageFileName.Value);
1452                         }
1453                         if (subNode != null)
1454                         {
1455                             if (resourceUpdater != null)
1456                             {
1457                                 if (!File.Exists(packageFileSource.Value))
1458                                 {
1459                                     if (_results != null)
1460                                         _results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Error, "GenerateBootstrapper.PackageResourceFileNotFound", packageFileSource.Value, builder.Name));
1461                                     fSucceeded = false;
1462                                     continue;
1463                                 }
1464                                 resourceUpdater.AddFileResource(packageFileSource.Value, packageFileDestination.Value);
1465                             }
1466                         }
1467                         else
1468                         {
1469                             if (settings.ComponentsLocation != ComponentsLocation.HomeSite || !VerifyHomeSiteInformation(packageFileNode, builder, settings, _results))
1470                             {
1471                                 if (settings.CopyComponents)
1472                                 {
1473                                     string strDestinationFileName = System.IO.Path.Combine(settings.OutputPath, packageFileDestination.Value);
1474                                     try
1475                                     {
1476                                         if (!File.Exists(packageFileSource.Value))
1477                                         {
1478                                             if (_results != null)
1479                                                 _results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Error, "GenerateBootstrapper.PackageFileNotFound", packageFileDestination.Value, builder.Name));
1480                                             fSucceeded = false;
1481                                             continue;
1482                                         }
1483                                         EnsureFolderExists(System.IO.Path.GetDirectoryName(strDestinationFileName));
1484                                         File.Copy(packageFileSource.Value, strDestinationFileName, true);
1485                                         ClearReadOnlyAttribute(strDestinationFileName);
1486                                     }
1487                                     catch (UnauthorizedAccessException ex)
1488                                     {
1489                                         if (_results != null)
1490                                             _results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Error, "GenerateBootstrapper.CopyPackageError", packageFileSource.Value, builder.Name, ex.Message));
1491                                         fSucceeded = false;
1492                                         continue;
1493                                     }
1494                                     catch (IOException ex)
1495                                     {
1496                                         if (_results != null)
1497                                             _results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Error, "GenerateBootstrapper.CopyPackageError", packageFileSource.Value, builder.Name, ex.Message));
1498                                         fSucceeded = false;
1499                                         continue;
1500                                     }
1501                                     catch (ArgumentException ex)
1502                                     {
1503                                         if (_results != null)
1504                                             _results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Error, "GenerateBootstrapper.CopyPackageError", packageFileSource.Value, builder.Name, ex.Message));
1505                                         fSucceeded = false;
1506                                         continue;
1507                                     }
1508                                     catch (NotSupportedException ex)
1509                                     {
1510                                         if (_results != null)
1511                                             _results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Error, "GenerateBootstrapper.CopyPackageError", packageFileSource.Value, builder.Name, ex.Message));
1512                                         fSucceeded = false;
1513                                         continue;
1514                                     }
1515                                     filesCopied.Add(strDestinationFileName);
1516                                 }
1517                                 else
1518                                 {
1519                                     filesCopied.Add(packageFileSource.Value);
1520                                 }
1521 
1522                                 // Add the file size to the PackageFileNode
1523                                 XmlAttribute sizeAttribute = packageFileNode.OwnerDocument.CreateAttribute("Size");
1524                                 FileInfo fi = new FileInfo(packageFileSource.Value);
1525                                 sizeAttribute.Value = "" + (fi.Length.ToString(CultureInfo.InvariantCulture));
1526                                 MergeAttribute(packageFileNode, sizeAttribute);
1527                             }
1528                         }
1529                     }
1530                 }
1531                 // Add the Eula attribute correctly
1532                 if (eulas != null && eulaAttribute != null && !String.IsNullOrEmpty(eulaAttribute.Value))
1533                 {
1534                     if (File.Exists(eulaAttribute.Value))
1535                     {
1536                         // eulas[GetFileHash(eulaAttribute.Value)] = eulaAttribute.Value;
1537                         string key = GetFileHash(eulaAttribute.Value);
1538                         if (eulas.ContainsKey(key))
1539                             eulaAttribute.Value = ((DictionaryEntry)eulas[key]).Key.ToString();
1540                         else
1541                         {
1542                             string configFileKey = string.Format(CultureInfo.InvariantCulture, "EULA{0}", eulas.Count);
1543                             DictionaryEntry de = new DictionaryEntry(configFileKey, eulaAttribute.Value);
1544                             eulas[key] = de;
1545                             eulaAttribute.Value = configFileKey;
1546                         }
1547                     }
1548                     else
1549                     {
1550                         if (_results != null)
1551                             _results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Error, "GenerateBootstrapper.PackageResourceFileNotFound", eulaAttribute.Value, builder.Name));
1552                         fSucceeded = false;
1553                         continue;
1554                     }
1555                 }
1556                 // Write the package node
1557                 if (configElement != null)
1558                 {
1559                     configElement.AppendChild(configElement.OwnerDocument.ImportNode(node, true));
1560                     DumpXmlToFile(node, string.Format(CultureInfo.CurrentCulture, "{0}.{1}.xml", package.Product.ProductCode, package.Culture));
1561                 }
1562             }
1563 
1564             return fSucceeded;
1565         }
1566 
CreateProductNode(XmlNode node)1567         private XmlNode CreateProductNode(XmlNode node)
1568         {
1569             // create a new Product node for the passed-in product
1570             XmlNode productNode = _document.CreateElement("Product", BOOTSTRAPPER_NAMESPACE);
1571             XmlAttribute sourceAttribute;
1572 
1573             // find the ProductCode attribute
1574             sourceAttribute = (XmlAttribute)(node.Attributes.GetNamedItem("ProductCode"));
1575             Debug.Assert(sourceAttribute != null, "we should not be here if there is no ProductCode attribute");
1576 
1577             AddAttribute(productNode, "ProductCode", sourceAttribute.Value);
1578 
1579             node.Attributes.Remove(sourceAttribute);
1580 
1581             return productNode;
1582         }
1583 
ReadAttribute(XmlNode node, string strAttributeName)1584         private string ReadAttribute(XmlNode node, string strAttributeName)
1585         {
1586             XmlAttribute attribute = (XmlAttribute)(node.Attributes.GetNamedItem(strAttributeName));
1587 
1588             if (attribute != null)
1589                 return attribute.Value;
1590 
1591             return null;
1592         }
1593 
EnsureFolderExists(string strFolderPath)1594         private void EnsureFolderExists(string strFolderPath)
1595         {
1596             if (!Directory.Exists(strFolderPath))
1597             {
1598                 Directory.CreateDirectory(strFolderPath);
1599             }
1600         }
1601 
ClearReadOnlyAttribute(string strFileName)1602         private void ClearReadOnlyAttribute(string strFileName)
1603         {
1604             FileAttributes attribs = File.GetAttributes(strFileName);
1605             if ((attribs & FileAttributes.ReadOnly) != 0)
1606             {
1607                 attribs = attribs & (~FileAttributes.ReadOnly);
1608                 File.SetAttributes(strFileName, attribs);
1609             }
1610         }
1611 
ByteArrayToString(byte[] byteArray)1612         private string ByteArrayToString(byte[] byteArray)
1613         {
1614             if (byteArray == null) return null;
1615 
1616             System.Text.StringBuilder output = new System.Text.StringBuilder(byteArray.Length);
1617             foreach (byte byteValue in byteArray)
1618                 output.Append(byteValue.ToString("X02", CultureInfo.InvariantCulture));
1619 
1620             return output.ToString();
1621         }
1622 
GetFileHash(string filePath)1623         private string GetFileHash(string filePath)
1624         {
1625             FileInfo fi = new System.IO.FileInfo(filePath);
1626             String retVal = null;
1627 
1628             // Bootstrapper is always signed with the SHA-256 algorithm, no matter which version of
1629             // the .NET Framework we are targeting.  In ideal situations, bootstrapper files will be
1630             // pre-signed anwyay; this is a fallback in case we ever encounter a bootstrapper that is
1631             // not signed.
1632             System.Security.Cryptography.SHA256CryptoServiceProvider sha = new System.Security.Cryptography.SHA256CryptoServiceProvider();
1633 
1634             using (Stream s = fi.OpenRead())
1635             {
1636                 retVal = ByteArrayToString(sha.ComputeHash(s));
1637             }
1638             return retVal;
1639         }
1640 
ReplaceAttributeString(XmlNode node, string attributeName, XmlNode stringsNode)1641         private void ReplaceAttributeString(XmlNode node, string attributeName, XmlNode stringsNode)
1642         {
1643             string stringNodeLookupTemplate = BOOTSTRAPPER_PREFIX + ":String[@Name='{0}']";
1644             XmlAttribute attribute = (XmlAttribute)(node.Attributes.GetNamedItem(attributeName));
1645             if (attribute != null)
1646             {
1647                 XmlNode stringNode = stringsNode.SelectSingleNode(string.Format(CultureInfo.InvariantCulture, stringNodeLookupTemplate, attribute.Value), _xmlNamespaceManager);
1648                 if (stringNode != null)
1649                     attribute.Value = stringNode.InnerText;
1650             }
1651         }
1652 
GetPackageForSettings(BuildSettings settings, ProductBuilder builder, BuildResults results)1653         private Package GetPackageForSettings(BuildSettings settings, ProductBuilder builder, BuildResults results)
1654         {
1655             CultureInfo ci = Util.GetCultureInfoFromString(settings.Culture);
1656             CultureInfo fallbackCI = Util.GetCultureInfoFromString(settings.FallbackCulture);
1657             Package package = null;
1658 
1659             if (builder.Product.Packages.Count == 0)
1660             {
1661                 if (results != null)
1662                     results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Error, "GenerateBootstrapper.ProductCultureNotFound", builder.Name));
1663                 return null;
1664             }
1665 
1666             if (ci != null)
1667             {
1668                 package = builder.Product.Packages.Package(ci.Name);
1669                 if (package != null) return package;
1670 
1671                 // Target culture not found?  Go through the progression of parent cultures (up until but excluding the invariant culture) -> fallback culture -> parent fallback culture -> default culture -> parent default culture -> any culture available
1672                 // Note: there is no warning if the parent culture of the requested culture is found
1673                 CultureInfo parentCulture = ci.Parent;
1674 
1675                 // Keep going up the chain of parents, stopping at the invariant culture
1676                 while (parentCulture != null && parentCulture != CultureInfo.InvariantCulture)
1677                 {
1678                     package = GetPackageForSettings_Helper(ci, parentCulture, builder, results, false);
1679                     if (package != null) return package;
1680 
1681                     parentCulture = parentCulture.Parent;
1682                 }
1683             }
1684 
1685 
1686             if (fallbackCI != null)
1687             {
1688                 package = GetPackageForSettings_Helper(ci, fallbackCI, builder, results, true);
1689                 if (package != null) return package;
1690 
1691                 if (!fallbackCI.IsNeutralCulture)
1692                 {
1693                     package = GetPackageForSettings_Helper(ci, fallbackCI.Parent, builder, results, true);
1694                     if (package != null) return package;
1695                 }
1696             }
1697 
1698             package = GetPackageForSettings_Helper(ci, Util.DefaultCultureInfo, builder, results, true);
1699             if (package != null) return package;
1700 
1701             if (!Util.DefaultCultureInfo.IsNeutralCulture)
1702             {
1703                 package = GetPackageForSettings_Helper(ci, Util.DefaultCultureInfo.Parent, builder, results, true);
1704                 if (package != null) return package;
1705             }
1706 
1707             if (results != null && ci != null)
1708                 results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Warning, "GenerateBootstrapper.UsingProductCulture", ci.Name, builder.Name, builder.Product.Packages.Item(0).Culture));
1709             return builder.Product.Packages.Item(0);
1710         }
1711 
GetPackageForSettings_Helper(CultureInfo culture, CultureInfo altCulture, ProductBuilder builder, BuildResults results, bool fShowWarning)1712         private Package GetPackageForSettings_Helper(CultureInfo culture, CultureInfo altCulture, ProductBuilder builder, BuildResults results, bool fShowWarning)
1713         {
1714             if (altCulture == null)
1715                 return null;
1716             Package package = builder.Product.Packages.Package(altCulture.Name);
1717             if (package != null)
1718             {
1719                 if (fShowWarning && culture != null && results != null)
1720                     results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Warning, "GenerateBootstrapper.UsingProductCulture", culture.Name, builder.Name, altCulture.Name));
1721                 return package;
1722             }
1723             return null;
1724         }
1725 
BuildResources(BuildSettings settings, ResourceUpdater resourceUpdater)1726         private bool BuildResources(BuildSettings settings, ResourceUpdater resourceUpdater)
1727         {
1728             if (_cultures.Count == 0)
1729             {
1730                 if (_results != null)
1731                     _results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Error, "GenerateBootstrapper.NoResources"));
1732                 return false;
1733             }
1734 
1735             int codePage = -1;
1736             XmlNode resourcesNode = GetResourcesNodeForSettings(settings, _results, ref codePage);
1737             XmlNode stringsNode = resourcesNode.SelectSingleNode("Strings");
1738             XmlNode fontsNode = resourcesNode.SelectSingleNode("Fonts");
1739 
1740             if (stringsNode == null)
1741             {
1742                 if (_results != null)
1743                     _results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Error, "GenerateBootstrapper.NoStringsForCulture", resourcesNode.Attributes.GetNamedItem("Culture").Value));
1744                 return false;
1745             }
1746 
1747             XmlNodeList stringNodes = stringsNode.SelectNodes("String");
1748 
1749             foreach (XmlNode stringNode in stringNodes)
1750             {
1751                 XmlAttribute resourceIdAttribute = (XmlAttribute)stringNode.Attributes.GetNamedItem("Name");
1752 
1753                 if (resourceIdAttribute != null)
1754                 {
1755                     resourceUpdater.AddStringResource(MESSAGE_TABLE, resourceIdAttribute.Value.ToUpper(CultureInfo.InvariantCulture), stringNode.InnerText);
1756                 }
1757             }
1758 
1759             if (fontsNode != null)
1760             {
1761                 foreach (XmlNode fontNode in fontsNode.SelectNodes("Font"))
1762                 {
1763                     ConvertChildsNodeToAttributes(fontNode);
1764                 }
1765                 string fontsConfig = XmlToConfigurationFile(fontsNode);
1766                 resourceUpdater.AddStringResource(RESOURCE_TABLE, "SETUPRES", fontsConfig);
1767                 DumpXmlToFile(fontsNode, "fonts.cfg.xml");
1768                 DumpStringToFile(fontsConfig, "fonts.cfg", false);
1769                 if (codePage != -1)
1770                     resourceUpdater.AddStringResource(RESOURCE_TABLE, "CODEPAGE", codePage.ToString(CultureInfo.InvariantCulture));
1771             }
1772             return true;
1773         }
1774 
GetResourcesNodeForSettings(BuildSettings settings, BuildResults results, ref int codepage)1775         private XmlNode GetResourcesNodeForSettings(BuildSettings settings, BuildResults results, ref int codepage)
1776         {
1777             CultureInfo ci = Util.GetCultureInfoFromString(settings.Culture);
1778             CultureInfo fallbackCI = Util.GetCultureInfoFromString(settings.FallbackCulture);
1779             XmlNode cultureNode = null;
1780 
1781 
1782             if (ci != null)
1783             {
1784                 // Work through the progression of parent cultures (up until but excluding the invariant culture) -> fallback culture -> parent fallback culture -> default culture -> parent default culture -> any available culture
1785                 cultureNode = GetResourcesNodeForSettings_Helper(ci, ci, results, ref codepage, false);
1786                 if (cultureNode != null) return cultureNode;
1787                 CultureInfo parentCulture = ci.Parent;
1788 
1789                 // Keep going up the chain of parents, stopping at the invariant culture
1790                 while (parentCulture != null && parentCulture != CultureInfo.InvariantCulture)
1791                 {
1792                     cultureNode = GetResourcesNodeForSettings_Helper(ci, parentCulture, results, ref codepage, false);
1793                     if (cultureNode != null) return cultureNode;
1794 
1795                     parentCulture = parentCulture.Parent;
1796                 }
1797             }
1798 
1799             if (fallbackCI != null)
1800             {
1801                 cultureNode = GetResourcesNodeForSettings_Helper(ci, fallbackCI, results, ref codepage, true);
1802                 if (cultureNode != null) return cultureNode;
1803 
1804                 if (!fallbackCI.IsNeutralCulture)
1805                 {
1806                     cultureNode = GetResourcesNodeForSettings_Helper(ci, fallbackCI.Parent, results, ref codepage, true);
1807                     if (cultureNode != null) return cultureNode;
1808                 }
1809             }
1810 
1811             cultureNode = GetResourcesNodeForSettings_Helper(ci, Util.DefaultCultureInfo, results, ref codepage, true);
1812             if (cultureNode != null) return cultureNode;
1813 
1814             if (!Util.DefaultCultureInfo.IsNeutralCulture)
1815             {
1816                 cultureNode = GetResourcesNodeForSettings_Helper(ci, Util.DefaultCultureInfo.Parent, results, ref codepage, true);
1817                 if (cultureNode != null) return cultureNode;
1818             }
1819 
1820             IEnumerator keys = _cultures.Keys.GetEnumerator();
1821             keys.MoveNext();
1822             string altCulture = (string)keys.Current;
1823             if (ci != null && results != null)
1824                 results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Warning, "GenerateBootstrapper.UsingResourcesCulture", ci.Name, altCulture));
1825             GetCodePage(altCulture, ref codepage);
1826             return (XmlNode)_cultures[altCulture.ToLowerInvariant()];
1827         }
1828 
GetResourcesNodeForSettings_Helper(CultureInfo culture, CultureInfo altCulture, BuildResults results, ref int codepage, bool fShowWarning)1829         private XmlNode GetResourcesNodeForSettings_Helper(CultureInfo culture, CultureInfo altCulture, BuildResults results, ref int codepage, bool fShowWarning)
1830         {
1831             if (altCulture != null && _cultures.Contains(altCulture.Name.ToLowerInvariant()))
1832             {
1833                 if (fShowWarning && culture != null && results != null)
1834                     results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Warning, "GenerateBootstrapper.UsingResourcesCulture", culture.Name, altCulture.Name));
1835 
1836                 codepage = altCulture.TextInfo.ANSICodePage;
1837                 return (XmlNode)_cultures[altCulture.Name.ToLowerInvariant()];
1838             }
1839 
1840             return null;
1841         }
1842 
GetCodePage(string culture, ref int codePage)1843         private void GetCodePage(string culture, ref int codePage)
1844         {
1845             try
1846             {
1847                 System.Globalization.CultureInfo info = new System.Globalization.CultureInfo(culture);
1848                 codePage = info.TextInfo.ANSICodePage;
1849             }
1850             catch (ArgumentException ex)
1851             {
1852                 Debug.Fail(ex.Message);
1853             }
1854         }
1855 
ReplacePackageFileAttributes(XmlNode targetNodes, string targetAttribute, XmlNode sourceNodes, string sourceSubNodeName, string sourceOldName, string sourceNewName)1856         private void ReplacePackageFileAttributes(XmlNode targetNodes, string targetAttribute, XmlNode sourceNodes, string sourceSubNodeName, string sourceOldName, string sourceNewName)
1857         {
1858             XmlNodeList sourceNodeList = sourceNodes.SelectNodes(BOOTSTRAPPER_PREFIX + ":" + sourceSubNodeName, _xmlNamespaceManager);
1859 
1860             foreach (XmlNode sourceNode in sourceNodeList)
1861             {
1862                 XmlAttribute oldNameAttribute = (XmlAttribute)(sourceNode.Attributes.GetNamedItem(sourceOldName));
1863                 XmlAttribute newNameAttribute = (XmlAttribute)(sourceNode.Attributes.GetNamedItem(sourceNewName));
1864 
1865                 if (oldNameAttribute != null && newNameAttribute != null)
1866                 {
1867                     ReplaceAttributes(targetNodes, targetAttribute, oldNameAttribute.Value, newNameAttribute.Value);
1868                 }
1869             }
1870         }
1871 
CreateApplicationElement(XmlElement configElement, BuildSettings settings)1872         private XmlElement CreateApplicationElement(XmlElement configElement, BuildSettings settings)
1873         {
1874             XmlElement applicationElement = null;
1875 
1876             if (!String.IsNullOrEmpty(settings.ApplicationName) || !String.IsNullOrEmpty(settings.ApplicationFile))
1877             {
1878                 applicationElement = configElement.OwnerDocument.CreateElement("Application");
1879                 if (!String.IsNullOrEmpty(settings.ApplicationName))
1880                 {
1881                     AddAttribute(applicationElement, "Name", settings.ApplicationName);
1882                 }
1883                 AddAttribute(applicationElement, "RequiresElevation", settings.ApplicationRequiresElevation ? "true" : "false");
1884 
1885                 if (!String.IsNullOrEmpty(settings.ApplicationFile))
1886                 {
1887                     XmlElement filesNode = applicationElement.OwnerDocument.CreateElement("Files");
1888                     XmlElement fileNode = filesNode.OwnerDocument.CreateElement("File");
1889                     AddAttribute(fileNode, "Name", settings.ApplicationFile);
1890                     AddAttribute(fileNode, URLNAME_ATTRIBUTE, Uri.EscapeUriString(settings.ApplicationFile));
1891                     filesNode.AppendChild(fileNode);
1892                     applicationElement.AppendChild(filesNode);
1893                 }
1894             }
1895             return applicationElement;
1896         }
1897 
AddAttribute(XmlNode node, string attributeName, string attributeValue)1898         private void AddAttribute(XmlNode node, string attributeName, string attributeValue)
1899         {
1900             XmlAttribute attrib = node.OwnerDocument.CreateAttribute(attributeName);
1901             attrib.Value = attributeValue;
1902             node.Attributes.Append(attrib);
1903         }
1904 
1905         [SuppressMessage("Microsoft.Security.Xml", "CA3073: ReviewTrustedXsltUse.", Justification = "Input style sheet comes from our own assemblies. Hence it is a trusted source.")]
1906         [SuppressMessage("Microsoft.Security.Xml", "CA3059: UseXmlReaderForXPathDocument.", Justification = "Input style sheet comes from our own assemblies. Hence it is a trusted source.")]
1907         [SuppressMessage("Microsoft.Security.Xml", "CA3052: UseXmlResolver.", Justification = "Input style sheet comes from our own assemblies. Hence it is a trusted source.")]
XmlToConfigurationFile(XmlNode input)1908         private string XmlToConfigurationFile(XmlNode input)
1909         {
1910             using (XmlNodeReader reader = new XmlNodeReader(input))
1911             {
1912                 Stream s = GetEmbeddedResourceStream(CONFIG_TRANSFORM);
1913                 XPathDocument d = new XPathDocument(s);
1914                 XslCompiledTransform xslc = new XslCompiledTransform();
1915                 // Using the Trusted Xslt is fine as the style sheet comes from our own assembly.
1916                 xslc.Load(d, XsltSettings.TrustedXslt, new XmlUrlResolver());
1917 
1918                 XPathDocument xml = new XPathDocument(reader);
1919 
1920                 using (MemoryStream m = new MemoryStream())
1921                 {
1922                     using (StreamWriter w = new StreamWriter(m))
1923                     {
1924                         xslc.Transform(xml, null, w);
1925 
1926                         w.Flush();
1927                         m.Position = 0;
1928 
1929                         using (StreamReader r = new StreamReader(m))
1930                         {
1931                             // HACKHACK
1932                             string str = r.ReadToEnd();
1933                             str = str.Replace("%NEWLINE%", Environment.NewLine);
1934                             return str;
1935                         }
1936                     }
1937                 }
1938             }
1939         }
1940 
GetEmbeddedResourceStream(string name)1941         private Stream GetEmbeddedResourceStream(string name)
1942         {
1943             Assembly a = Assembly.GetExecutingAssembly();
1944             Stream s = a.GetManifestResourceStream(String.Format(CultureInfo.InvariantCulture, "{0}.{1}", typeof(BootstrapperBuilder).Namespace, name));
1945             Debug.Assert(s != null, String.Format(CultureInfo.CurrentCulture, "EmbeddedResource '{0}' not found", name));
1946             return s;
1947         }
1948 
GetAssemblyPath()1949         private string GetAssemblyPath()
1950         {
1951             return System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
1952         }
1953 
DumpXmlToFile(XmlNode node, string fileName)1954         private void DumpXmlToFile(XmlNode node, string fileName)
1955         {
1956             if (s_logging)
1957             {
1958                 try
1959                 {
1960                     using (XmlTextWriter xmlwriter = new XmlTextWriter(System.IO.Path.Combine(s_logPath, fileName), System.Text.Encoding.UTF8))
1961                     {
1962                         xmlwriter.Formatting = Formatting.Indented;
1963                         xmlwriter.Indentation = 4;
1964                         xmlwriter.WriteNode(new XmlNodeReader(node), true);
1965                     }
1966                 }
1967                 catch (IOException)
1968                 {
1969                     // can't write info to a log file?  This is a trouble-shooting helper only, and
1970                     // this exception can be ignored
1971                 }
1972                 catch (UnauthorizedAccessException)
1973                 {
1974                     // can't write info to a log file?  This is a trouble-shooting helper only, and
1975                     // this exception can be ignored
1976                 }
1977                 catch (ArgumentException)
1978                 {
1979                     // can't write info to a log file?  This is a trouble-shooting helper only, and
1980                     // this exception can be ignored
1981                 }
1982                 catch (NotSupportedException)
1983                 {
1984                     // can't write info to a log file?  This is a trouble-shooting helper only, and
1985                     // this exception can be ignored
1986                 }
1987                 catch (XmlException)
1988                 {
1989                     // can't write info to a log file?  This is a trouble-shooting helper only, and
1990                     // this exception can be ignored
1991                 }
1992             }
1993         }
1994 
DumpStringToFile(string text, string fileName, bool append)1995         private void DumpStringToFile(string text, string fileName, bool append)
1996         {
1997             if (s_logging)
1998             {
1999                 try
2000                 {
2001                     using (StreamWriter fileWriter = new StreamWriter(System.IO.Path.Combine(s_logPath, fileName), append))
2002                     {
2003                         fileWriter.Write(text);
2004                     }
2005                 }
2006                 catch (IOException)
2007                 {
2008                     // can't write info to a log file?  This is a trouble-shooting helper only, and
2009                     // this exception can be ignored
2010                 }
2011                 catch (UnauthorizedAccessException)
2012                 {
2013                     // can't write info to a log file?  This is a trouble-shooting helper only, and
2014                     // this exception can be ignored
2015                 }
2016                 catch (ArgumentException)
2017                 {
2018                     // can't write info to a log file?  This is a trouble-shooting helper only, and
2019                     // this exception can be ignored
2020                 }
2021                 catch (NotSupportedException)
2022                 {
2023                     // can't write info to a log file?  This is a trouble-shooting helper only, and
2024                     // this exception can be ignored
2025                 }
2026             }
2027         }
2028 
VerifyHomeSiteInformation(XmlNode packageFileNode, ProductBuilder builder, BuildSettings settings, BuildResults results)2029         private bool VerifyHomeSiteInformation(XmlNode packageFileNode, ProductBuilder builder, BuildSettings settings, BuildResults results)
2030         {
2031             if (settings.ComponentsLocation != ComponentsLocation.HomeSite)
2032             {
2033                 return true;
2034             }
2035 
2036             XmlAttribute homesiteAttribute = packageFileNode.Attributes[HOMESITE_ATTRIBUTE];
2037 
2038             if (homesiteAttribute == null && builder.Product.CopyAllPackageFiles != CopyAllFilesType.CopyAllFilesIfNotHomeSite)
2039             {
2040                 if (results != null)
2041                     results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Warning, "GenerateBootstrapper.PackageHomeSiteMissing", builder.Name));
2042                 return false;
2043             }
2044 
2045             return true;
2046         }
2047 
AddVerificationInformation(XmlNode packageFileNode, string fileSource, string fileName, ProductBuilder builder, BuildSettings settings, BuildResults results)2048         private bool AddVerificationInformation(XmlNode packageFileNode, string fileSource, string fileName, ProductBuilder builder, BuildSettings settings, BuildResults results)
2049         {
2050             XmlAttribute hashAttribute = packageFileNode.Attributes[HASH_ATTRIBUTE];
2051             XmlAttribute publicKeyAttribute = packageFileNode.Attributes[PUBLICKEY_ATTRIBUTE];
2052 
2053             if (File.Exists(fileSource))
2054             {
2055                 string publicKey = GetPublicKeyOfFile(fileSource);
2056                 if (hashAttribute == null && publicKeyAttribute == null)
2057                 {
2058                     // If neither the Hash nor PublicKey attributes were specified in the manifest, add it
2059                     if (publicKey != null)
2060                     {
2061                         AddAttribute(packageFileNode, PUBLICKEY_ATTRIBUTE, publicKey);
2062                     }
2063                     else
2064                     {
2065                         AddAttribute(packageFileNode, HASH_ATTRIBUTE, GetFileHash(fileSource));
2066                     }
2067                 }
2068                 if (publicKeyAttribute != null)
2069                 {
2070                     // Always use the PublicKey of the file on disk
2071                     if (publicKey != null)
2072                         ReplaceAttribute(packageFileNode, PUBLICKEY_ATTRIBUTE, publicKey);
2073                     else
2074                     {
2075                         // File on disk is not signed.  Remove the public key info, and make sure the hash is written instead
2076                         packageFileNode.Attributes.RemoveNamedItem(PUBLICKEY_ATTRIBUTE);
2077                         if (hashAttribute == null)
2078                             AddAttribute(packageFileNode, HASH_ATTRIBUTE, GetFileHash(fileSource));
2079                     }
2080 
2081                     // If the public key in the file doesn't match the public key on disk, issue a build warning
2082                     if (publicKey == null || !publicKey.ToLowerInvariant().Equals(publicKeyAttribute.Value.ToLowerInvariant()))
2083                     {
2084                         if (results != null)
2085                             results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Warning, "GenerateBootstrapper.DifferingPublicKeys", PUBLICKEY_ATTRIBUTE, builder.Name, fileSource));
2086                     }
2087                 }
2088                 if (hashAttribute != null)
2089                 {
2090                     string fileHash = GetFileHash(fileSource);
2091 
2092                     // Always use the Hash of the file on disk
2093                     ReplaceAttribute(packageFileNode, HASH_ATTRIBUTE, fileHash);
2094 
2095                     // If the public key in the file doesn't match the public key on disk, issue a build warning
2096                     if (!fileHash.ToLowerInvariant().Equals(hashAttribute.Value.ToLowerInvariant()))
2097                     {
2098                         if (results != null)
2099                             results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Warning, "GenerateBootstrapper.DifferingPublicKeys", "Hash", builder.Name, fileSource));
2100                     }
2101                 }
2102             }
2103             else if (settings.ComponentsLocation == ComponentsLocation.HomeSite)
2104             {
2105                 if (hashAttribute == null && publicKeyAttribute == null)
2106                 {
2107                     if (results != null)
2108                     {
2109                         results.AddMessage(BuildMessage.CreateMessage(BuildMessageSeverity.Error, "GenerateBootstrapper.MissingVerificationInformation", fileName, builder.Name));
2110                     }
2111                     return false;
2112                 }
2113             }
2114 
2115             return true;
2116         }
2117 
GetPublicKeyOfFile(string fileSource)2118         private string GetPublicKeyOfFile(string fileSource)
2119         {
2120             if (File.Exists(fileSource))
2121             {
2122                 try
2123                 {
2124                     X509Certificate cert = new X509Certificate(fileSource);
2125                     string publicKey = cert.GetPublicKeyString();
2126                     return publicKey;
2127                 }
2128                 catch (System.Security.Cryptography.CryptographicException)
2129                 {
2130                     // This just means the file is not signed.
2131                 }
2132             }
2133 
2134             return null;
2135         }
2136 
ConvertChildsNodeToAttributes(XmlNode node)2137         private void ConvertChildsNodeToAttributes(XmlNode node)
2138         {
2139             XmlNode childNode = node.FirstChild;
2140             while (childNode != null)
2141             {
2142                 // Need to get the next child node now because when the current node is removed, the NextSibling
2143                 // will be null
2144                 XmlNode currentNode = childNode;
2145                 childNode = currentNode.NextSibling;
2146                 if (currentNode.Attributes.Count == 0 && currentNode.InnerText.Length > 0)
2147                 {
2148                     AddAttribute(node, currentNode.Name, currentNode.InnerText);
2149                     node.RemoveChild(currentNode);
2150                 }
2151             }
2152         }
2153 
GetLogPath()2154         private static string GetLogPath()
2155         {
2156             if (!s_logging) return null;
2157             string logPath = System.IO.Path.Combine(
2158                 Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
2159                 @"Microsoft\VisualStudio\" + VisualStudioConstants.CurrentVisualStudioVersion + @"\VSPLOG");
2160             if (!Directory.Exists(logPath))
2161                 Directory.CreateDirectory(logPath);
2162             return logPath;
2163         }
2164 
GetIncludedProducts(Product product)2165         private Dictionary<string, Product> GetIncludedProducts(Product product)
2166         {
2167             Dictionary<string, Product> includedProducts = new Dictionary<string, Product>(StringComparer.OrdinalIgnoreCase);
2168 
2169             // Add in this product in case there is a circular includes:
2170             // we won't continue to explore this product.  It will be removed later.
2171             includedProducts.Add(product.ProductCode, product);
2172 
2173             // Recursively add included products
2174             foreach (Product p in product.Includes)
2175             {
2176                 AddIncludedProducts(p, includedProducts);
2177             }
2178 
2179             includedProducts.Remove(product.ProductCode);
2180             return includedProducts;
2181         }
2182 
AddIncludedProducts(Product product, Dictionary<string, Product> includedProducts)2183         private void AddIncludedProducts(Product product, Dictionary<string, Product> includedProducts)
2184         {
2185             if (!includedProducts.ContainsKey(product.ProductCode))
2186             {
2187                 includedProducts.Add(product.ProductCode, product);
2188                 foreach (Product p in product.Includes)
2189                 {
2190                     AddIncludedProducts(p, includedProducts);
2191                 }
2192             }
2193         }
2194 
MapLCIDToCultureName(int lcid)2195         private string MapLCIDToCultureName(int lcid)
2196         {
2197             if (lcid == 0)
2198                 return Util.DefaultCultureInfo.Name;
2199 
2200             try
2201             {
2202                 CultureInfo ci = new CultureInfo(lcid);
2203                 return ci.Name;
2204             }
2205             catch (ArgumentException)
2206             {
2207                 // Can't convert this lcid to a CultureInfo?  Just return the default CultureInfo instead...
2208                 return Util.DefaultCultureInfo.Name;
2209             }
2210         }
2211     }
2212 }
2213