* roottypes.cs: Rename from tree.cs.
[mono.git] / mcs / class / corlib / Microsoft.Win32 / UnixRegistryApi.cs
1 //
2 // Microsoft.Win32/IRegistryApi.cs
3 //
4 // Authors:
5 //      Miguel de Icaza (miguel@gnome.org)
6 //
7 // (C) 2005 Novell, Inc (http://www.novell.com)
8 // 
9 // MISSING:
10 //   Someone could the same subkey twice: once read/write once readonly,
11 //   currently since we use a unique hash based on the file name, we are unable
12 //   to have two versions of the same key and hence unable to throw an exception
13 //   if the user tries to write to a read-only key.
14 //
15 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
16 //
17 // Permission is hereby granted, free of charge, to any person obtaining
18 // a copy of this software and associated documentation files (the
19 // "Software"), to deal in the Software without restriction, including
20 // without limitation the rights to use, copy, modify, merge, publish,
21 // distribute, sublicense, and/or sell copies of the Software, and to
22 // permit persons to whom the Software is furnished to do so, subject to
23 // the following conditions:
24 // 
25 // The above copyright notice and this permission notice shall be
26 // included in all copies or substantial portions of the Software.
27 // 
28 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
30 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
31 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
32 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
33 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
34 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 //
36
37 using System;
38 using System.Collections;
39 using System.Globalization;
40 using System.IO;
41 using System.Text;
42 using System.Runtime.InteropServices;
43 using System.Reflection;
44 using System.Security;
45 using System.Threading;
46
47 namespace Microsoft.Win32 {
48
49         class KeyHandler {
50                 static Hashtable key_to_handler = new Hashtable ();
51                 static Hashtable dir_to_key = new Hashtable ();
52                 public string Dir;
53                 public IntPtr Handle;
54
55                 public Hashtable values;
56                 string file;
57                 bool dirty;
58                 
59                 KeyHandler (RegistryKey rkey, string basedir)
60                 {
61                         if (!Directory.Exists (basedir)){
62                                 try {
63                                         Directory.CreateDirectory (basedir);
64                                 } catch (Exception e){
65                                         Console.Error.WriteLine ("KeyHandler error while creating directory {0}:\n{1}", basedir, e);
66                                 }
67                         }
68                         Dir = basedir;
69                         file = Path.Combine (Dir, "values.xml");
70                         Load ();
71                 }
72
73                 public void Load ()
74                 {
75                         values = new Hashtable ();
76                         if (!File.Exists (file))
77                                 return;
78                         
79                         try {
80                                 using (FileStream fs = File.OpenRead (file)){
81                                         StreamReader r = new StreamReader (fs);
82                                         string xml = r.ReadToEnd ();
83                                         if (xml.Length == 0)
84                                                 return;
85                                         
86                                         SecurityElement tree = SecurityElement.FromString (xml);
87                                         if (tree.Tag == "values" && tree.Children != null){
88                                                 foreach (SecurityElement value in tree.Children){
89                                                         if (value.Tag == "value"){
90                                                                 LoadKey (value);
91                                                         }
92                                                 }
93                                         }
94                                 }
95                         } catch (Exception e){
96                                 Console.Error.WriteLine ("While loading registry key at {0}: {1}", file, e);
97                                 values.Clear ();
98                         }
99                 }
100
101                 void LoadKey (SecurityElement se)
102                 {
103                         Hashtable h = se.Attributes;
104                         try {
105                                 string name = (string) h ["name"];
106                                 if (name == null)
107                                         return;
108                                 string type = (string) h ["type"];
109                                 if (type == null)
110                                         return;
111                                 
112                                 switch (type){
113                                 case "int":
114                                         values [name] = Int32.Parse (se.Text);
115                                         break;
116                                 case "bytearray":
117                                         Convert.FromBase64String (se.Text);
118                                         break;
119                                 case "string":
120                                         values [name] = se.Text;
121                                         break;
122                                 case "string-array":
123                                         ArrayList sa = new ArrayList ();
124                                         if (se.Children != null){
125                                                 foreach (SecurityElement stre in se.Children){
126                                                         sa.Add (stre.Text);
127                                                 }
128                                         }
129                                         values [name] = sa.ToArray (typeof (string));
130                                         break;
131                                 }
132                         } catch {
133                                 // We ignore individual errors in the file.
134                         }
135                 }
136                 
137                 public RegistryKey Ensure (RegistryKey rkey, string extra)
138                 {
139                         lock (typeof (KeyHandler)){
140                                 string f = Path.Combine (Dir, extra);
141                                 if (dir_to_key.Contains (f))
142                                         return (RegistryKey) dir_to_key [f];
143
144                                 KeyHandler kh = new KeyHandler (rkey, f);
145                                 RegistryKey rk = new RegistryKey (kh, CombineName (rkey, extra));
146                                 key_to_handler [rk] = kh;
147                                 dir_to_key [f] = rk;
148                                 return rk;
149                         }
150                 }
151
152                 public RegistryKey Probe (RegistryKey rkey, string extra, bool write)
153                 {
154                         lock (typeof (KeyHandler)){
155                                 string f = Path.Combine (Dir, extra);
156                                 if (dir_to_key.Contains (f))
157                                         return (RegistryKey) dir_to_key [f];
158                                 if (Directory.Exists (f)){
159                                         KeyHandler kh = new KeyHandler (rkey, f);
160                                         RegistryKey rk = new RegistryKey (kh, CombineName (rkey, extra));
161                                         dir_to_key [f] = rk;
162                                         key_to_handler [rk] = kh;
163                                         return rk;
164                                 }
165                                 return null;
166                         }
167                 }
168
169                 static string CombineName (RegistryKey rkey, string extra)
170                 {
171                         if (extra.IndexOf ('/') != -1)
172                                 extra = extra.Replace ('/', '\\');
173                         
174                         return String.Concat (rkey.Name, "\\", extra);
175                 }
176                 
177                 public static KeyHandler Lookup (RegistryKey rkey)
178                 {
179                         lock (typeof (KeyHandler)){
180                                 KeyHandler k = (KeyHandler) key_to_handler [rkey];
181                                 if (k != null)
182                                         return k;
183
184                                 RegistryHive x = (RegistryHive) rkey.Data;
185                                 switch (x){
186                                 case RegistryHive.ClassesRoot:
187                                 case RegistryHive.CurrentConfig:
188                                 case RegistryHive.CurrentUser:
189                                 case RegistryHive.DynData:
190                                 case RegistryHive.LocalMachine:
191                                 case RegistryHive.PerformanceData:
192                                 case RegistryHive.Users:
193                                         string d = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.Personal), ".mono/registry");
194                                         d = Path.Combine (d, x.ToString ());
195                                         
196                                         k = new KeyHandler (rkey, d);
197                                         key_to_handler [rkey] = k;
198                                         break;
199                                 default:
200                                         throw new Exception ("Unknown RegistryHive");
201                                 }
202                                 key_to_handler [rkey] = k;
203                                 return k;
204                         }
205                 }
206
207                 public static void Drop (RegistryKey rkey)
208                 {
209                         KeyHandler k = (KeyHandler) key_to_handler [rkey];
210                         if (k == null)
211                                 return;
212                         dir_to_key.Remove (k.Dir);
213                         key_to_handler.Remove (rkey);
214                 }
215
216                 public static void Drop (string dir)
217                 {
218                         if (dir_to_key.Contains (dir)){
219                                 key_to_handler.Remove (dir_to_key [dir]); 
220                                 dir_to_key.Remove (dir);
221                         }
222                 }
223
224                 public void SetValue (string name, object value)
225                 {
226                         // immediately convert non-native registry values to string to avoid
227                         // returning it unmodified in calls to UnixRegistryApi.GetValue
228                         if (value is int || value is string || value is byte[] || value is string[])
229                                 values[name] = value;
230                         else
231                                 values[name] = value.ToString ();
232                         SetDirty ();
233                 }
234
235                 void SetDirty ()
236                 {
237                         lock (typeof (KeyHandler)){
238                                 if (dirty)
239                                         return;
240                                 dirty = true;
241                                 new Timer (DirtyTimeout, null, 3000, Timeout.Infinite);
242                         }
243                 }
244
245                 public void DirtyTimeout (object state)
246                 {
247                         Flush ();
248                 }
249                 
250                 public void Flush ()
251                 {
252                         lock (typeof (KeyHandler)){
253                                 dirty = false;
254                                 Save ();
255                         }
256                 }
257
258                 ~KeyHandler ()
259                 {
260                         Flush ();
261                 }
262                 
263                 void Save ()
264                 {
265                         if (!File.Exists (file) && values.Count == 0)
266                                 return;
267                         
268                         SecurityElement se = new SecurityElement ("values");
269                         
270                         foreach (DictionaryEntry de in values){
271                                 object val = de.Value;
272                                 SecurityElement value = new SecurityElement ("value");
273                                 value.AddAttribute ("name", (string) de.Key);
274                                 
275                                 if (val is string){
276                                         value.AddAttribute ("type", "string");
277                                         value.Text = (string) val;
278                                 } else if (val is int){
279                                         value.AddAttribute ("type", "int");
280                                         value.Text = val.ToString ();
281                                 } else if (val is byte []){
282                                         value.AddAttribute ("type", "bytearray");
283                                         value.Text = Convert.ToBase64String ((byte[]) val);
284                                 } else if (val is string []){
285                                         value.AddAttribute ("type", "string-array");
286
287                                         foreach (string ss in (string[]) val){
288                                                 SecurityElement str = new SecurityElement ("string");
289                                                 str.Text = ss; 
290                                                 value.AddChild (str);
291                                         }
292                                 }
293                                 se.AddChild (value);
294                         }
295
296                         try {
297                                 using (FileStream fs = File.Create (file)){
298                                         StreamWriter sw = new StreamWriter (fs);
299
300                                         sw.Write (se.ToString ());
301                                         sw.Flush ();
302                                 }
303                         } catch (Exception e){
304                                 Console.Error.WriteLine ("When saving {0} got {1}", file, e);
305                         }
306                 }
307         }
308         
309         internal class UnixRegistryApi : IRegistryApi {
310
311                 static string ToUnix (string keyname)
312                 {
313                         if (keyname.IndexOf ('\\') != -1)
314                                 keyname = keyname.Replace ('\\', '/');
315                         return keyname.ToLower ();
316                 }
317
318                 static bool IsWellKnownKey (string parentKeyName, string keyname)
319                 {
320                         // FIXME: Add more keys if needed
321                         if (parentKeyName == Registry.CurrentUser.Name ||
322                                 parentKeyName == Registry.LocalMachine.Name)
323                                 return (0 == String.Compare ("software", keyname, true, CultureInfo.InvariantCulture));
324
325                         return false;
326                 }
327
328                 public RegistryKey CreateSubKey (RegistryKey rkey, string keyname)
329                 {
330                         KeyHandler self = KeyHandler.Lookup (rkey);
331                         return self.Ensure (rkey, ToUnix (keyname));
332                 }
333
334                 public RegistryKey OpenSubKey (RegistryKey rkey, string keyname, bool writtable)
335                 {
336                         KeyHandler self = KeyHandler.Lookup (rkey);
337                         RegistryKey result = self.Probe (rkey, ToUnix (keyname), writtable);
338                         if (result == null && IsWellKnownKey (rkey.Name, keyname)) {
339                                 result = CreateSubKey (rkey, keyname);
340                         }
341
342                         return result;
343                 }
344                 
345                 public void Flush (RegistryKey rkey)
346                 {
347                         KeyHandler self = KeyHandler.Lookup (rkey);
348                         self.Flush ();
349                 }
350                 
351                 public void Close (RegistryKey rkey)
352                 {
353                         KeyHandler.Drop (rkey);
354                 }
355                 
356                 public object GetValue (RegistryKey rkey, string name, bool return_default_value, object default_value)
357                 {
358                         KeyHandler self = KeyHandler.Lookup (rkey);
359
360                         if (self.values.Contains (name))
361                                 return self.values [name];
362                         if (return_default_value)
363                                 return default_value;
364                         return null;
365                 }
366                 
367                 public void SetValue (RegistryKey rkey, string name, object value)
368                 {
369                         KeyHandler self = KeyHandler.Lookup (rkey);
370                         self.SetValue (name, value);
371                 }
372
373                 public int SubKeyCount (RegistryKey rkey)
374                 {
375                         KeyHandler self = KeyHandler.Lookup (rkey);
376
377                         return Directory.GetDirectories (self.Dir).Length;
378                 }
379                 
380                 public int ValueCount (RegistryKey rkey)
381                 {
382                         KeyHandler self = KeyHandler.Lookup (rkey);
383
384                         return self.values.Keys.Count;
385                 }
386                 
387                 public void DeleteValue (RegistryKey rkey, string name, bool throw_if_missing)
388                 {
389                         KeyHandler self = KeyHandler.Lookup (rkey);
390
391                         if (throw_if_missing && !self.values.Contains (name))
392                                 throw new ArgumentException ("the given value does not exist", "name");
393
394                         self.values.Remove (name);
395                 }
396                 
397                 public void DeleteKey (RegistryKey rkey, string keyname, bool throw_if_missing)
398                 {
399                         KeyHandler self = KeyHandler.Lookup (rkey);
400                         string dir = Path.Combine (self.Dir, keyname);
401                         
402                         if (Directory.Exists (dir)){
403                                 Directory.Delete (dir, true);
404                                 KeyHandler.Drop (dir);
405                         } else if (throw_if_missing)
406                                 throw new ArgumentException ("the given value does not exist", "value");
407                 }
408                 
409                 public string [] GetSubKeyNames (RegistryKey rkey)
410                 {
411                         KeyHandler self = KeyHandler.Lookup (rkey);
412                         DirectoryInfo selfDir = new DirectoryInfo (self.Dir);
413                         DirectoryInfo[] subDirs = selfDir.GetDirectories ();
414                         string[] subKeyNames = new string[subDirs.Length];
415                         for (int i = 0; i < subDirs.Length; i++) {
416                                 DirectoryInfo subDir = subDirs[i];
417                                 subKeyNames[i] = subDir.Name;
418                         }
419                         return subKeyNames;
420                 }
421                 
422                 public string [] GetValueNames (RegistryKey rkey)
423                 {
424                         KeyHandler self = KeyHandler.Lookup (rkey);
425                         ICollection keys = self.values.Keys;
426
427                         string [] vals = new string [keys.Count];
428                         keys.CopyTo (vals, 0);
429                         return vals;
430                 }
431
432                 public string ToString (RegistryKey rkey)
433                 {
434                         return rkey.Name;
435                 }
436         }
437 }