Merge pull request #1573 from akoeplinger/msbuild-import-empty
[mono.git] / mcs / class / System / System.Configuration / CustomizableFileSettingsProvider.cs
1 //
2 // CustomizableFileSettingsProvider.cs
3 //
4 // Authors:
5 //      Noriaki Okimoto  <seara@ojk.sppd.ne.jp>
6 //      Atsushi Enomoto  <atsushi@ximian.com>
7 //
8 // (C)2007 Noriaki Okimoto
9 //
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:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
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.
28 //
29
30 #if CONFIGURATION_DEP
31
32 extern alias PrebuiltSystem;
33
34 using System;
35 using System.Collections;
36 using System.Collections.Generic;
37 using System.Configuration;
38 using System.Globalization;
39 using System.IO;
40 using System.Reflection;
41 using System.Security.Cryptography;
42 using System.Text;
43 using System.Xml;
44
45 using NameValueCollection = PrebuiltSystem.System.Collections.Specialized.NameValueCollection;
46
47 namespace System.Configuration
48 {
49         // location to store user configuration settings.
50         internal enum UserConfigLocationOption : uint
51         {
52                 Product = 0x20,
53                 Product_VersionMajor = 0x21,
54                 Product_VersionMinor = 0x22,
55                 Product_VersionBuild = 0x24,
56                 Product_VersionRevision = 0x28,
57                 Company_Product = 0x30,
58                 Company_Product_VersionMajor = 0x31,
59                 Company_Product_VersionMinor = 0x32,
60                 Company_Product_VersionBuild = 0x34,
61                 Company_Product_VersionRevision = 0x38,
62                 Evidence = 0x40,
63                 Other = 0x8000
64         }
65         
66         internal class CustomizableFileSettingsProvider : SettingsProvider, IApplicationSettingsProvider
67         {
68                 // KLUDGE WARNING.
69                 //
70                 // This is used from within System.Web to allow mapping of the ExeConfigFilename to
71                 // the correct Web.config for the current request. Otherwise web applications will
72                 // not be able to access settings from Web.config. The type assigned to this
73                 // variable must descend from the ConfigurationFileMap class and its
74                 // MachineConfigFilename will be used to set the ExeConfigFilename.
75                 //
76                 // This is necessary to fix bug #491531
77 #pragma warning disable 649
78                 private static Type webConfigurationFileMapType;
79 #pragma warning restore 649
80                 
81                 private static string userRoamingPath = "";
82                 private static string userLocalPath = "";
83                 private static string userRoamingPathPrevVersion = "";
84                 private static string userLocalPathPrevVersion = "";
85                 private static string userRoamingName = "user.config";
86                 private static string userLocalName = "user.config";
87                 private static string userRoamingBasePath = "";
88                 private static string userLocalBasePath = "";
89                 private static string CompanyName = "";
90                 private static string ProductName = "";
91                 private static string ForceVersion = "";
92                 private static string[] ProductVersion;
93
94                 // whether to include parts in the folder name or not:
95                 private static bool isVersionMajor = false;     // 0x0001       major version
96                 private static bool isVersionMinor = false;     // 0x0002       minor version
97                 private static bool isVersionBuild = false;     // 0x0004       build version
98                 private static bool isVersionRevision = false;  // 0x0008       revision
99                 private static bool isCompany = true;           // 0x0010       corporate name
100                 private static bool isProduct = true;           // 0x0020       product name
101                 private static bool isEvidence = false;         // 0x0040       evidence hash
102
103                 private static bool userDefine = false;         // 0x8000       ignore all above and use user definition
104
105                 private static UserConfigLocationOption userConfig = UserConfigLocationOption.Company_Product;
106
107                 public override void Initialize (string name, NameValueCollection config)
108                 {
109                         base.Initialize (name, config);
110                 }
111
112                 // full path to roaming user.config
113                 internal static string UserRoamingFullPath {
114                         get { return Path.Combine (userRoamingPath, userRoamingName); }
115                 }
116
117                 // full path to local user.config
118                 internal static string UserLocalFullPath {
119                         get { return Path.Combine (userLocalPath, userLocalName); }
120                 }
121
122                 // previous full path to roaming user.config
123                 public static string PrevUserRoamingFullPath {
124                         get { return Path.Combine (userRoamingPathPrevVersion, userRoamingName); }
125                 }
126
127                 // previous full path to local user.config
128                 public static string PrevUserLocalFullPath {
129                         get { return Path.Combine (userLocalPathPrevVersion, userLocalName); }
130                 }
131
132                 // path to roaming user.config
133                 public static string UserRoamingPath {
134                         get { return userRoamingPath; }
135                 }
136
137                 // path to local user.config
138                 public static string UserLocalPath {
139                         get { return userLocalPath; }
140                 }
141
142                 // file name which is equivalent to user.config, for roaming user
143                 public static string UserRoamingName {
144                         get { return userRoamingName; }
145                 }
146
147                 // file name which is equivalent to user.config, for local user
148                 public static string UserLocalName {
149                         get { return userLocalName; }
150                 }
151
152                 public static UserConfigLocationOption UserConfigSelector
153                 {
154                         get { return userConfig; }
155                         set {
156                                 userConfig = value;
157
158                                 if (((uint) userConfig & 0x8000) != 0) {
159                                         isVersionMajor = false;
160                                         isVersionMinor = false;
161                                         isVersionBuild = false;
162                                         isVersionRevision = false;
163                                         isCompany = false;
164                                         return;
165                                 }
166
167                                 isVersionRevision = ((uint) userConfig & 0x0008) != 0;
168                                 isVersionBuild = isVersionRevision | ((uint)userConfig & 0x0004) != 0;
169                                 isVersionMinor = isVersionBuild | ((uint)userConfig & 0x0002) != 0;
170                                 isVersionMajor = IsVersionMinor | ((uint)userConfig & 0x0001) != 0;
171
172                                 isCompany = ((uint) userConfig & 0x0010) != 0;
173                                 isProduct = ((uint) userConfig & 0x0020) != 0;
174                         }
175                 }
176
177                 // whether the path to include the major version.
178                 public static bool IsVersionMajor
179                 {
180                         get { return isVersionMajor; }
181                         set
182                         {
183                                 isVersionMajor = value;
184                                 isVersionMinor = false;
185                                 isVersionBuild = false;
186                                 isVersionRevision = false;
187                         }
188                 }
189
190                 // whether the path to include minor version.
191                 public static bool IsVersionMinor
192                 {
193                         get { return isVersionMinor; }
194                         set
195                         {
196                                 isVersionMinor = value;
197                                 if (isVersionMinor)
198                                         isVersionMajor = true;
199                                 isVersionBuild = false;
200                                 isVersionRevision = false;
201                         }
202                 }
203
204                 // whether the path to include build version.
205                 public static bool IsVersionBuild
206                 {
207                         get { return isVersionBuild; }
208                         set
209                         {
210                                 isVersionBuild = value;
211                                 if (isVersionBuild) {
212                                         isVersionMajor = true;
213                                         isVersionMinor = true;
214                                 }
215                                 isVersionRevision = false;
216                         }
217                 }
218
219                 // whether the path to include revision.
220                 public static bool IsVersionRevision
221                 {
222                         get { return isVersionRevision; }
223                         set
224                         {
225                                 isVersionRevision = value;
226                                 if (isVersionRevision) {
227                                         isVersionMajor = true;
228                                         isVersionMinor = true;
229                                         isVersionBuild = true;
230                                 }
231                         }
232                 }
233
234                 // whether the path to include company name.
235                 public static bool IsCompany
236                 {
237                         get { return isCompany; }
238                         set { isCompany = value; }
239                 }
240
241                 // whether the path to include evidence hash.
242                 public static bool IsEvidence
243                 {
244                         get { return isEvidence; }
245                         set { isEvidence = value; }
246                 }
247
248                 // AssemblyCompanyAttribute->Namespace->"Program"
249                 private static string GetCompanyName ()
250                 {
251                         Assembly assembly = Assembly.GetEntryAssembly ();
252                         if (assembly == null)
253                                 assembly = Assembly.GetCallingAssembly ();
254
255                         AssemblyCompanyAttribute [] attrs = (AssemblyCompanyAttribute []) assembly.GetCustomAttributes (typeof (AssemblyCompanyAttribute), true);
256                 
257                         if ((attrs != null) && attrs.Length > 0) {
258                                 return attrs [0].Company;
259                         }
260
261                         MethodInfo entryPoint = assembly.EntryPoint;
262                         Type entryType = entryPoint != null ? entryPoint.DeclaringType : null;
263                         if (entryType != null && !String.IsNullOrEmpty (entryType.Namespace)) {
264                                 int end = entryType.Namespace.IndexOf ('.');
265                                 return end < 0 ? entryType.Namespace : entryType.Namespace.Substring (0, end);
266                         }
267                         return "Program";
268                 }
269
270                 private static string GetProductName ()
271                 {
272                         Assembly assembly = Assembly.GetEntryAssembly ();
273                         if (assembly == null)
274                                 assembly = Assembly.GetCallingAssembly ();
275
276                         byte [] pkt = assembly.GetName ().GetPublicKeyToken ();
277                         return String.Format ("{0}_{1}_{2}",
278                                 AppDomain.CurrentDomain.FriendlyName,
279                                 pkt != null && pkt.Length > 0 ? "StrongName" : "Url",
280                                 GetEvidenceHash());
281                 }
282
283                 // Note: Changed from base64() to hex output to avoid unexpected chars like '\' or '/' with filesystem meaning.
284                 //       Otherwise eventually filenames, which are invalid on linux or windows, might be created.
285                 // Signed-off-by:  Carsten Schlote <schlote@vahanus.net>
286                 // TODO: Compare with .NET. It might be also, that their way isn't suitable for Unix OS derivates (slahes in output)
287                 private static string GetEvidenceHash ()
288                 {
289                         Assembly assembly = Assembly.GetEntryAssembly ();
290                         if (assembly == null)
291                                 assembly = Assembly.GetCallingAssembly ();
292
293                         byte [] pkt = assembly.GetName ().GetPublicKeyToken ();
294                         byte [] hash = SHA1.Create ().ComputeHash (pkt != null && pkt.Length >0 ? pkt : Encoding.UTF8.GetBytes (assembly.EscapedCodeBase));
295                         System.Text.StringBuilder evidence_string = new System.Text.StringBuilder();
296                         foreach (byte b in hash)
297                                 evidence_string.AppendFormat("{0:x2}",b);
298                         return evidence_string.ToString ();
299                 }
300
301                 private static string GetProductVersion ()
302                 {
303                         Assembly assembly = Assembly.GetEntryAssembly ();
304                         if (assembly == null)
305                                 assembly = Assembly.GetCallingAssembly ();
306                         if (assembly == null)
307                                 return string.Empty;
308
309                         return assembly.GetName ().Version.ToString ();
310                 }
311
312                 private static void CreateUserConfigPath ()
313                 {
314                         if (userDefine)
315                                 return;
316
317                         if (ProductName == "")
318                                 ProductName = GetProductName ();
319                         if (CompanyName == "")
320                                 CompanyName = GetCompanyName ();
321                         if (ForceVersion == "")
322                                 ProductVersion = GetProductVersion ().Split('.');
323
324                         // C:\Documents and Settings\(user)\Application Data
325                         if (userRoamingBasePath == "")
326                                 userRoamingPath = Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData);
327                         else
328                                 userRoamingPath = userRoamingBasePath;
329
330                         // C:\Documents and Settings\(user)\Local Settings\Application Data (on Windows)
331                         if (userLocalBasePath == "")
332                                 userLocalPath = Environment.GetFolderPath (Environment.SpecialFolder.LocalApplicationData);
333                         else
334                                 userLocalPath = userLocalBasePath;
335
336                         if (isCompany) {
337                                 userRoamingPath = Path.Combine (userRoamingPath, CompanyName);
338                                 userLocalPath = Path.Combine (userLocalPath, CompanyName);
339                         }
340
341                         if (isProduct) {
342                                 if (isEvidence) {
343                                         Assembly assembly = Assembly.GetEntryAssembly ();
344                                         if (assembly == null)
345                                                 assembly = Assembly.GetCallingAssembly ();
346                                         byte [] pkt = assembly.GetName ().GetPublicKeyToken ();
347                                         ProductName = String.Format ("{0}_{1}_{2}",
348                                                 ProductName,
349                                                 pkt != null ? "StrongName" : "Url",
350                                                 GetEvidenceHash());
351                                 }
352                                 userRoamingPath = Path.Combine (userRoamingPath, ProductName);
353                                 userLocalPath = Path.Combine (userLocalPath, ProductName);
354                                 
355                         }
356
357                         string versionName;
358
359                         if (ForceVersion == "") {
360                                 if (isVersionRevision)
361                                         versionName = String.Format ("{0}.{1}.{2}.{3}", ProductVersion [0], ProductVersion [1], ProductVersion [2], ProductVersion [3]);
362                                 else if (isVersionBuild)
363                                         versionName = String.Format ("{0}.{1}.{2}", ProductVersion [0], ProductVersion [1], ProductVersion [2]);
364                                 else if (isVersionMinor)
365                                         versionName = String.Format ("{0}.{1}", ProductVersion [0], ProductVersion [1]);
366                                 else if (isVersionMajor)
367                                         versionName = ProductVersion [0];
368                                 else
369                                         versionName = "";
370                         }
371                         else
372                                 versionName = ForceVersion;
373
374                         string prevVersionRoaming = PrevVersionPath (userRoamingPath, versionName);
375                         string prevVersionLocal = PrevVersionPath (userLocalPath, versionName);
376                         
377                         userRoamingPath = Path.Combine (userRoamingPath, versionName);
378                         userLocalPath = Path.Combine (userLocalPath, versionName);
379                         if (prevVersionRoaming != "")
380                                 userRoamingPathPrevVersion = Path.Combine(userRoamingPath, prevVersionRoaming);
381                         if (prevVersionLocal != "")
382                                 userLocalPathPrevVersion = Path.Combine(userLocalPath, prevVersionLocal);
383                 }
384
385                 // string for the previous version. It ignores newer ones.
386                 private static string PrevVersionPath (string dirName, string currentVersion)
387                 {
388                         string prevVersionString = "";
389
390                         if (!Directory.Exists(dirName))
391                                 return prevVersionString;
392                         DirectoryInfo currentDir = new DirectoryInfo (dirName);
393                         foreach (DirectoryInfo dirInfo in currentDir.GetDirectories ())
394                                 if (String.Compare (currentVersion, dirInfo.Name, StringComparison.Ordinal) > 0)
395                                         if (String.Compare (prevVersionString, dirInfo.Name, StringComparison.Ordinal) < 0)
396                                                 prevVersionString = dirInfo.Name;
397
398                         return prevVersionString;
399                 }
400
401                 // sets the explicit path to store roaming user.config or equivalent.
402                 // (returns the path validity.)
403                 public static bool SetUserRoamingPath (string configPath)
404                 {
405                         if (CheckPath (configPath))
406                         {
407                                 userRoamingBasePath = configPath;
408                                 return true;
409                         }
410                         else
411                                 return false;
412                 }
413
414                 // sets the explicit path to store local user.config or equivalent.
415                 // (returns the path validity.)
416                 public static bool SetUserLocalPath (string configPath)
417                 {
418                         if (CheckPath (configPath))
419                         {
420                                 userLocalBasePath = configPath;
421                                 return true;
422                         }
423                         else
424                                 return false;
425                 }
426
427                 private static bool CheckFileName (string configFile)
428                 {
429                         /*
430                         char[] invalidFileChars = Path.GetInvalidFileNameChars();
431
432                         foreach (char invalidChar in invalidFileChars)
433                         {
434                                 if (configFile.Contains(invalidChar.ToString()))
435                                 {
436                                         return false;
437                                 }
438                         }
439                         return true;
440                         */
441                         return configFile.IndexOfAny (Path.GetInvalidFileNameChars ()) < 0;
442                 }
443
444                 // sets the explicit roaming file name which is user.config equivalent.
445                 // (returns the file name validity.)
446                 public static bool SetUserRoamingFileName (string configFile)
447                 {
448                         if (CheckFileName (configFile))
449                         {
450                                 userRoamingName = configFile;
451                                 return true;
452                         }
453                         else
454                                 return false;
455                 }
456
457                 // sets the explicit local file name which is user.config equivalent.
458                 // (returns the file name validity.)
459                 public static bool SetUserLocalFileName (string configFile)
460                 {
461                         if (CheckFileName (configFile))
462                         {
463                                 userLocalName = configFile;
464                                 return true;
465                         }
466                         else
467                                 return false;
468                 }
469
470                 // sets the explicit company name for folder.
471                 // (returns the file name validity.)
472                 public static bool SetCompanyName (string companyName)
473                 {
474                         if (CheckFileName (companyName))
475                         {
476                                 CompanyName = companyName;
477                                 return true;
478                         }
479                         else
480                                 return false;
481                 }
482
483                 // sets the explicit product name for folder.
484                 // (returns the file name validity.)
485                 public static bool SetProductName (string productName)
486                 {
487                         if (CheckFileName (productName))
488                         {
489                                 ProductName = productName;
490                                 return true;
491                         }
492                         else
493                                 return false;
494                 }
495
496                 // sets the explicit major version for folder.
497                 public static bool SetVersion (int major)
498                 {
499                         ForceVersion = string.Format ("{0}", major);
500                         return true;
501                 }
502
503                 // sets the explicit major and minor versions for folder.
504                 public static bool SetVersion (int major, int minor)
505                 {
506                         ForceVersion = string.Format ("{0}.{1}", major, minor);
507                         return true;
508                 }
509
510                 // sets the explicit major/minor/build numbers for folder.
511                 public static bool SetVersion (int major, int minor, int build)
512                 {
513                         ForceVersion = string.Format ("{0}.{1}.{2}", major, minor, build);
514                         return true;
515                 }
516
517                 // sets the explicit major/minor/build/revision numbers for folder.
518                 public static bool SetVersion (int major, int minor, int build, int revision)
519                 {
520                         ForceVersion = string.Format ("{0}.{1}.{2}.{3}", major, minor, build, revision);
521                         return true;
522                 }
523
524                 // sets the explicit version number string for folder.
525                 public static bool SetVersion (string forceVersion)
526                 {
527                         if (CheckFileName (forceVersion))
528                         {
529                                 ForceVersion = forceVersion;
530                                 return true;
531                         }
532                         else
533                                 return false;
534                 }
535
536                 private static bool CheckPath (string configPath)
537                 {
538                         char[] invalidPathChars = Path.GetInvalidPathChars ();
539
540                         /*
541                         foreach (char invalidChar in invalidPathChars)
542                         {
543                                 if (configPath.Contains (invalidChar.ToString()))
544                                 {
545                                         return false;
546                                 }
547                         }
548                         */
549                         if (configPath.IndexOfAny (invalidPathChars) >= 0)
550                                 return false;
551
552                         string folder = configPath;
553                         string fileName;
554                         while ((fileName = Path.GetFileName (folder)) != "")
555                         {
556                                 if (!CheckFileName (fileName))
557                                 {
558                                         return false;
559                                 }
560                                 folder = Path.GetDirectoryName (folder);
561                         }
562
563                         return true;
564                 }
565
566
567                 public override string Name {
568                         get { return base.Name; }
569                 }
570
571                 string app_name = String.Empty;//"OJK.CustomSetting.CustomizableLocalFileSettingsProvider";
572                 public override string ApplicationName {
573                         get { return app_name; }
574                         set { app_name = value; }
575                 }
576
577                 private ExeConfigurationFileMap exeMapCurrent = null;
578                 private ExeConfigurationFileMap exeMapPrev = null;
579                 private SettingsPropertyValueCollection values = null;
580
581                 private void SaveProperties (ExeConfigurationFileMap exeMap, SettingsPropertyValueCollection collection, ConfigurationUserLevel level, SettingsContext context, bool checkUserLevel)
582                 {
583                         Configuration config = ConfigurationManager.OpenMappedExeConfiguration (exeMap, level);
584                         
585                         UserSettingsGroup userGroup = config.GetSectionGroup ("userSettings") as UserSettingsGroup;
586                         bool isRoaming = (level == ConfigurationUserLevel.PerUserRoaming);
587
588 #if true // my reimplementation
589
590                         if (userGroup == null) {
591                                 userGroup = new UserSettingsGroup ();
592                                 config.SectionGroups.Add ("userSettings", userGroup);
593                         }
594                         ApplicationSettingsBase asb = context.CurrentSettings;
595                         string class_name = NormalizeInvalidXmlChars ((asb != null ? asb.GetType () : typeof (ApplicationSettingsBase)).FullName);
596                         ClientSettingsSection userSection = null;
597                         ConfigurationSection cnf = userGroup.Sections.Get (class_name);
598                         userSection = cnf as ClientSettingsSection;
599                         if (userSection == null) {
600                                 userSection = new ClientSettingsSection ();
601                                 userGroup.Sections.Add (class_name, userSection);
602                         }
603
604                         bool hasChanges = false;
605
606                         if (userSection == null)
607                                 return;
608
609                         foreach (SettingsPropertyValue value in collection) {
610                                 if (checkUserLevel && value.Property.Attributes.Contains (typeof (SettingsManageabilityAttribute)) != isRoaming)
611                                         continue;
612                                 // The default impl does not save the ApplicationScopedSetting properties
613                                 if (value.Property.Attributes.Contains (typeof (ApplicationScopedSettingAttribute)))
614                                         continue;
615
616                                 hasChanges = true;
617                                 SettingElement element = userSection.Settings.Get (value.Name);
618                                 if (element == null) {
619                                         element = new SettingElement (value.Name, value.Property.SerializeAs);
620                                         userSection.Settings.Add (element);
621                                 }
622                                 if (element.Value.ValueXml == null)
623                                         element.Value.ValueXml = new XmlDocument ().CreateElement ("value");
624                                 switch (value.Property.SerializeAs) {
625                                 case SettingsSerializeAs.Xml:
626                                         element.Value.ValueXml.InnerXml = (value.SerializedValue as string) ?? string.Empty;
627                                         break;
628                                 case SettingsSerializeAs.String:
629                                         element.Value.ValueXml.InnerText = value.SerializedValue as string;
630                                         break;
631                                 case SettingsSerializeAs.Binary:
632                                         element.Value.ValueXml.InnerText = value.SerializedValue != null ? Convert.ToBase64String (value.SerializedValue as byte []) : string.Empty;
633                                         break;
634                                 default:
635                                         throw new NotImplementedException ();
636                                 }
637                         }
638                         if (hasChanges)
639                                 config.Save (ConfigurationSaveMode.Minimal, true);
640
641 #else // original impl. - likely buggy to miss some properties to save
642
643                         foreach (ConfigurationSection configSection in userGroup.Sections)
644                         {
645                                 ClientSettingsSection userSection = configSection as ClientSettingsSection;
646                                 if (userSection != null)
647                                 {
648 /*
649                                         userSection.Settings.Clear();
650
651                                         foreach (SettingsPropertyValue propertyValue in collection)
652                                         {
653                                                 if (propertyValue.IsDirty)
654                                                 {
655                                                         SettingElement element = new SettingElement(propertyValue.Name, SettingsSerializeAs.String);
656                                                         element.Value.ValueXml = new XmlDocument();
657                                                         element.Value.ValueXml.InnerXml = (string)propertyValue.SerializedValue;
658                                                         userSection.Settings.Add(element);
659                                                 }
660                                         }
661 */
662                                         foreach (SettingElement element in userSection.Settings)
663                                         {
664                                                 if (collection [element.Name] != null) {
665                                                         if (collection [element.Name].Property.Attributes.Contains (typeof (SettingsManageabilityAttribute)) != isRoaming)
666                                                                 continue;
667
668                                                         element.SerializeAs = SettingsSerializeAs.String;
669                                                         element.Value.ValueXml.InnerXml = (string) collection [element.Name].SerializedValue;   ///Value = XmlElement
670                                                 }
671                                         }
672  
673                                 }
674                         }
675                         config.Save (ConfigurationSaveMode.Minimal, true);
676 #endif
677                 }
678
679                 // NOTE: We should add here all the chars that are valid in a name of a class (Ecma-wise),
680                 // but invalid in an xml element name, and provide a better impl if we get too many of them.
681                 string NormalizeInvalidXmlChars (string str)
682                 {
683                         char [] invalid_chars = new char [] { '+' };
684
685                         if (str == null || str.IndexOfAny (invalid_chars) == -1)
686                                 return str;
687
688                         // Replace with its hexadecimal values.
689                         str = str.Replace ("+", "_x002B_");
690                         return str;
691                 }
692
693                 private void LoadPropertyValue (SettingsPropertyCollection collection, SettingElement element, bool allowOverwrite)
694                 {
695                         SettingsProperty prop = collection [element.Name];
696                         if (prop == null) { // see bug #343459
697                                 prop = new SettingsProperty (element.Name);
698                                 collection.Add (prop);
699                         }
700
701                         SettingsPropertyValue value = new SettingsPropertyValue (prop);
702                         value.IsDirty = false;
703                         if (element.Value.ValueXml != null) {
704                                 switch (value.Property.SerializeAs) {
705                                 case SettingsSerializeAs.Xml:
706                                         value.SerializedValue = element.Value.ValueXml.InnerXml;
707                                         break;
708                                 case SettingsSerializeAs.String:
709                                         value.SerializedValue = element.Value.ValueXml.InnerText.Trim ();
710                                         break;
711                                 case SettingsSerializeAs.Binary:
712                                         value.SerializedValue = Convert.FromBase64String (element.Value.ValueXml.InnerText);
713                                         break;
714                                 }
715                         }
716                         else
717                                 value.SerializedValue = prop.DefaultValue;
718                         try
719                         {
720                                 if (allowOverwrite)
721                                         values.Remove (element.Name);
722                                 values.Add (value);
723                         } catch (ArgumentException ex) {
724                                 throw new ConfigurationErrorsException (string.Format (
725                                         CultureInfo.InvariantCulture,
726                                         "Failed to load value for '{0}'.",
727                                         element.Name), ex);
728                         }
729                 }
730
731                 private void LoadProperties (ExeConfigurationFileMap exeMap, SettingsPropertyCollection collection, ConfigurationUserLevel level, string sectionGroupName, bool allowOverwrite, string groupName)
732                 {
733                         Configuration config = ConfigurationManager.OpenMappedExeConfiguration (exeMap,level);
734                         
735                         ConfigurationSectionGroup sectionGroup = config.GetSectionGroup (sectionGroupName);
736                         if (sectionGroup != null) {
737                                 foreach (ConfigurationSection configSection in sectionGroup.Sections) {
738                                         if (configSection.SectionInformation.Name != groupName)
739                                                 continue;
740
741                                         ClientSettingsSection clientSection = configSection as ClientSettingsSection;
742                                         if (clientSection == null)
743                                                 continue;
744
745                                         foreach (SettingElement element in clientSection.Settings) {
746                                                 LoadPropertyValue(collection, element, allowOverwrite);
747                                         }
748                                         // Only the first one seems to be processed by MS
749                                         break;
750                                 }
751                         }
752
753                 }
754
755                 public override void SetPropertyValues (SettingsContext context, SettingsPropertyValueCollection collection)
756                 {
757                         CreateExeMap ();
758
759                         if (UserLocalFullPath == UserRoamingFullPath)
760                         {
761                                 SaveProperties (exeMapCurrent, collection, ConfigurationUserLevel.PerUserRoaming, context, false);
762                         } else {
763                                 SaveProperties (exeMapCurrent, collection, ConfigurationUserLevel.PerUserRoaming, context, true);
764                                 SaveProperties (exeMapCurrent, collection, ConfigurationUserLevel.PerUserRoamingAndLocal, context, true);
765                         }
766                 }
767
768                 public override SettingsPropertyValueCollection GetPropertyValues (SettingsContext context, SettingsPropertyCollection collection)
769                 {
770                         CreateExeMap ();
771
772                         values = new SettingsPropertyValueCollection ();
773                         string groupName = context ["GroupName"] as string;
774                         groupName = NormalizeInvalidXmlChars (groupName); // we likely saved the element removing the non valid xml chars.
775                         LoadProperties (exeMapCurrent, collection, ConfigurationUserLevel.None, "applicationSettings", false, groupName);
776                         LoadProperties (exeMapCurrent, collection, ConfigurationUserLevel.None, "userSettings", false, groupName);
777
778                         LoadProperties (exeMapCurrent, collection, ConfigurationUserLevel.PerUserRoaming, "userSettings", true, groupName);
779                         LoadProperties (exeMapCurrent, collection, ConfigurationUserLevel.PerUserRoamingAndLocal, "userSettings", true, groupName);
780
781                         // create default values if not exist
782                         foreach (SettingsProperty p in collection)
783                                 if (values [p.Name] == null)
784                                         values.Add (new SettingsPropertyValue (p));
785                         return values;
786                 }
787
788                 /// creates an ExeConfigurationFileMap
789                 private void CreateExeMap ()
790                 {
791                         if (exeMapCurrent == null) {
792                                 CreateUserConfigPath ();
793
794                                 // current version
795                                 exeMapCurrent = new ExeConfigurationFileMap ();
796                                 
797                                 // exeMapCurrent.ExeConfigFilename = System.Windows.Forms.Application.ExecutablePath + ".config";
798                                 Assembly entry = Assembly.GetEntryAssembly () ?? Assembly.GetExecutingAssembly ();
799                                 exeMapCurrent.ExeConfigFilename = entry.Location + ".config";
800                                 exeMapCurrent.LocalUserConfigFilename = UserLocalFullPath;
801                                 exeMapCurrent.RoamingUserConfigFilename = UserRoamingFullPath;
802
803                                 if (webConfigurationFileMapType != null && typeof (ConfigurationFileMap).IsAssignableFrom (webConfigurationFileMapType)) {
804                                         try {
805                                                 ConfigurationFileMap cfgFileMap = Activator.CreateInstance (webConfigurationFileMapType) as ConfigurationFileMap;
806                                                 if (cfgFileMap != null) {
807                                                         string fpath = cfgFileMap.MachineConfigFilename;
808                                                         if (!String.IsNullOrEmpty (fpath))
809                                                                 exeMapCurrent.ExeConfigFilename = fpath;
810                                                 }
811                                         } catch {
812                                                 // ignore
813                                         }
814                                 }
815                                 
816                                 // previous version
817                                 if ((PrevUserLocalFullPath != "") && (PrevUserRoamingFullPath != ""))
818                                 {
819                                         exeMapPrev = new ExeConfigurationFileMap();
820                                         // exeMapPrev.ExeConfigFilename = System.Windows.Forms.Application.ExecutablePath + ".config";
821                                         exeMapPrev.ExeConfigFilename = entry.Location + ".config";
822                                         exeMapPrev.LocalUserConfigFilename = PrevUserLocalFullPath;
823                                         exeMapPrev.RoamingUserConfigFilename = PrevUserRoamingFullPath;
824                                 }
825                         }
826                 }
827
828                 // FIXME: implement
829                 public SettingsPropertyValue GetPreviousVersion (SettingsContext context, SettingsProperty property)
830                 {
831                         return null;
832                 }
833
834                 public void Reset (SettingsContext context)
835                 {
836                         if (values != null) {
837                                 foreach (SettingsPropertyValue propertyValue in values) {
838                                         // Can't use propertyValue.Property.DefaultValue
839                                         // as it may cause InvalidCastException (see bug# 532180)
840                                         values[propertyValue.Name].PropertyValue = propertyValue.Reset ();
841                                 }
842                         }
843                 }
844
845                 // FIXME: implement
846                 public void Upgrade (SettingsContext context, SettingsPropertyCollection properties)
847                 {
848                 }
849
850                 public static void setCreate ()
851                 {
852                         CreateUserConfigPath();
853                 }
854         }
855 }
856
857 #endif