/Aurora/Framework/Configuration/ConfigurationLoader.cs
C# | 538 lines | 413 code | 51 blank | 74 comment | 92 complexity | 563da387c33f4700b41128c550ed8a61 MD5 | raw file
1/* 2 * Copyright (c) Contributors, http://aurora-sim.org/, http://opensimulator.org/ 3 * See CONTRIBUTORS.TXT for a full list of copyright holders. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * * Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * * Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * * Neither the name of the Aurora-Sim Project nor the 13 * names of its contributors may be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY 17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY 20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28using System; 29using System.Collections.Generic; 30using System.IO; 31using System.Linq; 32using Nini.Config; 33using Nini.Ini; 34 35namespace Aurora.Framework 36{ 37 /// <summary> 38 /// Loads the Configuration files into nIni 39 /// </summary> 40 public class ConfigurationLoader 41 { 42 public string defaultIniFile = "Aurora.ini"; 43 44 public string iniFilePath = ""; 45 46 /// <summary> 47 /// Should we save all merging of the .ini files to the filesystem? 48 /// </summary> 49 protected bool inidbg; 50 51 public Dictionary<string, string> m_defines = new Dictionary<string, string>(); 52 53 /// <summary> 54 /// Should we show all the loading of the config files? 55 /// </summary> 56 protected bool showIniLoading; 57 58 /// <summary> 59 /// Loads the region configuration 60 /// </summary> 61 /// <param name = "argvSource">Parameters passed into the process when started</param> 62 /// <returns>A configuration that gets passed to modules</returns> 63 public IConfigSource LoadConfigSettings(IConfigSource argvSource) 64 { 65 iniFilePath = ""; 66 bool iniFileExists = false; 67 bool oldoptions = false; 68 69 string mainIniDirectory = ""; 70 string mainIniFileName = defaultIniFile; 71 string secondaryIniFileName = ""; 72 73 List<string> sources = new List<string>(); 74 string basePath = Util.configDir(); 75 76 if (argvSource != null) 77 { 78 IConfig startupConfig = argvSource.Configs["Startup"]; 79 80 81 oldoptions = 82 startupConfig.GetBoolean("oldoptions", false); 83 84 inidbg = 85 startupConfig.GetBoolean("inidbg", inidbg); 86 87 showIniLoading = 88 startupConfig.GetBoolean("inishowfileloading", showIniLoading); 89 90 if (oldoptions) 91 { 92 string masterFileName = 93 startupConfig.GetString("inimaster", String.Empty); 94 95 string iniGridName = 96 startupConfig.GetString("inigrid", String.Empty); 97 98 if (iniGridName == string.Empty) //Read the old name then 99 iniGridName = 100 startupConfig.GetString("inifile", String.Empty); 101 102 string iniSimName = 103 startupConfig.GetString("inisim", defaultIniFile); 104 105 //Be mindful of these when modifying... 106 //1) When file A includes file B, if the same directive is found in both, that the value in file B wins. 107 //2) That inifile may be used with or without inimaster being used. 108 //3) That any values for directives pulled in via inifile (Config Set 2) override directives of the same name found in the directive set (Config Set 1) created by reading in bin/Aurora.ini and its subsequently included files or that created by reading in whatever file inimaster points to and its subsequently included files. 109 110 if (IsUri(masterFileName)) 111 { 112 if (!sources.Contains(masterFileName)) 113 sources.Add(masterFileName); 114 } 115 else 116 { 117 string masterFilePath = Util.BasePathCombine(masterFileName); 118 119 if (masterFileName != String.Empty && 120 File.Exists(masterFilePath) && 121 (!sources.Contains(masterFilePath))) 122 sources.Add(masterFilePath); 123 if (iniGridName == "") //Then it doesn't exist and we need to set this 124 iniFilePath = masterFilePath; 125 if (iniSimName == "") //Then it doesn't exist and we need to set this 126 iniFilePath = masterFilePath; 127 } 128 129 if (iniGridName != "") 130 { 131 if (IsUri(iniGridName)) 132 { 133 if (!sources.Contains(iniGridName)) 134 sources.Add(iniGridName); 135 iniFilePath = iniGridName; 136 } 137 else 138 { 139 iniFilePath = Util.BasePathCombine(iniGridName); 140 141 if (File.Exists(iniFilePath)) 142 { 143 if (!sources.Contains(iniFilePath)) 144 sources.Add(iniFilePath); 145 } 146 } 147 } 148 149 if (iniSimName != "") 150 { 151 if (IsUri(iniSimName)) 152 { 153 if (!sources.Contains(iniSimName)) 154 sources.Add(iniSimName); 155 iniFilePath = iniSimName; 156 } 157 else 158 { 159 iniFilePath = Util.BasePathCombine(iniSimName); 160 161 if (File.Exists(iniFilePath)) 162 { 163 if (!sources.Contains(iniFilePath)) 164 sources.Add(iniFilePath); 165 } 166 } 167 } 168 169 string iniDirName = 170 startupConfig.GetString("inidirectory", ""); 171 172 if (iniDirName != "" && Directory.Exists(iniDirName)) 173 { 174 Console.WriteLine(string.Format("Searching folder {0} for config ini files", 175 iniDirName)); 176 177 string[] fileEntries = Directory.GetFiles(iniDirName); 178#if (!ISWIN) 179 foreach (string filePath in fileEntries) 180 { 181 string extension = Path.GetExtension(filePath); 182 if (extension != null && extension.ToLower() == ".ini") 183 { 184 if (!sources.Contains(Path.Combine(iniDirName, filePath))) 185 sources.Add(Path.Combine(iniDirName, filePath)); 186 } 187 } 188#else 189 foreach (string filePath in fileEntries.Where(filePath => 190 { 191 var extension = Path.GetExtension(filePath); 192 return extension != null && extension.ToLower() == ".ini"; 193 }).Where(filePath => !sources.Contains(Path.Combine(iniDirName, filePath)))) 194 { 195 sources.Add(Path.Combine(iniDirName, filePath)); 196 } 197#endif 198 } 199 } 200 else 201 { 202 mainIniDirectory = startupConfig.GetString("mainIniDirectory", ""); 203 mainIniFileName = startupConfig.GetString("mainIniFileName", defaultIniFile); 204 secondaryIniFileName = startupConfig.GetString("secondaryIniFileName", ""); 205 } 206 } 207 208 if (!oldoptions) 209 { 210 if (mainIniDirectory != "") 211 basePath = mainIniDirectory; 212 if (mainIniFileName != "") 213 { 214 if (IsUri(mainIniFileName)) 215 { 216 if (!sources.Contains(mainIniFileName)) 217 sources.Add(mainIniFileName); 218 } 219 else 220 { 221 string mainIniFilePath = Path.Combine(mainIniDirectory, mainIniFileName); 222 if (!sources.Contains(mainIniFilePath)) 223 sources.Add(mainIniFilePath); 224 } 225 } 226 227 if (secondaryIniFileName != "") 228 { 229 if (IsUri(secondaryIniFileName)) 230 { 231 if (!sources.Contains(secondaryIniFileName)) 232 sources.Add(secondaryIniFileName); 233 } 234 else 235 { 236 string secondaryIniFilePath = Path.Combine(mainIniDirectory, secondaryIniFileName); 237 if (!sources.Contains(secondaryIniFilePath)) 238 sources.Add(secondaryIniFilePath); 239 } 240 } 241 } 242 243 IConfigSource m_config = new IniConfigSource(); 244 IConfigSource m_fakeconfig = new IniConfigSource(); 245 246 //Console.WriteLine(string.Format("[Config]: Reading configuration settings")); 247 248 if (sources.Count == 0) 249 { 250 Console.WriteLine(string.Format("[CONFIG]: Could not load any configuration")); 251 Console.WriteLine(string.Format("[CONFIG]: Did you copy the " + defaultIniFile + ".example file to " + defaultIniFile + 252 "?")); 253 throw new NotSupportedException(); 254 } 255 256 List<string> triedPaths = new List<string>(); 257 for (int i = 0; i < sources.Count; i++) 258 { 259 //Read all non .example files first, then read all the example ones 260 261 if (File.Exists(sources[i]) && 262 ReadConfig(sources[i], i, m_fakeconfig)) 263 iniFileExists = true; 264 else if (File.Exists(sources[i] + ".example") && 265 ReadConfig(sources[i] + ".example", i, m_fakeconfig)) 266 iniFileExists = true; 267 AddIncludes(sources, basePath, ref i, ref triedPaths, m_fakeconfig); 268 } 269 270 // 271 sources.Reverse(); 272 for (int i = 0; i < sources.Count; i++) 273 { 274 //Read all non .example files first, then read all the example ones 275 276 if (File.Exists(sources[i])) 277 ReadConfig(sources[i], i, m_config); 278 else if (File.Exists(sources[i] + ".example")) 279 ReadConfig(sources[i] + ".example", i, m_config); 280 } 281 282 FixDefines(ref m_config); 283 284 if (!iniFileExists) 285 { 286 Console.WriteLine(string.Format("[CONFIG]: Could not load any configuration")); 287 Console.WriteLine(string.Format("[CONFIG]: Configuration exists, but there was an error loading it!")); 288 throw new NotSupportedException(); 289 } 290 // Make sure command line options take precedence 291 if(argvSource != null) 292 m_config.Merge(argvSource); 293 294 return m_config; 295 } 296 297 private void FixDefines(ref IConfigSource m_config) 298 { 299 if (m_defines.Count == 0) 300 return; 301 302 foreach (IConfig config in m_config.Configs) 303 { 304 int i = 0; 305 foreach (string value in config.GetValues()) 306 { 307 string value1 = value; 308 foreach (string newValue in from def in m_defines.Keys where value1.Contains(def) select value1.Replace(def, m_defines[def])) 309 { 310 config.Set(config.GetKeys()[i], newValue); 311 } 312 i++; 313 } 314 } 315 } 316 317 /// <summary> 318 /// Adds the included files as ini configuration files 319 /// </summary> 320 /// <param name = "sources">List of URL strings or filename strings</param> 321 /// <param name = "cntr">Where should we start inserting sources into the list?</param> 322 private void AddIncludes(List<string> sources, string basePath, ref int cntr, ref List<string> triedPaths, 323 IConfigSource configSource) 324 { 325 int cn = cntr; 326 //Where should we insert the sources into the list? 327 //loop over config sources 328 foreach (IConfig config in configSource.Configs) 329 { 330 // Look for Include-* in the key name 331 string[] keys = config.GetKeys(); 332 foreach (string k in keys) 333 { 334 if (k.StartsWith("Define-")) 335 { 336 if (!m_defines.ContainsKey(k.Remove(0, 7))) 337 m_defines.Add(k.Remove(0, 7), config.GetString(k)); 338 } 339 else if (k.StartsWith("Include-")) 340 { 341 // read the config file to be included. 342 string file = config.GetString(k); 343 if (triedPaths.Contains(file)) 344 continue; 345 triedPaths.Add(file); 346 if (IsUri(file)) 347 { 348 if (!sources.Contains(file)) 349 { 350 cn++; 351 sources.Insert(cn, file); 352 } 353 } 354 else 355 { 356 // Resolve relative paths with wildcards 357 string chunkWithoutWildcards = file; 358 string chunkWithWildcards = string.Empty; 359 int wildcardIndex = file.IndexOfAny(new[] {'*', '?'}); 360 if (wildcardIndex != -1) 361 { 362 chunkWithoutWildcards = file.Substring(0, wildcardIndex); 363 chunkWithWildcards = file.Substring(wildcardIndex); 364 } 365 string path = Path.Combine(basePath, chunkWithoutWildcards + chunkWithWildcards); 366 List<string> paths = new List<string>(new string[1] {path}); 367 if (path.Contains("*")) 368 if (path.Contains("*.ini")) 369 { 370 paths.AddRange(Util.GetSubFiles(path)); 371 List<string> examplefiles = 372 new List<string>(Util.GetSubFiles(path.Replace(".ini", ".ini.example"))); 373#if (!ISWIN) 374 examplefiles.RemoveAll(delegate(string s) 375 { 376 return paths.Contains(s.Replace(".example", "")); 377 }); 378#else 379 examplefiles.RemoveAll( 380 s => paths.Contains(s.Replace(".example", ""))); 381#endif 382 paths.AddRange(examplefiles); 383 } 384 else 385 paths.AddRange(Util.GetSubFiles(path)); 386#if (!ISWIN) 387 foreach (string p in paths) 388 { 389 if (!sources.Contains(p)) 390 { 391 cn++; 392 sources.Insert(cn, p); 393 } 394 } 395#else 396 foreach (string p in paths.Where(p => !sources.Contains(p))) 397 { 398 cn++; 399 sources.Insert(cn, p); 400 } 401#endif 402 } 403 } 404 else if (k.StartsWith("RemoveInclude-")) 405 { 406 // read the config file to be included. 407 string file = config.GetString(k); 408 if (triedPaths.Contains(file)) 409 continue; 410 triedPaths.Add(file); 411 if (IsUri(file)) 412 { 413 if (!sources.Contains(file)) 414 { 415 cn--; 416 sources.Remove(file); 417 } 418 } 419 else 420 { 421 // Resolve relative paths with wildcards 422 string chunkWithoutWildcards = file; 423 string chunkWithWildcards = string.Empty; 424 int wildcardIndex = file.IndexOfAny(new[] {'*', '?'}); 425 if (wildcardIndex != -1) 426 { 427 chunkWithoutWildcards = file.Substring(0, wildcardIndex); 428 chunkWithWildcards = file.Substring(wildcardIndex); 429 } 430 string path = Path.Combine(basePath, chunkWithoutWildcards + chunkWithWildcards); 431 string[] paths = new string[1] {path}; 432 if (path.Contains("*")) 433 paths = Util.GetSubFiles(path); 434#if (!ISWIN) 435 foreach (string p in paths) 436 { 437 if (!sources.Contains(p)) 438 { 439 cn--; 440 sources.Remove(p); 441 } 442 } 443#else 444 foreach (string p in paths.Where(p => !sources.Contains(p))) 445 { 446 cn--; 447 sources.Remove(p); 448 } 449#endif 450 } 451 } 452 } 453 } 454 } 455 456 /// <summary> 457 /// Check if we can convert the string to a URI 458 /// </summary> 459 /// <param name = "file">String uri to the remote resource</param> 460 /// <returns>true if we can convert the string to a Uri object</returns> 461 private bool IsUri(string file) 462 { 463 Uri configUri; 464 465 return Uri.TryCreate(file, UriKind.Absolute, 466 out configUri) && configUri.Scheme == Uri.UriSchemeHttp; 467 } 468 469 /// <summary> 470 /// Provide same ini loader functionality for standard ini and master ini - file system or XML over http 471 /// </summary> 472 /// <param name = "iniPath">Full path to the ini</param> 473 /// <returns></returns> 474 private bool ReadConfig(string iniPath, int i, IConfigSource source) 475 { 476 bool success = false; 477 478 if (!IsUri(iniPath)) 479 { 480 if (showIniLoading) 481 Console.WriteLine(string.Format("[CONFIG]: Reading configuration file {0}", Util.BasePathCombine(iniPath))); 482 483 source.Merge(new IniConfigSource(iniPath, IniFileType.AuroraStyle)); 484 if (inidbg) 485 { 486 WriteConfigFile(i, source); 487 } 488 success = true; 489 } 490 else 491 { 492 // The ini file path is a http URI 493 // Try to read it 494 try 495 { 496 string file = Utilities.ReadExternalWebsite(iniPath); 497 string filename = Path.GetTempFileName(); 498 File.WriteAllText(filename, file); 499 500 if (showIniLoading) 501 Console.WriteLine(string.Format("[CONFIG]: Reading configuration file {0}", Util.BasePathCombine(iniPath))); 502 503 source.Merge(new IniConfigSource(filename, IniFileType.AuroraStyle)); 504 if (inidbg) 505 { 506 WriteConfigFile(i, source); 507 } 508 File.Delete(filename); 509 success = true; 510 } 511 catch (Exception e) 512 { 513 Console.WriteLine(string.Format("[CONFIG]: Exception reading config from URI {0}\n" + e, iniPath)); 514 Environment.Exit(1); 515 } 516 } 517 return success; 518 } 519 520 private void WriteConfigFile(int i, IConfigSource m_config) 521 { 522 string m_fileName = "ConfigFileDump" + i + ".ini"; 523 Console.WriteLine(string.Format("Writing config dump file to " + m_fileName)); 524 try 525 { 526 //Add the user 527 FileStream stream = new FileStream(m_fileName, FileMode.Create); 528 StreamWriter m_streamWriter = new StreamWriter(stream); 529 m_streamWriter.BaseStream.Position += m_streamWriter.BaseStream.Length; 530 m_streamWriter.WriteLine(m_config.ToString()); 531 m_streamWriter.Close(); 532 } 533 catch 534 { 535 } 536 } 537 } 538}