Use the right socket for the data stream
[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                         byte [] pkt = assembly.GetName ().GetPublicKeyToken ();
286                         return String.Format ("{0}_{1}_{2}",
287                                 AppDomain.CurrentDomain.FriendlyName,
288                                 pkt != null && pkt.Length > 0 ? "StrongName" : "Url",
289                                 GetEvidenceHash());
290 #else // AssemblyProductAttribute-based code
291                         AssemblyProductAttribute [] attrs = (AssemblyProductAttribute[]) assembly.GetCustomAttributes (typeof (AssemblyProductAttribute), true);
292                 
293                         if ((attrs != null) && attrs.Length > 0) {
294                                 return attrs [0].Product;
295                         }
296                         return assembly.GetName ().Name;
297 #endif
298                 }
299
300                 // Note: Changed from base64() to hex output to avoid unexpected chars like '\' or '/' with filesystem meaning.
301                 //       Otherwise eventually filenames, which are invalid on linux or windows, might be created.
302                 // Signed-off-by:  Carsten Schlote <schlote@vahanus.net>
303                 // TODO: Compare with .NET. It might be also, that their way isn't suitable for Unix OS derivates (slahes in output)
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.Length >0 ? pkt : Encoding.UTF8.GetBytes (assembly.EscapedCodeBase));
312                         System.Text.StringBuilder evidence_string = new System.Text.StringBuilder();
313                         foreach (byte b in hash)
314                                 evidence_string.AppendFormat("{0:x2}",b);
315                         return evidence_string.ToString ();
316                 }
317
318                 private static string GetProductVersion ()
319                 {
320                         Assembly assembly = Assembly.GetEntryAssembly ();
321                         if (assembly == null)
322                                 assembly = Assembly.GetCallingAssembly ();
323                         if (assembly == null)
324                                 return string.Empty;
325
326                         return assembly.GetName ().Version.ToString ();
327                 }
328
329                 private static void CreateUserConfigPath ()
330                 {
331                         if (userDefine)
332                                 return;
333
334                         if (ProductName == "")
335                                 ProductName = GetProductName ();
336                         if (CompanyName == "")
337                                 CompanyName = GetCompanyName ();
338                         if (ForceVersion == "")
339                                 ProductVersion = GetProductVersion ().Split('.');
340
341                         // C:\Documents and Settings\(user)\Application Data
342 #if !TARGET_JVM
343                         if (userRoamingBasePath == "")
344                                 userRoamingPath = Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData);
345                         else
346 #endif
347                                 userRoamingPath = userRoamingBasePath;
348
349                         // C:\Documents and Settings\(user)\Local Settings\Application Data (on Windows)
350 #if !TARGET_JVM
351                         if (userLocalBasePath == "")
352                                 userLocalPath = Environment.GetFolderPath (Environment.SpecialFolder.LocalApplicationData);
353                         else
354 #endif
355                                 userLocalPath = userLocalBasePath;
356
357                         if (isCompany) {
358                                 userRoamingPath = Path.Combine (userRoamingPath, CompanyName);
359                                 userLocalPath = Path.Combine (userLocalPath, CompanyName);
360                         }
361
362                         if (isProduct) {
363                                 if (isEvidence) {
364                                         Assembly assembly = Assembly.GetEntryAssembly ();
365                                         if (assembly == null)
366                                                 assembly = Assembly.GetCallingAssembly ();
367 #if !TARGET_JVM
368                                         byte [] pkt = assembly.GetName ().GetPublicKeyToken ();
369                                         ProductName = String.Format ("{0}_{1}_{2}",
370                                                 ProductName,
371                                                 pkt != null ? "StrongName" : "Url",
372                                                 GetEvidenceHash());
373 #endif
374                                 }
375                                 userRoamingPath = Path.Combine (userRoamingPath, ProductName);
376                                 userLocalPath = Path.Combine (userLocalPath, ProductName);
377                                 
378                         }
379
380                         string versionName;
381
382                         if (ForceVersion == "") {
383                                 if (isVersionRevision)
384                                         versionName = String.Format ("{0}.{1}.{2}.{3}", ProductVersion [0], ProductVersion [1], ProductVersion [2], ProductVersion [3]);
385                                 else if (isVersionBuild)
386                                         versionName = String.Format ("{0}.{1}.{2}", ProductVersion [0], ProductVersion [1], ProductVersion [2]);
387                                 else if (isVersionMinor)
388                                         versionName = String.Format ("{0}.{1}", ProductVersion [0], ProductVersion [1]);
389                                 else if (isVersionMajor)
390                                         versionName = ProductVersion [0];
391                                 else
392                                         versionName = "";
393                         }
394                         else
395                                 versionName = ForceVersion;
396
397                         string prevVersionRoaming = PrevVersionPath (userRoamingPath, versionName);
398                         string prevVersionLocal = PrevVersionPath (userLocalPath, versionName);
399                         
400                         userRoamingPath = Path.Combine (userRoamingPath, versionName);
401                         userLocalPath = Path.Combine (userLocalPath, versionName);
402                         if (prevVersionRoaming != "")
403                                 userRoamingPathPrevVersion = Path.Combine(userRoamingPath, prevVersionRoaming);
404                         if (prevVersionLocal != "")
405                                 userLocalPathPrevVersion = Path.Combine(userLocalPath, prevVersionLocal);
406                 }
407
408                 // string for the previous version. It ignores newer ones.
409                 private static string PrevVersionPath (string dirName, string currentVersion)
410                 {
411                         string prevVersionString = "";
412
413                         if (!Directory.Exists(dirName))
414                                 return prevVersionString;
415                         DirectoryInfo currentDir = new DirectoryInfo (dirName);
416                         foreach (DirectoryInfo dirInfo in currentDir.GetDirectories ())
417                                 if (String.Compare (currentVersion, dirInfo.Name, StringComparison.Ordinal) > 0)
418                                         if (String.Compare (prevVersionString, dirInfo.Name, StringComparison.Ordinal) < 0)
419                                                 prevVersionString = dirInfo.Name;
420
421                         return prevVersionString;
422                 }
423
424                 // sets the explicit path to store roaming user.config or equivalent.
425                 // (returns the path validity.)
426                 public static bool SetUserRoamingPath (string configPath)
427                 {
428                         if (CheckPath (configPath))
429                         {
430                                 userRoamingBasePath = configPath;
431                                 return true;
432                         }
433                         else
434                                 return false;
435                 }
436
437                 // sets the explicit path to store local user.config or equivalent.
438                 // (returns the path validity.)
439                 public static bool SetUserLocalPath (string configPath)
440                 {
441                         if (CheckPath (configPath))
442                         {
443                                 userLocalBasePath = configPath;
444                                 return true;
445                         }
446                         else
447                                 return false;
448                 }
449
450                 private static bool CheckFileName (string configFile)
451                 {
452                         /*
453                         char[] invalidFileChars = Path.GetInvalidFileNameChars();
454
455                         foreach (char invalidChar in invalidFileChars)
456                         {
457                                 if (configFile.Contains(invalidChar.ToString()))
458                                 {
459                                         return false;
460                                 }
461                         }
462                         return true;
463                         */
464                         return configFile.IndexOfAny (Path.GetInvalidFileNameChars ()) < 0;
465                 }
466
467                 // sets the explicit roaming file name which is user.config equivalent.
468                 // (returns the file name validity.)
469                 public static bool SetUserRoamingFileName (string configFile)
470                 {
471                         if (CheckFileName (configFile))
472                         {
473                                 userRoamingName = configFile;
474                                 return true;
475                         }
476                         else
477                                 return false;
478                 }
479
480                 // sets the explicit local file name which is user.config equivalent.
481                 // (returns the file name validity.)
482                 public static bool SetUserLocalFileName (string configFile)
483                 {
484                         if (CheckFileName (configFile))
485                         {
486                                 userLocalName = configFile;
487                                 return true;
488                         }
489                         else
490                                 return false;
491                 }
492
493                 // sets the explicit company name for folder.
494                 // (returns the file name validity.)
495                 public static bool SetCompanyName (string companyName)
496                 {
497                         if (CheckFileName (companyName))
498                         {
499                                 CompanyName = companyName;
500                                 return true;
501                         }
502                         else
503                                 return false;
504                 }
505
506                 // sets the explicit product name for folder.
507                 // (returns the file name validity.)
508                 public static bool SetProductName (string productName)
509                 {
510                         if (CheckFileName (productName))
511                         {
512                                 ProductName = productName;
513                                 return true;
514                         }
515                         else
516                                 return false;
517                 }
518
519                 // sets the explicit major version for folder.
520                 public static bool SetVersion (int major)
521                 {
522                         ForceVersion = string.Format ("{0}", major);
523                         return true;
524                 }
525
526                 // sets the explicit major and minor versions for folder.
527                 public static bool SetVersion (int major, int minor)
528                 {
529                         ForceVersion = string.Format ("{0}.{1}", major, minor);
530                         return true;
531                 }
532
533                 // sets the explicit major/minor/build numbers for folder.
534                 public static bool SetVersion (int major, int minor, int build)
535                 {
536                         ForceVersion = string.Format ("{0}.{1}.{2}", major, minor, build);
537                         return true;
538                 }
539
540                 // sets the explicit major/minor/build/revision numbers for folder.
541                 public static bool SetVersion (int major, int minor, int build, int revision)
542                 {
543                         ForceVersion = string.Format ("{0}.{1}.{2}.{3}", major, minor, build, revision);
544                         return true;
545                 }
546
547                 // sets the explicit version number string for folder.
548                 public static bool SetVersion (string forceVersion)
549                 {
550                         if (CheckFileName (forceVersion))
551                         {
552                                 ForceVersion = forceVersion;
553                                 return true;
554                         }
555                         else
556                                 return false;
557                 }
558
559                 private static bool CheckPath (string configPath)
560                 {
561                         char[] invalidPathChars = Path.GetInvalidPathChars ();
562
563                         /*
564                         foreach (char invalidChar in invalidPathChars)
565                         {
566                                 if (configPath.Contains (invalidChar.ToString()))
567                                 {
568                                         return false;
569                                 }
570                         }
571                         */
572                         if (configPath.IndexOfAny (invalidPathChars) >= 0)
573                                 return false;
574
575                         string folder = configPath;
576                         string fileName;
577                         while ((fileName = Path.GetFileName (folder)) != "")
578                         {
579                                 if (!CheckFileName (fileName))
580                                 {
581                                         return false;
582                                 }
583                                 folder = Path.GetDirectoryName (folder);
584                         }
585
586                         return true;
587                 }
588
589
590                 public override string Name {
591                         get { return base.Name; }
592                 }
593
594                 string app_name = String.Empty;//"OJK.CustomSetting.CustomizableLocalFileSettingsProvider";
595                 public override string ApplicationName {
596                         get { return app_name; }
597                         set { app_name = value; }
598                 }
599
600                 private ExeConfigurationFileMap exeMapCurrent = null;
601                 private ExeConfigurationFileMap exeMapPrev = null;
602                 private SettingsPropertyValueCollection values = null;
603
604                 private void SaveProperties (ExeConfigurationFileMap exeMap, SettingsPropertyValueCollection collection, ConfigurationUserLevel level, SettingsContext context, bool checkUserLevel)
605                 {
606                         Configuration config = ConfigurationManager.OpenMappedExeConfiguration (exeMap, level);
607                         
608                         UserSettingsGroup userGroup = config.GetSectionGroup ("userSettings") as UserSettingsGroup;
609                         bool isRoaming = (level == ConfigurationUserLevel.PerUserRoaming);
610
611 #if true // my reimplementation
612
613                         if (userGroup == null) {
614                                 userGroup = new UserSettingsGroup ();
615                                 config.SectionGroups.Add ("userSettings", userGroup);
616                                 ApplicationSettingsBase asb = context.CurrentSettings;
617                                 ClientSettingsSection cs = new ClientSettingsSection ();
618                                 string class_name = NormalizeInvalidXmlChars ((asb != null ? asb.GetType () : typeof (ApplicationSettingsBase)).FullName);
619                                 userGroup.Sections.Add (class_name, cs);
620                         }
621
622                         bool hasChanges = false;
623
624                         foreach (ConfigurationSection section in userGroup.Sections) {
625                                 ClientSettingsSection userSection = section as ClientSettingsSection;
626                                 if (userSection == null)
627                                         continue;
628
629                                 foreach (SettingsPropertyValue value in collection) {
630                                         if (checkUserLevel && value.Property.Attributes.Contains (typeof (SettingsManageabilityAttribute)) != isRoaming)
631                                                 continue;
632                                         // The default impl does not save the ApplicationScopedSetting properties
633                                         if (value.Property.Attributes.Contains (typeof (ApplicationScopedSettingAttribute)))
634                                                 continue;
635
636                                         hasChanges = true;
637                                         SettingElement element = userSection.Settings.Get (value.Name);
638                                         if (element == null) {
639                                                 element = new SettingElement (value.Name, value.Property.SerializeAs);
640                                                 userSection.Settings.Add (element);
641                                         }
642                                         if (element.Value.ValueXml == null)
643                                                 element.Value.ValueXml = new XmlDocument ().CreateElement ("value");
644                                         switch (value.Property.SerializeAs) {
645                                         case SettingsSerializeAs.Xml:
646                                                 element.Value.ValueXml.InnerXml = (value.SerializedValue as string) ?? string.Empty;
647                                                 break;
648                                         case SettingsSerializeAs.String:
649                                                 element.Value.ValueXml.InnerText = value.SerializedValue as string;
650                                                 break;
651                                         case SettingsSerializeAs.Binary:
652                                                 element.Value.ValueXml.InnerText = value.SerializedValue != null ? Convert.ToBase64String (value.SerializedValue as byte []) : string.Empty;
653                                                 break;
654                                         default:
655                                                 throw new NotImplementedException ();
656                                         }
657                                 }
658                         }
659                         if (hasChanges)
660                                 config.Save (ConfigurationSaveMode.Minimal, true);
661
662 #else // original impl. - likely buggy to miss some properties to save
663
664                         foreach (ConfigurationSection configSection in userGroup.Sections)
665                         {
666                                 ClientSettingsSection userSection = configSection as ClientSettingsSection;
667                                 if (userSection != null)
668                                 {
669 /*
670                                         userSection.Settings.Clear();
671
672                                         foreach (SettingsPropertyValue propertyValue in collection)
673                                         {
674                                                 if (propertyValue.IsDirty)
675                                                 {
676                                                         SettingElement element = new SettingElement(propertyValue.Name, SettingsSerializeAs.String);
677                                                         element.Value.ValueXml = new XmlDocument();
678                                                         element.Value.ValueXml.InnerXml = (string)propertyValue.SerializedValue;
679                                                         userSection.Settings.Add(element);
680                                                 }
681                                         }
682 */
683                                         foreach (SettingElement element in userSection.Settings)
684                                         {
685                                                 if (collection [element.Name] != null) {
686                                                         if (collection [element.Name].Property.Attributes.Contains (typeof (SettingsManageabilityAttribute)) != isRoaming)
687                                                                 continue;
688
689                                                         element.SerializeAs = SettingsSerializeAs.String;
690                                                         element.Value.ValueXml.InnerXml = (string) collection [element.Name].SerializedValue;   ///Value = XmlElement
691                                                 }
692                                         }
693  
694                                 }
695                         }
696                         config.Save (ConfigurationSaveMode.Minimal, true);
697 #endif
698                 }
699
700                 // NOTE: We should add here all the chars that are valid in a name of a class (Ecma-wise),
701                 // but invalid in an xml element name, and provide a better impl if we get too many of them.
702                 string NormalizeInvalidXmlChars (string str)
703                 {
704                         char [] invalid_chars = new char [] { '+' };
705
706                         if (str == null || str.IndexOfAny (invalid_chars) == -1)
707                                 return str;
708
709                         // Replace with its hexadecimal values.
710                         str = str.Replace ("+", "_x002B_");
711                         return str;
712                 }
713
714                 private void LoadPropertyValue (SettingsPropertyCollection collection, SettingElement element, bool allowOverwrite)
715                 {
716                         SettingsProperty prop = collection [element.Name];
717                         if (prop == null) { // see bug #343459
718                                 prop = new SettingsProperty (element.Name);
719                                 collection.Add (prop);
720                         }
721
722                         SettingsPropertyValue value = new SettingsPropertyValue (prop);
723                         value.IsDirty = false;
724                         if (element.Value.ValueXml != null) {
725                                 switch (value.Property.SerializeAs) {
726                                 case SettingsSerializeAs.Xml:
727                                         value.SerializedValue = element.Value.ValueXml.InnerXml;
728                                         break;
729                                 case SettingsSerializeAs.String:
730                                         value.SerializedValue = element.Value.ValueXml.InnerText;
731                                         break;
732                                 case SettingsSerializeAs.Binary:
733                                         value.SerializedValue = Convert.FromBase64String (element.Value.ValueXml.InnerText);
734                                         break;
735                                 }
736                         }
737                         else
738                                 value.SerializedValue = prop.DefaultValue;
739                         try
740                         {
741                                 if (allowOverwrite)
742                                         values.Remove (element.Name);
743                                 values.Add (value);
744                         } catch (ArgumentException ex) {
745                                 throw new ConfigurationErrorsException (string.Format (
746                                         CultureInfo.InvariantCulture,
747                                         "Failed to load value for '{0}'.",
748                                         element.Name), ex);
749                         }
750                 }
751
752                 private void LoadProperties (ExeConfigurationFileMap exeMap, SettingsPropertyCollection collection, ConfigurationUserLevel level, string sectionGroupName, bool allowOverwrite, string groupName)
753                 {
754                         Configuration config = ConfigurationManager.OpenMappedExeConfiguration (exeMap,level);
755                         
756                         ConfigurationSectionGroup sectionGroup = config.GetSectionGroup (sectionGroupName);
757                         if (sectionGroup != null) {
758                                 foreach (ConfigurationSection configSection in sectionGroup.Sections) {
759                                         if (configSection.SectionInformation.Name != groupName)
760                                                 continue;
761
762                                         ClientSettingsSection clientSection = configSection as ClientSettingsSection;
763                                         if (clientSection == null)
764                                                 continue;
765
766                                         foreach (SettingElement element in clientSection.Settings) {
767                                                 LoadPropertyValue(collection, element, allowOverwrite);
768                                         }
769                                         // Only the first one seems to be processed by MS
770                                         break;
771                                 }
772                         }
773
774                 }
775
776                 public override void SetPropertyValues (SettingsContext context, SettingsPropertyValueCollection collection)
777                 {
778                         CreateExeMap ();
779
780                         if (UserLocalFullPath == UserRoamingFullPath)
781                         {
782                                 SaveProperties (exeMapCurrent, collection, ConfigurationUserLevel.PerUserRoaming, context, false);
783                         } else {
784                                 SaveProperties (exeMapCurrent, collection, ConfigurationUserLevel.PerUserRoaming, context, true);
785                                 SaveProperties (exeMapCurrent, collection, ConfigurationUserLevel.PerUserRoamingAndLocal, context, true);
786                         }
787                 }
788
789                 public override SettingsPropertyValueCollection GetPropertyValues (SettingsContext context, SettingsPropertyCollection collection)
790                 {
791                         CreateExeMap ();
792
793                         if (values == null) {
794                                 values = new SettingsPropertyValueCollection ();
795                                 string groupName = context ["GroupName"] as string;
796                                 groupName = NormalizeInvalidXmlChars (groupName); // we likely saved the element removing the non valid xml chars.
797                                 LoadProperties (exeMapCurrent, collection, ConfigurationUserLevel.None, "applicationSettings", false, groupName);
798                                 LoadProperties (exeMapCurrent, collection, ConfigurationUserLevel.None, "userSettings", false, groupName);
799
800                                 LoadProperties (exeMapCurrent, collection, ConfigurationUserLevel.PerUserRoaming, "userSettings", true, groupName);
801                                 LoadProperties (exeMapCurrent, collection, ConfigurationUserLevel.PerUserRoamingAndLocal, "userSettings", true, groupName);
802
803                                 // create default values if not exist
804                                 foreach (SettingsProperty p in collection)
805                                         if (values [p.Name] == null)
806                                                 values.Add (new SettingsPropertyValue (p));
807                         }
808                         return values;
809                 }
810
811                 /// creates an ExeConfigurationFileMap
812                 private void CreateExeMap ()
813                 {
814                         if (exeMapCurrent == null) {
815                                 CreateUserConfigPath ();
816
817                                 // current version
818                                 exeMapCurrent = new ExeConfigurationFileMap ();
819                                 
820                                 // exeMapCurrent.ExeConfigFilename = System.Windows.Forms.Application.ExecutablePath + ".config";
821                                 Assembly entry = Assembly.GetEntryAssembly () ?? Assembly.GetExecutingAssembly ();
822                                 exeMapCurrent.ExeConfigFilename = entry.Location + ".config";
823                                 exeMapCurrent.LocalUserConfigFilename = UserLocalFullPath;
824                                 exeMapCurrent.RoamingUserConfigFilename = UserRoamingFullPath;
825
826                                 if (webConfigurationFileMapType != null && typeof (ConfigurationFileMap).IsAssignableFrom (webConfigurationFileMapType)) {
827                                         try {
828                                                 ConfigurationFileMap cfgFileMap = Activator.CreateInstance (webConfigurationFileMapType) as ConfigurationFileMap;
829                                                 if (cfgFileMap != null) {
830                                                         string fpath = cfgFileMap.MachineConfigFilename;
831                                                         if (!String.IsNullOrEmpty (fpath))
832                                                                 exeMapCurrent.ExeConfigFilename = fpath;
833                                                 }
834                                         } catch {
835                                                 // ignore
836                                         }
837                                 }
838                                 
839                                 // previous version
840                                 if ((PrevUserLocalFullPath != "") && (PrevUserRoamingFullPath != ""))
841                                 {
842                                         exeMapPrev = new ExeConfigurationFileMap();
843                                         // exeMapPrev.ExeConfigFilename = System.Windows.Forms.Application.ExecutablePath + ".config";
844                                         exeMapPrev.ExeConfigFilename = entry.Location + ".config";
845                                         exeMapPrev.LocalUserConfigFilename = PrevUserLocalFullPath;
846                                         exeMapPrev.RoamingUserConfigFilename = PrevUserRoamingFullPath;
847                                 }
848                         }
849                 }
850
851                 // FIXME: implement
852                 public SettingsPropertyValue GetPreviousVersion (SettingsContext context, SettingsProperty property)
853                 {
854                         return null;
855                 }
856
857                 public void Reset (SettingsContext context)
858                 {
859                         SettingsPropertyCollection coll = new SettingsPropertyCollection ();
860                         GetPropertyValues (context, coll);
861                         foreach (SettingsPropertyValue propertyValue in values) {
862                                 // Can't use propertyValue.Property.DefaultValue
863                                 // as it may cause InvalidCastException (see bug# 532180)
864                                 propertyValue.PropertyValue = propertyValue.Reset ();
865                         }
866                         SetPropertyValues (context, values);
867                 }
868
869                 // FIXME: implement
870                 public void Upgrade (SettingsContext context, SettingsPropertyCollection properties)
871                 {
872                 }
873
874                 public static void setCreate ()
875                 {
876                         CreateUserConfigPath();
877                 }
878         }
879 }
880
881 #endif