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