* Mono.Posix.dll.sources: Rename Mono.Posix to Mono.Unix.
[mono.git] / mcs / class / corlib / System.Security.Permissions / FileIOPermission.cs
1 // \r
2 // System.Security.Permissions.FileIOPermission.cs \r
3 //\r
4 // Authors:
5 //      Nick Drochak, ndrochak@gol.com\r
6 //      Sebastien Pouliot  <sebastien@ximian.com>
7 //
8 // Copyright (C) 2001 Nick Drochak, All Rights Reserved\r
9 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30 \r
31 using System.Collections;\r
32 using System.IO;\r
33 using System.Text;\r
34
35 #if NET_2_0
36 using System.Security.AccessControl;
37 #endif
38 \r
39 namespace System.Security.Permissions {\r
40 \r
41         [Serializable]\r
42         public sealed class FileIOPermission\r
43                 : CodeAccessPermission, IBuiltInPermission, IUnrestrictedPermission {\r
44
45                 private const int version = 1;
46                 private static char[] m_badCharacters = {'\"','<', '>', '|', '*', '?'};\r
47
48                 private bool m_Unrestricted = false;\r
49                 private FileIOPermissionAccess m_AllFilesAccess = FileIOPermissionAccess.NoAccess;\r
50                 private FileIOPermissionAccess m_AllLocalFilesAccess = FileIOPermissionAccess.NoAccess;\r
51                 private ArrayList readList;
52                 private ArrayList writeList;
53                 private ArrayList appendList;
54                 private ArrayList pathList;
55
56                 public FileIOPermission (PermissionState state)
57                 {\r
58                         if (CheckPermissionState (state, true) == PermissionState.Unrestricted) {
59                                 m_Unrestricted = true;\r
60                                 m_AllFilesAccess = FileIOPermissionAccess.AllAccess;\r
61                                 m_AllLocalFilesAccess = FileIOPermissionAccess.AllAccess;\r
62                         }
63                         CreateLists ();\r
64                 }\r
65 \r
66                 public FileIOPermission (FileIOPermissionAccess access, string path)
67                 {
68                         if (path == null)
69                                 throw new ArgumentNullException ("path");
70 \r
71                         CreateLists ();
72                         // access and path will be validated in AddPathList\r
73                         AddPathList (access, path);\r
74                 }\r
75 \r
76                 public FileIOPermission (FileIOPermissionAccess access, string[] pathList)
77                 {\r
78                         if (pathList == null)
79                                 throw new ArgumentNullException ("pathList");
80
81                         CreateLists ();\r
82                         // access and path will be validated in AddPathList\r
83                         AddPathList (access, pathList);\r
84                 }
85
86                 internal void CreateLists ()
87                 {
88                         readList = new ArrayList ();
89                         writeList = new ArrayList ();
90                         appendList = new ArrayList ();
91                         pathList = new ArrayList ();
92                 }
93
94 #if NET_2_0
95                 [MonoTODO ("Access Control isn't implemented")]
96                 public FileIOPermission (FileIOPermissionAccess access, AccessControlActions control, string path)
97                 {
98                         throw new NotImplementedException ();
99                 }\r
100 \r
101                 [MonoTODO ("Access Control isn't implemented")]
102                 public FileIOPermission (FileIOPermissionAccess access, AccessControlActions control, string[] pathList)
103                 {\r
104                         throw new NotImplementedException ();
105                 }
106 #endif\r
107 \r
108                 public FileIOPermissionAccess AllFiles {\r
109                         get { return m_AllFilesAccess; } \r
110                         set {\r
111                                 // if we are already set to unrestricted, don't change this property\r
112                                 if (!m_Unrestricted){\r
113                                         m_AllFilesAccess = value;\r
114                                 }\r
115                         }\r
116                 }\r
117 \r
118                 public FileIOPermissionAccess AllLocalFiles {\r
119                         get { return m_AllLocalFilesAccess; } \r
120                         set {\r
121                                 // if we are already set to unrestricted, don't change this property\r
122                                 if (!m_Unrestricted){\r
123                                         m_AllLocalFilesAccess = value;\r
124                                 }\r
125                         }\r
126                 }\r
127 \r
128                 public void AddPathList (FileIOPermissionAccess access, string path)
129                 {
130                         if ((FileIOPermissionAccess.AllAccess & access) != access)
131                                 ThrowInvalidFlag (access, true);
132                         ThrowIfInvalidPath (path);\r
133                         AddPathInternal (access, path);\r
134                 }\r
135 \r
136                 public void AddPathList (FileIOPermissionAccess access, string[] pathList)
137                 {\r
138                         if ((FileIOPermissionAccess.AllAccess & access) != access)
139                                 ThrowInvalidFlag (access, true);
140                         ThrowIfInvalidPath (pathList);\r
141                         foreach (string path in pathList) {
142                                 AddPathInternal (access, path);\r
143                         }\r
144                 }
145
146                 // internal to avoid duplicate checks
147                 internal void AddPathInternal (FileIOPermissionAccess access, string path)
148                 {
149                         path = Path.GetFullPath (path);
150 /*                      switch (access) {
151                                 case FileIOPermissionAccess.AllAccess:
152                                         readList.Add (path);
153                                         writeList.Add (path);
154                                         appendList.Add (path);
155                                         pathList.Add (path);
156                                         break;
157                                 case FileIOPermissionAccess.NoAccess:
158                                         // ??? unit tests doesn't show removal using NoAccess ???
159                                         break;
160                                 case FileIOPermissionAccess.Read:
161                                         readList.Add (path);
162                                         break;
163                                 case FileIOPermissionAccess.Write:
164                                         writeList.Add (path);
165                                         break;
166                                 case FileIOPermissionAccess.Append:
167                                         appendList.Add (path);
168                                         break;
169                                 case FileIOPermissionAccess.PathDiscovery:
170                                         pathList.Add (path);
171                                         break;
172                                 default:
173                                         ThrowInvalidFlag (access, true);
174                                         break;
175                         }*/
176
177                         if ((access & FileIOPermissionAccess.Read) == FileIOPermissionAccess.Read)
178                                 readList.Add (path);
179                         if ((access & FileIOPermissionAccess.Write) == FileIOPermissionAccess.Write)
180                                 writeList.Add (path);
181                         if ((access & FileIOPermissionAccess.Append) == FileIOPermissionAccess.Append)
182                                 appendList.Add (path);
183                         if ((access & FileIOPermissionAccess.PathDiscovery) == FileIOPermissionAccess.PathDiscovery)
184                                 pathList.Add (path);
185                 }
186
187                 public override IPermission Copy ()
188                 {\r
189                         if (m_Unrestricted)\r
190                                 return new FileIOPermission (PermissionState.Unrestricted);\r
191
192                         FileIOPermission copy = new FileIOPermission (PermissionState.None);
193                         copy.readList = (ArrayList) readList.Clone ();
194                         copy.writeList = (ArrayList) writeList.Clone ();
195                         copy.appendList = (ArrayList) appendList.Clone ();
196                         copy.pathList = (ArrayList) pathList.Clone ();
197                         copy.m_AllFilesAccess = m_AllFilesAccess;
198                         copy.m_AllLocalFilesAccess = m_AllLocalFilesAccess;
199                         return copy;\r
200                 }\r
201 \r
202                 public override void FromXml (SecurityElement esd)
203                 {\r
204                         // General validation in CodeAccessPermission
205                         CheckSecurityElement (esd, "esd", version, version);
206                         // Note: we do not (yet) care about the return value 
207                         // as we only accept version 1 (min/max values)
208
209                         if (IsUnrestricted (esd)) {\r
210                                 m_Unrestricted = true;\r
211                         }\r
212                         else{\r
213                                 m_Unrestricted = false;\r
214                                 string fileList = esd.Attribute ("Read");\r
215                                 string[] files;\r
216                                 if (fileList != null){\r
217                                         files = fileList.Split (';');\r
218                                         AddPathList (FileIOPermissionAccess.Read, files);\r
219                                 }\r
220                                 fileList = esd.Attribute ("Write");\r
221                                 if (fileList != null){\r
222                                         files = fileList.Split (';');\r
223                                         AddPathList (FileIOPermissionAccess.Write, files);\r
224                                 }\r
225                                 fileList = esd.Attribute ("Append");\r
226                                 if (fileList != null){\r
227                                         files = fileList.Split (';');\r
228                                         AddPathList (FileIOPermissionAccess.Append, files);\r
229                                 }\r
230                                 fileList = esd.Attribute ("PathDiscovery");\r
231                                 if (fileList != null){\r
232                                         files = fileList.Split (';');\r
233                                         AddPathList (FileIOPermissionAccess.PathDiscovery, files);\r
234                                 }\r
235                         }\r
236                 }\r
237 \r
238                 public string[] GetPathList (FileIOPermissionAccess access)
239                 {\r
240                         if ((FileIOPermissionAccess.AllAccess & access) != access)
241                                 ThrowInvalidFlag (access, true);
242
243                         //LAMESPEC: docs says it returns (semicolon separated) list, but return\r
244                         //type is array.  I think docs are wrong and it just returns an array
245                         ArrayList result = new ArrayList ();
246                         switch (access) {
247                                 case FileIOPermissionAccess.NoAccess:
248                                         break;
249                                 case FileIOPermissionAccess.Read:
250                                         result.AddRange (readList);
251                                         break;
252                                 case FileIOPermissionAccess.Write:
253                                         result.AddRange (writeList);
254                                         break;
255                                 case FileIOPermissionAccess.Append:
256                                         result.AddRange (appendList);
257                                         break;
258                                 case FileIOPermissionAccess.PathDiscovery:
259                                         result.AddRange (pathList);
260                                         break;
261                                 default:
262                                         ThrowInvalidFlag (access, false);
263                                         break;
264                         }
265                         return (result.Count > 0) ? (string[]) result.ToArray (typeof (string)) : null;\r
266                 }\r
267 \r
268                 public override IPermission Intersect (IPermission target)
269                 {
270                         FileIOPermission fiop = Cast (target);
271                         if (fiop == null)
272                                 return null;
273
274                         if (IsUnrestricted ())
275                                 return fiop.Copy ();
276                         if (fiop.IsUnrestricted ())
277                                 return Copy ();
278
279                         FileIOPermission result = new FileIOPermission (PermissionState.None);
280                         result.AllFiles = m_AllFilesAccess & fiop.AllFiles;
281                         result.AllLocalFiles = m_AllLocalFilesAccess & fiop.AllLocalFiles;
282
283                         IntersectKeys (readList, fiop.readList, result.readList);
284                         IntersectKeys (writeList, fiop.writeList, result.writeList);
285                         IntersectKeys (appendList, fiop.appendList, result.appendList);
286                         IntersectKeys (pathList, fiop.pathList, result.pathList);
287
288                         return (result.IsEmpty () ? null : result);
289                 }\r
290 \r
291 \r
292                 public override bool IsSubsetOf (IPermission target)
293                 {
294                         FileIOPermission fiop = Cast (target);
295                         if (fiop == null) 
296                                 return false;
297                         if (fiop.IsEmpty ())
298                                 return IsEmpty ();
299
300                         if (IsUnrestricted ())
301                                 return fiop.IsUnrestricted ();
302                         else if (fiop.IsUnrestricted ())
303                                 return true;
304
305                         if ((m_AllFilesAccess & fiop.AllFiles) != m_AllFilesAccess)
306                                 return false;
307                         if ((m_AllLocalFilesAccess & fiop.AllLocalFiles) != m_AllLocalFilesAccess)
308                                 return false;
309
310                         if (!KeyIsSubsetOf (appendList, fiop.appendList))
311                                 return false;
312                         if (!KeyIsSubsetOf (readList, fiop.readList))
313                                 return false;
314                         if (!KeyIsSubsetOf (writeList, fiop.writeList))
315                                 return false;
316                         if (!KeyIsSubsetOf (pathList, fiop.pathList))
317                                 return false;
318
319                         return true;
320                 }\r
321
322                 public bool IsUnrestricted ()
323                 {\r
324                         return m_Unrestricted;\r
325                 }\r
326 \r
327                 public void SetPathList (FileIOPermissionAccess access, string path)
328                 {
329                         if ((FileIOPermissionAccess.AllAccess & access) != access)
330                                 ThrowInvalidFlag (access, true);
331                         ThrowIfInvalidPath (path);\r
332                         // note: throw before clearing the actual list
333                         Clear (access);\r
334                         AddPathInternal (access, path);\r
335                 }\r
336                 \r
337                 public void SetPathList (FileIOPermissionAccess access, string[] pathList)
338                 {\r
339                         if ((FileIOPermissionAccess.AllAccess & access) != access)
340                                 ThrowInvalidFlag (access, true);
341                         ThrowIfInvalidPath (pathList);\r
342                         // note: throw before clearing the actual list
343                         Clear (access);
344                         foreach (string path in pathList)\r
345                                 AddPathInternal (access, path);\r
346                 }
347
348                 public override SecurityElement ToXml ()
349                 {\r
350                         SecurityElement se = Element (1);
351                         if (m_Unrestricted) {\r
352                                 se.AddAttribute ("Unrestricted", "true");\r
353                         }\r
354                         else {\r
355                                 string[] paths = GetPathList (FileIOPermissionAccess.Append);\r
356                                 if (null != paths && paths.Length > 0) {\r
357                                         se.AddAttribute ("Append", String.Join (";", paths));\r
358                                 }\r
359                                 paths = GetPathList (FileIOPermissionAccess.Read);\r
360                                 if (null != paths && paths.Length > 0) {\r
361                                         se.AddAttribute ("Read", String.Join (";", paths));\r
362                                 }\r
363                                 paths = GetPathList (FileIOPermissionAccess.Write);\r
364                                 if (null != paths && paths.Length > 0) {\r
365                                         se.AddAttribute ("Write", String.Join  (";", paths));\r
366                                 }\r
367                                 paths = GetPathList (FileIOPermissionAccess.PathDiscovery);\r
368                                 if (null != paths && paths.Length > 0) {\r
369                                         se.AddAttribute ("PathDiscovery", String.Join  (";", paths));\r
370                                 }\r
371                         }\r
372                         return se;\r
373                 }\r
374 \r
375                 public override IPermission Union (IPermission other)
376                 {\r
377                         FileIOPermission fiop = Cast (other);
378                         if (fiop == null)
379                                 return Copy ();
380
381                         if (IsUnrestricted () || fiop.IsUnrestricted ())
382                                 return new FileIOPermission (PermissionState.Unrestricted);
383
384                         if (IsEmpty () && fiop.IsEmpty ())
385                                 return null;
386
387                         FileIOPermission result = (FileIOPermission) Copy ();
388                         result.AllFiles |= fiop.AllFiles;
389                         result.AllLocalFiles |= fiop.AllLocalFiles;
390
391                         string[] paths = fiop.GetPathList (FileIOPermissionAccess.Read);
392                         if (paths != null) 
393                                 UnionKeys (result.readList, paths);
394
395                         paths = fiop.GetPathList (FileIOPermissionAccess.Write);
396                         if (paths != null)
397                                 UnionKeys (result.writeList, paths);
398
399                         paths = fiop.GetPathList (FileIOPermissionAccess.Append);
400                         if (paths != null) 
401                                 UnionKeys (result.appendList, paths);
402
403                         paths = fiop.GetPathList (FileIOPermissionAccess.PathDiscovery);
404                         if (paths != null) 
405                                 UnionKeys (result.pathList, paths);
406                         
407                         return result;
408                 }\r
409 \r
410 #if NET_2_0
411                 [MonoTODO]
412                 public override bool Equals (object obj)
413                 {
414                         return false;
415                 }
416
417                 [MonoTODO]
418                 public override int GetHashCode ()
419                 {
420                         return base.GetHashCode ();
421                 }
422 #endif
423
424                 // IBuiltInPermission\r
425                 int IBuiltInPermission.GetTokenIndex ()\r
426                 {\r
427                         return (int) BuiltInToken.FileIO;
428                 }
429
430                 // helpers
431
432                 private bool IsEmpty ()
433                 {
434                         return ((!m_Unrestricted) && (appendList.Count == 0) && (readList.Count == 0)
435                                 && (writeList.Count == 0) && (pathList.Count == 0));
436                 }
437
438                 private FileIOPermission Cast (IPermission target)
439                 {
440                         if (target == null)
441                                 return null;
442
443                         FileIOPermission fiop = (target as FileIOPermission);
444                         if (fiop == null) {
445                                 ThrowInvalidPermission (target, typeof (FileIOPermission));
446                         }
447
448                         return fiop;
449                 }
450
451                 internal void ThrowInvalidFlag (FileIOPermissionAccess access, bool context) 
452                 {
453                         string msg = null;
454                         if (context)
455                                 msg = Locale.GetText ("Unknown flag '{0}'.");
456                         else
457                                 msg = Locale.GetText ("Invalid flag '{0}' in this context.");
458                         throw new ArgumentException (String.Format (msg, access), "access");
459                 }
460
461                 internal void ThrowIfInvalidPath (string path)
462                 {
463                         if (path.LastIndexOfAny (m_badCharacters) >= 0) {\r
464                                 string msg = String.Format (Locale.GetText ("Invalid characters in path: '{0}'"), path);\r
465                                 throw new ArgumentException (msg, "path");\r
466                         }\r
467                         // LAMESPEC: docs don't say it must be a rooted path, but the MS implementation enforces it, so we will too.\r
468                         if (!Path.IsPathRooted (path)) {
469                                 string msg = Locale.GetText ("Absolute path information is required.");\r
470                                 throw new ArgumentException (msg, "path");\r
471                         }
472                 }
473 \r
474                 internal void ThrowIfInvalidPath (string[] paths)
475                 {
476                         foreach (string path in paths)
477                                 ThrowIfInvalidPath (path);
478                 }
479
480                 // we known that access is valid at this point
481                 internal void Clear (FileIOPermissionAccess access)
482                 {
483                         if ((access & FileIOPermissionAccess.Read) == FileIOPermissionAccess.Read)
484                                 readList.Clear ();
485                         if ((access & FileIOPermissionAccess.Write) == FileIOPermissionAccess.Write)
486                                 writeList.Clear ();
487                         if ((access & FileIOPermissionAccess.Append) == FileIOPermissionAccess.Append)
488                                 appendList.Clear ();
489                         if ((access & FileIOPermissionAccess.PathDiscovery) == FileIOPermissionAccess.PathDiscovery)
490                                 pathList.Clear ();
491                 }\r
492
493                 internal bool KeyIsSubsetOf (IList local, IList target)
494                 {
495                         bool result = false;
496                         foreach (string l in local) {
497                                 string c14nl = Path.GetFullPath (l);
498                                 foreach (string t in target) {
499                                         if (c14nl.StartsWith (Path.GetFullPath (t))) {
500                                                 result = true;
501                                                 break;
502                                         }
503                                 }
504                                 if (!result)
505                                         return false;
506                         }
507                         return true;
508                 }
509
510                 internal void UnionKeys (IList list, string[] paths)
511                 {
512                         foreach (string p in paths) {
513                                 // c14n
514                                 string path = Path.GetFullPath (p);
515                                 int len = list.Count;
516                                 if (len == 0) {
517                                         list.Add (path);
518                                 }
519                                 else {
520                                         for (int i=0; i < len; i++) {
521                                                 // c14n
522                                                 string s = Path.GetFullPath ((string) list [i]);
523                                                 if (s.StartsWith (path)) {
524                                                         // replace (with reduced version)
525                                                         list [i] = path;
526                                                         break;
527                                                 }
528                                                 else if (path.StartsWith (s)) {
529                                                         // no need to add
530                                                         break;
531                                                 }
532                                                 else {
533                                                         list.Add (path);
534                                                         break;
535                                                 }
536                                         }
537                                 }
538                         }
539                 }
540
541                 internal void IntersectKeys (IList local, IList target, IList result)
542                 {
543                         foreach (string l in local) {
544                                 foreach (string t in target) {
545                                         if (t.Length > l.Length) {
546                                                 if (t.StartsWith (l))
547                                                         result.Add (t);
548                                         }
549                                         else {
550                                                 if (l.StartsWith (t))
551                                                         result.Add (l);
552                                         }
553                                 }
554                         }
555                 }
556         }\r
557 }\r