[Microsoft.Build.Engine] Ignore import with empty project attribute when condition...
[mono.git] / mcs / class / corlib / System.Security.Permissions / FileIOPermission.cs
1 // 
2 // System.Security.Permissions.FileIOPermission.cs 
3 //
4 // Authors:
5 //      Nick Drochak, ndrochak@gol.com
6 //      Sebastien Pouliot  <sebastien@ximian.com>
7 //
8 // Copyright (C) 2001 Nick Drochak, All Rights Reserved
9 // Copyright (C) 2004-2005 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
31 using System.Collections;
32 using System.IO;
33 using System.Text;
34
35 using System.Runtime.InteropServices;
36 using System.Security.AccessControl;
37
38 namespace System.Security.Permissions {
39
40         [ComVisible (true)]
41         [Serializable]
42         public sealed class FileIOPermission
43                 : CodeAccessPermission, IBuiltInPermission, IUnrestrictedPermission {
44
45                 private const int version = 1;
46
47                 private static char[] BadPathNameCharacters;
48                 private static char[] BadFileNameCharacters;
49
50                 static FileIOPermission ()
51                 {
52                         // we keep a local (static) copies to avoid calls/allocations
53                         BadPathNameCharacters = Path.GetInvalidPathChars ();
54                         BadFileNameCharacters = Path.GetInvalidFileNameChars ();
55                 }
56
57                 private bool m_Unrestricted = false;
58                 private FileIOPermissionAccess m_AllFilesAccess = FileIOPermissionAccess.NoAccess;
59                 private FileIOPermissionAccess m_AllLocalFilesAccess = FileIOPermissionAccess.NoAccess;
60                 private ArrayList readList;
61                 private ArrayList writeList;
62                 private ArrayList appendList;
63                 private ArrayList pathList;
64
65                 public FileIOPermission (PermissionState state)
66                 {
67                         if (CheckPermissionState (state, true) == PermissionState.Unrestricted) {
68                                 m_Unrestricted = true;
69                                 m_AllFilesAccess = FileIOPermissionAccess.AllAccess;
70                                 m_AllLocalFilesAccess = FileIOPermissionAccess.AllAccess;
71                         }
72                         CreateLists ();
73                 }
74
75                 public FileIOPermission (FileIOPermissionAccess access, string path)
76                 {
77                         if (path == null)
78                                 throw new ArgumentNullException ("path");
79
80                         CreateLists ();
81                         // access and path will be validated in AddPathList
82                         AddPathList (access, path);
83                 }
84
85                 public FileIOPermission (FileIOPermissionAccess access, string[] pathList)
86                 {
87                         if (pathList == null)
88                                 throw new ArgumentNullException ("pathList");
89
90                         CreateLists ();
91                         // access and path will be validated in AddPathList
92                         AddPathList (access, pathList);
93                 }
94
95                 internal void CreateLists ()
96                 {
97                         readList = new ArrayList ();
98                         writeList = new ArrayList ();
99                         appendList = new ArrayList ();
100                         pathList = new ArrayList ();
101                 }
102
103                 [MonoTODO ("(2.0) Access Control isn't implemented")]
104                 public FileIOPermission (FileIOPermissionAccess access, AccessControlActions control, string path)
105                 {
106                         throw new NotImplementedException ();
107                 }
108
109                 [MonoTODO ("(2.0) Access Control isn't implemented")]
110                 public FileIOPermission (FileIOPermissionAccess access, AccessControlActions control, string[] pathList)
111                 {
112                         throw new NotImplementedException ();
113                 }
114
115                 public FileIOPermissionAccess AllFiles {
116                         get { return m_AllFilesAccess; } 
117                         set {
118                                 // if we are already set to unrestricted, don't change this property
119                                 if (!m_Unrestricted){
120                                         m_AllFilesAccess = value;
121                                 }
122                         }
123                 }
124
125                 public FileIOPermissionAccess AllLocalFiles {
126                         get { return m_AllLocalFilesAccess; } 
127                         set {
128                                 // if we are already set to unrestricted, don't change this property
129                                 if (!m_Unrestricted){
130                                         m_AllLocalFilesAccess = value;
131                                 }
132                         }
133                 }
134
135                 public void AddPathList (FileIOPermissionAccess access, string path)
136                 {
137                         if ((FileIOPermissionAccess.AllAccess & access) != access)
138                                 ThrowInvalidFlag (access, true);
139                         ThrowIfInvalidPath (path);
140                         AddPathInternal (access, path);
141                 }
142
143                 public void AddPathList (FileIOPermissionAccess access, string[] pathList)
144                 {
145                         if ((FileIOPermissionAccess.AllAccess & access) != access)
146                                 ThrowInvalidFlag (access, true);
147                         ThrowIfInvalidPath (pathList);
148                         foreach (string path in pathList) {
149                                 AddPathInternal (access, path);
150                         }
151                 }
152
153                 // internal to avoid duplicate checks
154                 internal void AddPathInternal (FileIOPermissionAccess access, string path)
155                 {
156                         // call InsecureGetFullPath (and not GetFullPath) to avoid recursion
157                         path = Path.InsecureGetFullPath (path);
158
159                         if ((access & FileIOPermissionAccess.Read) == FileIOPermissionAccess.Read)
160                                 readList.Add (path);
161                         if ((access & FileIOPermissionAccess.Write) == FileIOPermissionAccess.Write)
162                                 writeList.Add (path);
163                         if ((access & FileIOPermissionAccess.Append) == FileIOPermissionAccess.Append)
164                                 appendList.Add (path);
165                         if ((access & FileIOPermissionAccess.PathDiscovery) == FileIOPermissionAccess.PathDiscovery)
166                                 pathList.Add (path);
167                 }
168
169                 public override IPermission Copy ()
170                 {
171                         if (m_Unrestricted)
172                                 return new FileIOPermission (PermissionState.Unrestricted);
173
174                         FileIOPermission copy = new FileIOPermission (PermissionState.None);
175                         copy.readList = (ArrayList) readList.Clone ();
176                         copy.writeList = (ArrayList) writeList.Clone ();
177                         copy.appendList = (ArrayList) appendList.Clone ();
178                         copy.pathList = (ArrayList) pathList.Clone ();
179                         copy.m_AllFilesAccess = m_AllFilesAccess;
180                         copy.m_AllLocalFilesAccess = m_AllLocalFilesAccess;
181                         return copy;
182                 }
183
184                 public override void FromXml (SecurityElement esd)
185                 {
186                         // General validation in CodeAccessPermission
187                         CheckSecurityElement (esd, "esd", version, version);
188                         // Note: we do not (yet) care about the return value 
189                         // as we only accept version 1 (min/max values)
190
191                         if (IsUnrestricted (esd)) {
192                                 m_Unrestricted = true;
193                         }
194                         else{
195                                 m_Unrestricted = false;
196                                 string fileList = esd.Attribute ("Read");
197                                 string[] files;
198                                 if (fileList != null){
199                                         files = fileList.Split (';');
200                                         AddPathList (FileIOPermissionAccess.Read, files);
201                                 }
202                                 fileList = esd.Attribute ("Write");
203                                 if (fileList != null){
204                                         files = fileList.Split (';');
205                                         AddPathList (FileIOPermissionAccess.Write, files);
206                                 }
207                                 fileList = esd.Attribute ("Append");
208                                 if (fileList != null){
209                                         files = fileList.Split (';');
210                                         AddPathList (FileIOPermissionAccess.Append, files);
211                                 }
212                                 fileList = esd.Attribute ("PathDiscovery");
213                                 if (fileList != null){
214                                         files = fileList.Split (';');
215                                         AddPathList (FileIOPermissionAccess.PathDiscovery, files);
216                                 }
217                         }
218                 }
219
220                 public string[] GetPathList (FileIOPermissionAccess access)
221                 {
222                         if ((FileIOPermissionAccess.AllAccess & access) != access)
223                                 ThrowInvalidFlag (access, true);
224
225                         ArrayList result = new ArrayList ();
226                         switch (access) {
227                                 case FileIOPermissionAccess.NoAccess:
228                                         break;
229                                 case FileIOPermissionAccess.Read:
230                                         result.AddRange (readList);
231                                         break;
232                                 case FileIOPermissionAccess.Write:
233                                         result.AddRange (writeList);
234                                         break;
235                                 case FileIOPermissionAccess.Append:
236                                         result.AddRange (appendList);
237                                         break;
238                                 case FileIOPermissionAccess.PathDiscovery:
239                                         result.AddRange (pathList);
240                                         break;
241                                 default:
242                                         ThrowInvalidFlag (access, false);
243                                         break;
244                         }
245                         return (result.Count > 0) ? (string[]) result.ToArray (typeof (string)) : null;
246                 }
247
248                 public override IPermission Intersect (IPermission target)
249                 {
250                         FileIOPermission fiop = Cast (target);
251                         if (fiop == null)
252                                 return null;
253
254                         if (IsUnrestricted ())
255                                 return fiop.Copy ();
256                         if (fiop.IsUnrestricted ())
257                                 return Copy ();
258
259                         FileIOPermission result = new FileIOPermission (PermissionState.None);
260                         result.AllFiles = m_AllFilesAccess & fiop.AllFiles;
261                         result.AllLocalFiles = m_AllLocalFilesAccess & fiop.AllLocalFiles;
262
263                         IntersectKeys (readList, fiop.readList, result.readList);
264                         IntersectKeys (writeList, fiop.writeList, result.writeList);
265                         IntersectKeys (appendList, fiop.appendList, result.appendList);
266                         IntersectKeys (pathList, fiop.pathList, result.pathList);
267
268                         return (result.IsEmpty () ? null : result);
269                 }
270
271                 public override bool IsSubsetOf (IPermission target)
272                 {
273                         FileIOPermission fiop = Cast (target);
274                         if (fiop == null) 
275                                 return false;
276                         if (fiop.IsEmpty ())
277                                 return IsEmpty ();
278
279                         if (IsUnrestricted ())
280                                 return fiop.IsUnrestricted ();
281                         else if (fiop.IsUnrestricted ())
282                                 return true;
283
284                         if ((m_AllFilesAccess & fiop.AllFiles) != m_AllFilesAccess)
285                                 return false;
286                         if ((m_AllLocalFilesAccess & fiop.AllLocalFiles) != m_AllLocalFilesAccess)
287                                 return false;
288
289                         if (!KeyIsSubsetOf (appendList, fiop.appendList))
290                                 return false;
291                         if (!KeyIsSubsetOf (readList, fiop.readList))
292                                 return false;
293                         if (!KeyIsSubsetOf (writeList, fiop.writeList))
294                                 return false;
295                         if (!KeyIsSubsetOf (pathList, fiop.pathList))
296                                 return false;
297
298                         return true;
299                 }
300
301                 public bool IsUnrestricted ()
302                 {
303                         return m_Unrestricted;
304                 }
305
306                 public void SetPathList (FileIOPermissionAccess access, string path)
307                 {
308                         if ((FileIOPermissionAccess.AllAccess & access) != access)
309                                 ThrowInvalidFlag (access, true);
310                         ThrowIfInvalidPath (path);
311                         // note: throw before clearing the actual list
312                         Clear (access);
313                         AddPathInternal (access, path);
314                 }
315                 
316                 public void SetPathList (FileIOPermissionAccess access, string[] pathList)
317                 {
318                         if ((FileIOPermissionAccess.AllAccess & access) != access)
319                                 ThrowInvalidFlag (access, true);
320                         ThrowIfInvalidPath (pathList);
321                         // note: throw before clearing the actual list
322                         Clear (access);
323                         foreach (string path in pathList)
324                                 AddPathInternal (access, path);
325                 }
326
327                 public override SecurityElement ToXml ()
328                 {
329                         SecurityElement se = Element (1);
330                         if (m_Unrestricted) {
331                                 se.AddAttribute ("Unrestricted", "true");
332                         }
333                         else {
334                                 string[] paths = GetPathList (FileIOPermissionAccess.Append);
335                                 if (null != paths && paths.Length > 0) {
336                                         se.AddAttribute ("Append", String.Join (";", paths));
337                                 }
338                                 paths = GetPathList (FileIOPermissionAccess.Read);
339                                 if (null != paths && paths.Length > 0) {
340                                         se.AddAttribute ("Read", String.Join (";", paths));
341                                 }
342                                 paths = GetPathList (FileIOPermissionAccess.Write);
343                                 if (null != paths && paths.Length > 0) {
344                                         se.AddAttribute ("Write", String.Join  (";", paths));
345                                 }
346                                 paths = GetPathList (FileIOPermissionAccess.PathDiscovery);
347                                 if (null != paths && paths.Length > 0) {
348                                         se.AddAttribute ("PathDiscovery", String.Join  (";", paths));
349                                 }
350                         }
351                         return se;
352                 }
353
354                 public override IPermission Union (IPermission other)
355                 {
356                         FileIOPermission fiop = Cast (other);
357                         if (fiop == null)
358                                 return Copy ();
359
360                         if (IsUnrestricted () || fiop.IsUnrestricted ())
361                                 return new FileIOPermission (PermissionState.Unrestricted);
362
363                         if (IsEmpty () && fiop.IsEmpty ())
364                                 return null;
365
366                         FileIOPermission result = (FileIOPermission) Copy ();
367                         result.AllFiles |= fiop.AllFiles;
368                         result.AllLocalFiles |= fiop.AllLocalFiles;
369
370                         string[] paths = fiop.GetPathList (FileIOPermissionAccess.Read);
371                         if (paths != null) 
372                                 UnionKeys (result.readList, paths);
373
374                         paths = fiop.GetPathList (FileIOPermissionAccess.Write);
375                         if (paths != null)
376                                 UnionKeys (result.writeList, paths);
377
378                         paths = fiop.GetPathList (FileIOPermissionAccess.Append);
379                         if (paths != null) 
380                                 UnionKeys (result.appendList, paths);
381
382                         paths = fiop.GetPathList (FileIOPermissionAccess.PathDiscovery);
383                         if (paths != null) 
384                                 UnionKeys (result.pathList, paths);
385                         
386                         return result;
387                 }
388
389                 [MonoTODO ("(2.0)")]
390                 [ComVisible (false)]
391                 public override bool Equals (object obj)
392                 {
393                         return false;
394                 }
395
396                 [MonoTODO ("(2.0)")]
397                 [ComVisible (false)]
398                 public override int GetHashCode ()
399                 {
400                         return base.GetHashCode ();
401                 }
402
403                 // IBuiltInPermission
404                 int IBuiltInPermission.GetTokenIndex ()
405                 {
406                         return (int) BuiltInToken.FileIO;
407                 }
408
409                 // helpers
410
411                 private bool IsEmpty ()
412                 {
413                         return ((!m_Unrestricted) && (appendList.Count == 0) && (readList.Count == 0)
414                                 && (writeList.Count == 0) && (pathList.Count == 0));
415                 }
416
417                 private static FileIOPermission Cast (IPermission target)
418                 {
419                         if (target == null)
420                                 return null;
421
422                         FileIOPermission fiop = (target as FileIOPermission);
423                         if (fiop == null) {
424                                 ThrowInvalidPermission (target, typeof (FileIOPermission));
425                         }
426
427                         return fiop;
428                 }
429
430                 internal static void ThrowInvalidFlag (FileIOPermissionAccess access, bool context) 
431                 {
432                         string msg = null;
433                         if (context)
434                                 msg = Locale.GetText ("Unknown flag '{0}'.");
435                         else
436                                 msg = Locale.GetText ("Invalid flag '{0}' in this context.");
437                         throw new ArgumentException (String.Format (msg, access), "access");
438                 }
439
440                 internal static void ThrowIfInvalidPath (string path)
441                 {
442                         string dir = Path.GetDirectoryName (path);
443                         if ((dir != null) && (dir.LastIndexOfAny (BadPathNameCharacters) >= 0)) {
444                                 string msg = String.Format (Locale.GetText ("Invalid path characters in path: '{0}'"), path);
445                                 throw new ArgumentException (msg, "path");
446                         }
447
448                         string fname = Path.GetFileName (path);
449                         if ((fname != null) && (fname.LastIndexOfAny (BadFileNameCharacters) >= 0)) {
450                                 string msg = String.Format (Locale.GetText ("Invalid filename characters in path: '{0}'"), path);
451                                 throw new ArgumentException (msg, "path");
452                         }
453                         // LAMESPEC: docs don't say it must be a rooted path, but the MS implementation enforces it, so we will too.
454                         if (!Path.IsPathRooted (path)) {
455                                 string msg = Locale.GetText ("Absolute path information is required.");
456                                 throw new ArgumentException (msg, "path");
457                         }
458                 }
459
460                 internal static void ThrowIfInvalidPath (string[] paths)
461                 {
462                         foreach (string path in paths)
463                                 ThrowIfInvalidPath (path);
464                 }
465
466                 // we known that access is valid at this point
467                 internal void Clear (FileIOPermissionAccess access)
468                 {
469                         if ((access & FileIOPermissionAccess.Read) == FileIOPermissionAccess.Read)
470                                 readList.Clear ();
471                         if ((access & FileIOPermissionAccess.Write) == FileIOPermissionAccess.Write)
472                                 writeList.Clear ();
473                         if ((access & FileIOPermissionAccess.Append) == FileIOPermissionAccess.Append)
474                                 appendList.Clear ();
475                         if ((access & FileIOPermissionAccess.PathDiscovery) == FileIOPermissionAccess.PathDiscovery)
476                                 pathList.Clear ();
477                 }
478
479                 // note: all path in IList are already "full paths"
480                 internal static bool KeyIsSubsetOf (IList local, IList target)
481                 {
482                         bool result = false;
483                         foreach (string l in local) {
484                                 foreach (string t in target) {
485                                         if (Path.IsPathSubsetOf (t, l)) {
486                                                 result = true;
487                                                 break;
488                                         }
489                                 }
490                                 if (!result)
491                                         return false;
492                         }
493                         return true;
494                 }
495
496                 internal static void UnionKeys (IList list, string[] paths)
497                 {
498                         foreach (string path in paths) {
499                                 int len = list.Count;
500                                 if (len == 0) {
501                                         list.Add (path);
502                                 }
503                                 else {
504                                         int i;
505                                         for (i=0; i < len; i++) {
506                                                 string s = (string) list [i];
507                                                 if (Path.IsPathSubsetOf (path, s)) {
508                                                         // replace (with reduced version)
509                                                         list [i] = path;
510                                                         break;
511                                                 }
512                                                 else if (Path.IsPathSubsetOf (s, path)) {
513                                                         // no need to add
514                                                         break;
515                                                 }
516                                         }
517                                         if (i == len) {
518                                                 list.Add (path);
519                                         }
520                                 }
521                         }
522                 }
523
524                 internal static void IntersectKeys (IList local, IList target, IList result)
525                 {
526                         foreach (string l in local) {
527                                 foreach (string t in target) {
528                                         if (t.Length > l.Length) {
529                                                 if (Path.IsPathSubsetOf (l ,t))
530                                                         result.Add (t);
531                                         }
532                                         else {
533                                                 if (Path.IsPathSubsetOf (t, l))
534                                                         result.Add (l);
535                                         }
536                                 }
537                         }
538                 }
539         }
540 }