Merge pull request #839 from glinos/master
[mono.git] / mcs / class / corlib / System.Security.Permissions / RegistryPermission.cs
1 //
2 // System.Security.Permissions.RegistryPermission.cs
3 //
4 // Author
5 //      Sebastien Pouliot  <sebastien@ximian.com>
6 //
7 // Copyright (C) 2003 Motus Technologies. http://www.motus.com
8 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
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 using System.Collections;
31 using System.Globalization;
32 using System.Text;
33
34 using System.Runtime.InteropServices;
35 using System.Security.AccessControl;
36
37 namespace System.Security.Permissions {
38
39         [Serializable]
40         [ComVisible (true)]
41         public sealed class RegistryPermission
42                 : CodeAccessPermission, IUnrestrictedPermission, IBuiltInPermission {
43
44                 private const int version = 1;
45
46                 private PermissionState _state;
47 //              private RegistryPermissionAccess _access;
48                 private ArrayList createList;
49                 private ArrayList readList;
50                 private ArrayList writeList;
51 //              private AccessControlActions _control;
52                 // Constructors
53
54                 public RegistryPermission (PermissionState state)
55                 {
56                         _state = CheckPermissionState (state, true);
57                         createList = new ArrayList ();
58                         readList = new ArrayList ();
59                         writeList = new ArrayList ();
60                 }
61
62                 public RegistryPermission (RegistryPermissionAccess access, string pathList)
63                 {
64                         _state = PermissionState.None;
65                         createList = new ArrayList ();
66                         readList = new ArrayList ();
67                         writeList = new ArrayList ();
68                         AddPathList (access, pathList);
69                 }
70                 public RegistryPermission (RegistryPermissionAccess access, AccessControlActions control, string pathList)
71                 {
72                         if (!Enum.IsDefined (typeof (AccessControlActions), control)) {
73                                 string msg = String.Format (Locale.GetText ("Invalid enum {0}"), control);
74                                 throw new ArgumentException (msg, "AccessControlActions");
75                         }
76                         _state = PermissionState.None;
77                         AddPathList (access, control, pathList);
78                 }
79
80                 // Properties
81
82                 // Methods
83
84                 public void AddPathList (RegistryPermissionAccess access, string pathList) 
85                 {
86                         if (pathList == null)
87                                 throw new ArgumentNullException ("pathList");
88
89                         switch (access) {
90                                 case RegistryPermissionAccess.AllAccess:
91                                         AddWithUnionKey (createList, pathList);
92                                         AddWithUnionKey (readList, pathList);
93                                         AddWithUnionKey (writeList, pathList);
94                                         break;
95                                 case RegistryPermissionAccess.NoAccess:
96                                         // ??? unit tests doesn't show removal using NoAccess ???
97                                         break;
98                                 case RegistryPermissionAccess.Create:
99                                         AddWithUnionKey (createList, pathList);
100                                         break;
101                                 case RegistryPermissionAccess.Read:
102                                         AddWithUnionKey (readList, pathList);
103                                         break;
104                                 case RegistryPermissionAccess.Write:
105                                         AddWithUnionKey (writeList, pathList);
106                                         break;
107                                 default:
108                                         ThrowInvalidFlag (access, false);
109                                         break;
110                         }
111                 }
112                 [MonoTODO ("(2.0) Access Control isn't implemented")]
113                 public void AddPathList (RegistryPermissionAccess access, AccessControlActions control, string pathList) 
114                 {
115                         throw new NotImplementedException ();
116                 }
117                 public string GetPathList (RegistryPermissionAccess access)
118                 {
119                         switch (access) {
120                                 case RegistryPermissionAccess.AllAccess:
121                                 case RegistryPermissionAccess.NoAccess:
122                                         ThrowInvalidFlag (access, true);
123                                         break;
124                                 case RegistryPermissionAccess.Create:
125                                         return GetPathList (createList);
126                                 case RegistryPermissionAccess.Read:
127                                         return GetPathList (readList);
128                                 case RegistryPermissionAccess.Write:
129                                         return GetPathList (writeList);
130                                 default:
131                                         ThrowInvalidFlag (access, false);
132                                         break;
133                         }
134                         return null; // never reached
135                 }
136
137                 public void SetPathList (RegistryPermissionAccess access, string pathList)
138                 {
139                         if (pathList == null)
140                                 throw new ArgumentNullException ("pathList");
141
142                         string[] paths;
143                         switch (access) {
144                                 case RegistryPermissionAccess.AllAccess:
145                                         createList.Clear ();
146                                         readList.Clear ();
147                                         writeList.Clear ();
148                                         paths = pathList.Split (';');
149                                         foreach (string path in paths) {
150                                                 createList.Add (path);
151                                                 readList.Add (path);
152                                                 writeList.Add (path);
153                                         }
154                                         break;
155                                 case RegistryPermissionAccess.NoAccess:
156                                         // ??? unit tests doesn't show removal using NoAccess ???
157                                         break;
158                                 case RegistryPermissionAccess.Create:
159                                         createList.Clear ();
160                                         paths = pathList.Split (';');
161                                         foreach (string path in paths) {
162                                                 createList.Add (path);
163                                         }
164                                         break;
165                                 case RegistryPermissionAccess.Read:
166                                         readList.Clear ();
167                                         paths = pathList.Split (';');
168                                         foreach (string path in paths) {
169                                                 readList.Add (path);
170                                         }
171                                         break;
172                                 case RegistryPermissionAccess.Write:
173                                         writeList.Clear ();
174                                         paths = pathList.Split (';');
175                                         foreach (string path in paths) {
176                                                 writeList.Add (path);
177                                         }
178                                         break;
179                                 default:
180                                         ThrowInvalidFlag (access, false);
181                                         break;
182                         }
183                 }
184
185                 public override IPermission Copy () 
186                 {
187                         RegistryPermission rp = new RegistryPermission (_state);
188
189                         string path = GetPathList (RegistryPermissionAccess.Create);
190                         if (path != null)
191                                 rp.SetPathList (RegistryPermissionAccess.Create, path);
192
193                         path = GetPathList (RegistryPermissionAccess.Read);
194                         if (path != null)
195                                 rp.SetPathList (RegistryPermissionAccess.Read, path);
196
197                         path = GetPathList (RegistryPermissionAccess.Write);
198                         if (path != null)
199                                 rp.SetPathList (RegistryPermissionAccess.Write, path);
200                         return rp;
201                 }
202
203                 public override void FromXml (SecurityElement esd) 
204                 {
205                         // General validation in CodeAccessPermission
206                         CheckSecurityElement (esd, "esd", version, version);
207                         // Note: we do not (yet) care about the return value 
208                         // as we only accept version 1 (min/max values)
209
210                         // General validation in CodeAccessPermission
211                         CheckSecurityElement (esd, "esd", version, version);
212                         // Note: we do not (yet) care about the return value 
213                         // as we only accept version 1 (min/max values)
214
215                         if (IsUnrestricted (esd))
216                                 _state = PermissionState.Unrestricted;
217
218                         string create = esd.Attribute ("Create");
219                         if ((create != null) && (create.Length > 0))
220                                 SetPathList (RegistryPermissionAccess.Create, create);
221
222                         string read = esd.Attribute ("Read");
223                         if ((read != null) && (read.Length > 0))
224                                 SetPathList (RegistryPermissionAccess.Read, read);
225
226                         string write = esd.Attribute ("Write");
227                         if ((write != null) && (write.Length > 0))
228                                 SetPathList (RegistryPermissionAccess.Write, write);
229                 }
230
231                 public override IPermission Intersect (IPermission target) 
232                 {
233                         RegistryPermission rp = Cast (target);
234                         if (rp == null)
235                                 return null;
236
237                         if (IsUnrestricted ())
238                                 return rp.Copy ();
239                         if (rp.IsUnrestricted ())
240                                 return Copy ();
241
242                         RegistryPermission result = new RegistryPermission (PermissionState.None);
243
244                         IntersectKeys (createList, rp.createList, result.createList);
245                         IntersectKeys (readList, rp.readList, result.readList);
246                         IntersectKeys (writeList, rp.writeList, result.writeList);
247
248                         return (result.IsEmpty () ? null : result);
249                 }
250
251                 public override bool IsSubsetOf (IPermission target) 
252                 {
253                         RegistryPermission rp = Cast (target);
254                         if (rp == null) 
255                                 return false;
256                         if (rp.IsEmpty ())
257                                 return IsEmpty ();
258
259                         if (IsUnrestricted ())
260                                 return rp.IsUnrestricted ();
261                         else if (rp.IsUnrestricted ())
262                                 return true;
263
264                         if (!KeyIsSubsetOf (createList, rp.createList))
265                                 return false;
266                         if (!KeyIsSubsetOf (readList, rp.readList))
267                                 return false;
268                         if (!KeyIsSubsetOf (writeList, rp.writeList))
269                                 return false;
270
271                         return true;
272                 }
273
274                 public bool IsUnrestricted () 
275                 {
276                         return (_state == PermissionState.Unrestricted);
277                 }
278
279                 public override SecurityElement ToXml () 
280                 {
281                         SecurityElement se = Element (version);
282
283                         if (_state == PermissionState.Unrestricted) {
284                                 se.AddAttribute ("Unrestricted", "true");
285                         }
286                         else {
287                                 string path = GetPathList (RegistryPermissionAccess.Create);
288                                 if (path != null)
289                                         se.AddAttribute ("Create", path);
290                                 path = GetPathList (RegistryPermissionAccess.Read);
291                                 if (path != null)
292                                         se.AddAttribute ("Read", path);
293                                 path = GetPathList (RegistryPermissionAccess.Write);
294                                 if (path != null)
295                                         se.AddAttribute ("Write", path);
296                         }
297                         return se;
298                 }
299
300                 public override IPermission Union (IPermission other)
301                 {
302                         RegistryPermission rp = Cast (other);
303                         if (rp == null)
304                                 return Copy ();
305
306                         if (IsUnrestricted () || rp.IsUnrestricted ())
307                                 return new RegistryPermission (PermissionState.Unrestricted);
308
309                         if (IsEmpty () && rp.IsEmpty ())
310                                 return null;
311
312                         RegistryPermission result = (RegistryPermission) Copy ();
313                         string path = rp.GetPathList (RegistryPermissionAccess.Create);
314                         if (path != null) 
315                                 result.AddPathList (RegistryPermissionAccess.Create, path);
316                         path = rp.GetPathList (RegistryPermissionAccess.Read);
317                         if (path != null) 
318                                 result.AddPathList (RegistryPermissionAccess.Read, path);
319                         path = rp.GetPathList (RegistryPermissionAccess.Write);
320                         if (path != null)
321                                 result.AddPathList (RegistryPermissionAccess.Write, path);
322                         return result;
323                 }
324
325                 // IBuiltInPermission
326                 int IBuiltInPermission.GetTokenIndex ()
327                 {
328                         return (int) BuiltInToken.Registry;
329                 }
330
331                 // helpers
332
333                 private bool IsEmpty ()
334                 {
335                         return ((_state == PermissionState.None) && (createList.Count == 0) &&
336                                 (readList.Count == 0) && (writeList.Count == 0));
337                 }
338
339                 private RegistryPermission Cast (IPermission target)
340                 {
341                         if (target == null)
342                                 return null;
343
344                         RegistryPermission rp = (target as RegistryPermission);
345                         if (rp == null) {
346                                 ThrowInvalidPermission (target, typeof (RegistryPermission));
347                         }
348
349                         return rp;
350                 }
351
352                 internal void ThrowInvalidFlag (RegistryPermissionAccess flag, bool context) 
353                 {
354                         string msg = null;
355                         if (context)
356                                 msg = Locale.GetText ("Unknown flag '{0}'.");
357                         else
358                                 msg = Locale.GetText ("Invalid flag '{0}' in this context.");
359                         throw new ArgumentException (String.Format (msg, flag), "flag");
360                 }
361
362                 private string GetPathList (ArrayList list)
363                 {
364                         if (IsUnrestricted ())
365                                 return String.Empty;
366                         if (list.Count == 0)
367                                 return String.Empty;
368                         StringBuilder sb = new StringBuilder ();
369                         foreach (string path in list) {
370                                 sb.Append (path);
371                                 sb.Append (";");
372                         }
373
374                         string result = sb.ToString ();
375                         // remove last ';'
376                         int n = result.Length;
377                         if (n > 0)
378                                 return result.Substring (0, n - 1);
379                         return String.Empty;
380                 }
381
382                 internal bool KeyIsSubsetOf (IList local, IList target)
383                 {
384                         bool result = false;
385                         foreach (string l in local) {
386                                 foreach (string t in target) {
387                                         if (l.StartsWith (t)) {
388                                                 result = true;
389                                                 break;
390                                         }
391                                 }
392                                 if (!result)
393                                         return false;
394                         }
395                         return true;
396                 }
397
398                 internal void AddWithUnionKey (IList list, string pathList)
399                 {
400                         string[] paths = pathList.Split (';');
401                         foreach (string path in paths) {
402                                 int len = list.Count;
403                                 if (len == 0) {
404                                         list.Add (path);
405                                 }
406                                 else {
407                                         for (int i=0; i < len; i++) {
408                                                 string s = (string) list [i];
409                                                 if (s.StartsWith (path)) {
410                                                         // replace (with reduced version)
411                                                         list [i] = path;
412                                                 }
413                                                 else if (path.StartsWith (s)) {
414                                                         // no need to add
415                                                 }
416                                                 else {
417                                                         list.Add (path);
418                                                 }
419                                         }
420                                 }
421                         }
422                 }
423
424                 internal void IntersectKeys (IList local, IList target, IList result)
425                 {
426                         foreach (string l in local) {
427                                 foreach (string t in target) {
428                                         if (t.Length > l.Length) {
429                                                 if (t.StartsWith (l))
430                                                         result.Add (t);
431                                         }
432                                         else {
433                                                 if (l.StartsWith (t))
434                                                         result.Add (l);
435                                         }
436                                 }
437                         }
438                 }
439         }
440 }