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