2 // CustomizableLocalFileSettingsProvider.cs
5 // Noriaki Okimoto <seara@ojk.sppd.ne.jp>
6 // Atsushi Enomoto <atsushi@ximian.com>
8 // (C)2007 Noriaki Okimoto
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 #if NET_2_0 && CONFIGURATION_DEP
33 extern alias PrebuiltSystem;
37 using System.Collections;
38 using System.Collections.Generic;
39 using System.Configuration;
41 using System.Reflection;
42 using System.Security.Cryptography;
47 using NameValueCollection = System.Collections.Specialized.NameValueCollection;
49 using NameValueCollection = PrebuiltSystem.System.Collections.Specialized.NameValueCollection;
52 namespace System.Configuration
54 // location to store user configuration settings.
55 internal enum UserConfigLocationOption : uint
58 Product_VersionMajor = 0x21,
59 Product_VersionMinor = 0x22,
60 Product_VersionBuild = 0x24,
61 Product_VersionRevision = 0x28,
62 Company_Product = 0x30,
63 Company_Product_VersionMajor = 0x31,
64 Company_Product_VersionMinor = 0x32,
65 Company_Product_VersionBuild = 0x34,
66 Company_Product_VersionRevision = 0x38,
70 internal class CustomizableFileSettingsProvider : SettingsProvider, IApplicationSettingsProvider
72 private static string userRoamingPath = "";
73 private static string userLocalPath = "";
74 private static string userRoamingPathPrevVersion = "";
75 private static string userLocalPathPrevVersion = "";
76 private static string userRoamingName = "user.config";
77 private static string userLocalName = "user.config";
78 private static string userRoamingBasePath = "";
79 private static string userLocalBasePath = "";
80 private static string CompanyName = "";
81 private static string ProductName = "";
82 private static string ForceVersion = "";
83 private static string[] ProductVersion;
85 // whether to include parts in the folder name or not:
86 private static bool isVersionMajor = false; // 0x0001 major version
87 private static bool isVersionMinor = false; // 0x0002 minor version
88 private static bool isVersionBuild = false; // 0x0004 build version
89 private static bool isVersionRevision = false; // 0x0008 revision
90 private static bool isCompany = true; // 0x0010 corporate name
91 private static bool isProduct = true; // 0x0020 product name
93 private static bool userDefine = false; // 0x8000 ignore all above and use user definition
95 private static UserConfigLocationOption userConfig = UserConfigLocationOption.Company_Product;
97 public override void Initialize (string name, NameValueCollection config)
99 base.Initialize (name, config);
102 // full path to roaming user.config
103 internal static string UserRoamingFullPath {
104 get { return Path.Combine (userRoamingPath, userRoamingName); }
107 // full path to local user.config
108 internal static string UserLocalFullPath {
109 get { return Path.Combine (userLocalPath, userLocalName); }
112 // previous full path to roaming user.config
113 public static string PrevUserRoamingFullPath {
114 get { return Path.Combine (userRoamingPathPrevVersion, userRoamingName); }
117 // previous full path to local user.config
118 public static string PrevUserLocalFullPath {
119 get { return Path.Combine (userLocalPathPrevVersion, userLocalName); }
122 // path to roaming user.config
123 public static string UserRoamingPath {
124 get { return userRoamingPath; }
127 // path to local user.config
128 public static string UserLocalPath {
129 get { return userLocalPath; }
132 // file name which is equivalent to user.config, for roaming user
133 public static string UserRoamingName {
134 get { return userRoamingName; }
137 // file name which is equivalent to user.config, for local user
138 public static string UserLocalName {
139 get { return userLocalName; }
142 public static UserConfigLocationOption UserConfigSelector
144 get { return userConfig; }
148 if (((uint) userConfig & 0x8000) != 0) {
149 isVersionMajor = false;
150 isVersionMinor = false;
151 isVersionBuild = false;
152 isVersionRevision = false;
157 isVersionRevision = ((uint) userConfig & 0x0008) != 0;
158 isVersionBuild = isVersionRevision | ((uint)userConfig & 0x0004) != 0;
159 isVersionMinor = isVersionBuild | ((uint)userConfig & 0x0002) != 0;
160 isVersionMajor = IsVersionMinor | ((uint)userConfig & 0x0001) != 0;
162 isCompany = ((uint) userConfig & 0x0010) != 0;
163 isProduct = ((uint) userConfig & 0x0020) != 0;
167 // whether the path to include the major version.
168 public static bool IsVersionMajor
170 get { return isVersionMajor; }
173 isVersionMajor = value;
174 isVersionMinor = false;
175 isVersionBuild = false;
176 isVersionRevision = false;
180 // whether the path to include minor version.
181 public static bool IsVersionMinor
183 get { return isVersionMinor; }
186 isVersionMinor = value;
188 isVersionMajor = true;
189 isVersionBuild = false;
190 isVersionRevision = false;
194 // whether the path to include build version.
195 public static bool IsVersionBuild
197 get { return isVersionBuild; }
200 isVersionBuild = value;
201 if (isVersionBuild) {
202 isVersionMajor = true;
203 isVersionMinor = true;
205 isVersionRevision = false;
209 // whether the path to include revision.
210 public static bool IsVersionRevision
212 get { return isVersionRevision; }
215 isVersionRevision = value;
216 if (isVersionRevision) {
217 isVersionMajor = true;
218 isVersionMinor = true;
219 isVersionBuild = true;
224 // whether the path to include company name.
225 public static bool IsCompany
227 get { return isCompany; }
228 set { isCompany = value; }
231 // AssemblyCompanyAttribute->Namespace->"Program"
232 private static string GetCompanyName ()
234 Assembly assembly = Assembly.GetEntryAssembly ();
235 if (assembly == null)
236 assembly = Assembly.GetCallingAssembly ();
238 AssemblyCompanyAttribute [] attrs = (AssemblyCompanyAttribute []) assembly.GetCustomAttributes (typeof (AssemblyCompanyAttribute), true);
240 if ((attrs != null) && attrs.Length > 0) {
241 return attrs [0].Company;
245 MethodInfo entryPoint = assembly.EntryPoint;
246 Type entryType = entryPoint != null ? entryPoint.DeclaringType : null;
247 if (entryType != null && !String.IsNullOrEmpty (entryType.Namespace)) {
248 int end = entryType.Namespace.IndexOf ('.');
249 return end < 0 ? entryType.Namespace : entryType.Namespace.Substring (0, end);
253 return assembly.GetName ().Name;
257 private static string GetProductName ()
259 Assembly assembly = Assembly.GetEntryAssembly ();
260 if (assembly == null)
261 assembly = Assembly.GetCallingAssembly ();
265 byte [] pkt = assembly.GetName ().GetPublicKeyToken ();
266 byte [] hash = SHA1.Create ().ComputeHash (pkt != null ? pkt : Encoding.UTF8.GetBytes (assembly.EscapedCodeBase));
267 return String.Format ("{0}_{1}_{2}",
268 AppDomain.CurrentDomain.FriendlyName,
269 pkt != null ? "StrongName" : "Url",
270 // FIXME: it seems that something else is used
271 // here, to convert hash bytes to string.
272 Convert.ToBase64String (hash));
274 #else // AssemblyProductAttribute-based code
275 AssemblyProductAttribute [] attrs = (AssemblyProductAttribute[]) assembly.GetCustomAttributes (typeof (AssemblyProductAttribute), true);
277 if ((attrs != null) && attrs.Length > 0) {
278 return attrs [0].Product;
280 return assembly.GetName ().Name;
284 private static string GetProductVersion ()
286 Assembly assembly = Assembly.GetEntryAssembly ();
287 if (assembly == null)
288 assembly = Assembly.GetCallingAssembly ();
289 if (assembly == null)
292 return assembly.GetName ().Version.ToString ();
295 private static void CreateUserConfigPath ()
300 if (ProductName == "")
301 ProductName = GetProductName ();
302 if (CompanyName == "")
303 CompanyName = GetCompanyName ();
304 if (ForceVersion == "")
305 ProductVersion = GetProductVersion ().Split('.');
307 // C:\Documents and Settings\(user)\Application Data
309 if (userRoamingBasePath == "")
310 userRoamingPath = Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData);
313 userRoamingPath = userRoamingBasePath;
315 // C:\Documents and Settings\(user)\Local Settings\Application Data (on Windows)
317 if (userLocalBasePath == "")
318 userLocalPath = Environment.GetFolderPath (Environment.SpecialFolder.LocalApplicationData);
321 userLocalPath = userLocalBasePath;
324 userRoamingPath = Path.Combine (userRoamingPath, CompanyName);
325 userLocalPath = Path.Combine (userLocalPath, CompanyName);
329 userRoamingPath = Path.Combine (userRoamingPath, ProductName);
330 userLocalPath = Path.Combine (userLocalPath, ProductName);
335 if (ForceVersion == "") {
336 if (isVersionRevision)
337 versionName = String.Format ("{0}.{1}.{2}.{3}", ProductVersion [0], ProductVersion [1], ProductVersion [2], ProductVersion [3]);
338 else if (isVersionBuild)
339 versionName = String.Format ("{0}.{1}.{2}", ProductVersion [0], ProductVersion [1], ProductVersion [2]);
340 else if (isVersionMinor)
341 versionName = String.Format ("{0}.{1}", ProductVersion [0], ProductVersion [1]);
342 else if (isVersionMajor)
343 versionName = ProductVersion [0];
348 versionName = ForceVersion;
350 string prevVersionRoaming = PrevVersionPath (userRoamingPath, versionName);
351 string prevVersionLocal = PrevVersionPath (userLocalPath, versionName);
353 userRoamingPath = Path.Combine (userRoamingPath, versionName);
354 userLocalPath = Path.Combine (userLocalPath, versionName);
355 if (prevVersionRoaming != "")
356 userRoamingPathPrevVersion = Path.Combine(userRoamingPath, prevVersionRoaming);
357 if (prevVersionLocal != "")
358 userLocalPathPrevVersion = Path.Combine(userLocalPath, prevVersionLocal);
361 // string for the previous version. It ignores newer ones.
362 private static string PrevVersionPath (string dirName, string currentVersion)
364 string prevVersionString = "";
366 if (!Directory.Exists(dirName))
367 return prevVersionString;
368 DirectoryInfo currentDir = new DirectoryInfo (dirName);
369 foreach (DirectoryInfo dirInfo in currentDir.GetDirectories ())
370 if (String.Compare (currentVersion, dirInfo.Name, StringComparison.Ordinal) > 0)
371 if (String.Compare (prevVersionString, dirInfo.Name, StringComparison.Ordinal) < 0)
372 prevVersionString = dirInfo.Name;
374 return prevVersionString;
377 // sets the explicit path to store roaming user.config or equivalent.
378 // (returns the path validity.)
379 public static bool SetUserRoamingPath (string configPath)
381 if (CheckPath (configPath))
383 userRoamingBasePath = configPath;
390 // sets the explicit path to store local user.config or equivalent.
391 // (returns the path validity.)
392 public static bool SetUserLocalPath (string configPath)
394 if (CheckPath (configPath))
396 userLocalBasePath = configPath;
403 private static bool CheckFileName (string configFile)
406 char[] invalidFileChars = Path.GetInvalidFileNameChars();
408 foreach (char invalidChar in invalidFileChars)
410 if (configFile.Contains(invalidChar.ToString()))
417 return configFile.IndexOfAny (Path.GetInvalidFileNameChars ()) < 0;
420 // sets the explicit roaming file name which is user.config equivalent.
421 // (returns the file name validity.)
422 public static bool SetUserRoamingFileName (string configFile)
424 if (CheckFileName (configFile))
426 userRoamingName = configFile;
433 // sets the explicit local file name which is user.config equivalent.
434 // (returns the file name validity.)
435 public static bool SetUserLocalFileName (string configFile)
437 if (CheckFileName (configFile))
439 userLocalName = configFile;
446 // sets the explicit company name for folder.
447 // (returns the file name validity.)
448 public static bool SetCompanyName (string companyName)
450 if (CheckFileName (companyName))
452 CompanyName = companyName;
459 // sets the explicit product name for folder.
460 // (returns the file name validity.)
461 public static bool SetProductName (string productName)
463 if (CheckFileName (productName))
465 ProductName = productName;
472 // sets the explicit major version for folder.
473 public static bool SetVersion (int major)
475 ForceVersion = string.Format ("{0}", major);
479 // sets the explicit major and minor versions for folder.
480 public static bool SetVersion (int major, int minor)
482 ForceVersion = string.Format ("{0}.{1}", major, minor);
486 // sets the explicit major/minor/build numbers for folder.
487 public static bool SetVersion (int major, int minor, int build)
489 ForceVersion = string.Format ("{0}.{1}.{2}", major, minor, build);
493 // sets the explicit major/minor/build/revision numbers for folder.
494 public static bool SetVersion (int major, int minor, int build, int revision)
496 ForceVersion = string.Format ("{0}.{1}.{2}.{3}", major, minor, build, revision);
500 // sets the explicit version number string for folder.
501 public static bool SetVersion (string forceVersion)
503 if (CheckFileName (forceVersion))
505 ForceVersion = forceVersion;
512 private static bool CheckPath (string configPath)
514 char[] invalidPathChars = Path.GetInvalidPathChars ();
517 foreach (char invalidChar in invalidPathChars)
519 if (configPath.Contains (invalidChar.ToString()))
525 if (configPath.IndexOfAny (invalidPathChars) >= 0)
528 string folder = configPath;
530 while ((fileName = Path.GetFileName (folder)) != "")
532 if (!CheckFileName (fileName))
536 folder = Path.GetDirectoryName (folder);
543 public override string Name {
544 get { return base.Name; }
547 string app_name = String.Empty;//"OJK.CustomSetting.CustomizableLocalFileSettingsProvider";
548 public override string ApplicationName {
549 get { return app_name; }
550 set { app_name = value; }
553 private ExeConfigurationFileMap exeMapCurrent = null;
554 private ExeConfigurationFileMap exeMapPrev = null;
555 private SettingsPropertyValueCollection values = null;
557 private void SaveProperties (ExeConfigurationFileMap exeMap, SettingsPropertyValueCollection collection, ConfigurationUserLevel level, SettingsContext context, bool checkUserLevel)
559 Configuration config = ConfigurationManager.OpenMappedExeConfiguration (exeMap, level);
561 UserSettingsGroup userGroup = config.GetSectionGroup ("userSettings") as UserSettingsGroup;
562 bool isRoaming = (level == ConfigurationUserLevel.PerUserRoaming);
564 #if true // my reimplementation
566 if (userGroup == null) {
567 userGroup = new UserSettingsGroup ();
568 config.SectionGroups.Add ("userSettings", userGroup);
569 ApplicationSettingsBase asb = context.CurrentSettings;
570 ClientSettingsSection cs = new ClientSettingsSection ();
571 userGroup.Sections.Add (asb.GetType ().FullName, cs);
574 bool hasChanges = false;
576 foreach (ConfigurationSection section in userGroup.Sections) {
577 ClientSettingsSection userSection = section as ClientSettingsSection;
578 if (userSection == null)
581 XmlDocument doc = new XmlDocument ();
583 foreach (SettingsPropertyValue value in collection) {
584 if (checkUserLevel && value.Property.Attributes.Contains (typeof (SettingsManageabilityAttribute)) != isRoaming)
587 SettingElement element = userSection.Settings.Get (value.Name);
588 if (element == null) {
589 element = new SettingElement (value.Name, value.Property.SerializeAs);
590 userSection.Settings.Add (element);
592 if (element.Value.ValueXml == null)
593 element.Value.ValueXml = new XmlDocument ().CreateDocumentFragment ();
594 doc = element.Value.ValueXml.OwnerDocument;
595 switch (value.Property.SerializeAs) {
596 case SettingsSerializeAs.Xml:
597 element.Value.ValueXml.InnerXml = value.SerializedValue as string;
599 case SettingsSerializeAs.String:
600 element.Value.ValueXml.InnerText = value.SerializedValue as string;
602 case SettingsSerializeAs.Binary:
603 element.Value.ValueXml.InnerText = Convert.ToBase64String (value.SerializedValue as byte []);
606 throw new NotImplementedException ();
611 config.Save (ConfigurationSaveMode.Minimal, true);
613 #else // original impl. - likely buggy to miss some properties to save
615 foreach (ConfigurationSection configSection in userGroup.Sections)
617 ClientSettingsSection userSection = configSection as ClientSettingsSection;
618 if (userSection != null)
621 userSection.Settings.Clear();
623 foreach (SettingsPropertyValue propertyValue in collection)
625 if (propertyValue.IsDirty)
627 SettingElement element = new SettingElement(propertyValue.Name, SettingsSerializeAs.String);
628 element.Value.ValueXml = new XmlDocument();
629 element.Value.ValueXml.InnerXml = (string)propertyValue.SerializedValue;
630 userSection.Settings.Add(element);
634 foreach (SettingElement element in userSection.Settings)
636 if (collection [element.Name] != null) {
637 if (collection [element.Name].Property.Attributes.Contains (typeof (SettingsManageabilityAttribute)) != isRoaming)
640 element.SerializeAs = SettingsSerializeAs.String;
641 element.Value.ValueXml.InnerXml = (string) collection [element.Name].SerializedValue; ///Value = XmlElement
647 config.Save (ConfigurationSaveMode.Minimal, true);
651 private void LoadPropertyValue (SettingsPropertyCollection collection, SettingElement element, bool allowOverwrite)
653 SettingsProperty prop = collection [element.Name];
654 SettingsPropertyValue value = new SettingsPropertyValue (prop);
655 value.IsDirty = false;
656 value.SerializedValue = element.Value.ValueXml != null ? element.Value.ValueXml.InnerText : prop.DefaultValue;
660 values.Remove (element.Name);
662 } catch (ArgumentException) {
663 throw new ConfigurationErrorsException ();
667 private void LoadProperies (ExeConfigurationFileMap exeMap, SettingsPropertyCollection collection, ConfigurationUserLevel level, string sectionGroupName, bool allowOverwrite)
669 Configuration config = ConfigurationManager.OpenMappedExeConfiguration (exeMap,level);
671 ConfigurationSectionGroup sectionGroup = config.GetSectionGroup (sectionGroupName);
672 if (sectionGroup != null) {
673 foreach (ConfigurationSection configSection in sectionGroup.Sections) {
674 ClientSettingsSection clientSection = configSection as ClientSettingsSection;
675 if (clientSection != null)
676 foreach (SettingElement element in clientSection.Settings)
677 LoadPropertyValue(collection, element, allowOverwrite);
683 public override void SetPropertyValues (SettingsContext context, SettingsPropertyValueCollection collection)
687 if (UserLocalFullPath == UserRoamingFullPath)
689 SaveProperties (exeMapCurrent, collection, ConfigurationUserLevel.PerUserRoaming, context, false);
691 SaveProperties (exeMapCurrent, collection, ConfigurationUserLevel.PerUserRoaming, context, true);
692 SaveProperties (exeMapCurrent, collection, ConfigurationUserLevel.PerUserRoamingAndLocal, context, true);
696 public override SettingsPropertyValueCollection GetPropertyValues (SettingsContext context, SettingsPropertyCollection collection)
700 if (values == null) {
701 values = new SettingsPropertyValueCollection ();
702 LoadProperies (exeMapCurrent, collection, ConfigurationUserLevel.None, "applicationSettings", false);
703 LoadProperies (exeMapCurrent, collection, ConfigurationUserLevel.None, "userSettings", false);
705 LoadProperies (exeMapCurrent, collection, ConfigurationUserLevel.PerUserRoaming, "userSettings", true);
706 LoadProperies (exeMapCurrent, collection, ConfigurationUserLevel.PerUserRoamingAndLocal, "userSettings", true);
708 // create default values if not exist
709 foreach (SettingsProperty p in collection)
710 if (values [p.Name] == null)
711 values.Add (new SettingsPropertyValue (p));
716 /// creates an ExeConfigurationFileMap
717 private void CreateExeMap ()
719 if (exeMapCurrent == null) {
720 CreateUserConfigPath ();
723 exeMapCurrent = new ExeConfigurationFileMap ();
724 // exeMapCurrent.ExeConfigFilename = System.Windows.Forms.Application.ExecutablePath + ".config";
725 Assembly entry = Assembly.GetEntryAssembly () ?? Assembly.GetExecutingAssembly ();
726 exeMapCurrent.ExeConfigFilename = entry.Location + ".config";
727 exeMapCurrent.LocalUserConfigFilename = UserLocalFullPath;
728 exeMapCurrent.RoamingUserConfigFilename = UserRoamingFullPath;
731 if ((PrevUserLocalFullPath != "") && (PrevUserRoamingFullPath != ""))
733 exeMapPrev = new ExeConfigurationFileMap();
734 // exeMapPrev.ExeConfigFilename = System.Windows.Forms.Application.ExecutablePath + ".config";
735 exeMapPrev.ExeConfigFilename = entry.Location + ".config";
736 exeMapPrev.LocalUserConfigFilename = PrevUserLocalFullPath;
737 exeMapPrev.RoamingUserConfigFilename = PrevUserRoamingFullPath;
743 public SettingsPropertyValue GetPreviousVersion (SettingsContext context, SettingsProperty property)
748 public void Reset (SettingsContext context)
751 foreach (SettingsPropertyValue propertyValue in values) {
752 propertyValue.PropertyValue = propertyValue.Property.DefaultValue;
753 propertyValue.IsDirty = true;
755 SetPropertyValues (context, values);
759 public void Upgrade (SettingsContext context, SettingsPropertyCollection properties)
763 public static void setCreate ()
765 CreateUserConfigPath();