Merge pull request #1898 from alexanderkyte/debugger_variable_reflection
[mono.git] / mcs / class / corlib / System.IO.IsolatedStorage / IsolatedStorageFile.cs
1 //
2 // System.IO.IsolatedStorage.IsolatedStorageFile
3 //
4 // Authors
5 //      Jonathan Pryor (jonpryor@vt.edu)
6 //      Sebastien Pouliot  <sebastien@ximian.com>
7 //
8 // (C) 2003 Jonathan Pryor
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 using System.Collections;
31 using System.Reflection;
32 using System.Runtime.InteropServices;
33 using System.Runtime.Serialization.Formatters.Binary;
34 using System.Security;
35 using System.Security.Cryptography;
36 using System.Security.Permissions;
37 using System.Security.Policy;
38 using System.Text;
39 using System.Threading;
40
41 using Mono.Security.Cryptography;
42
43 namespace System.IO.IsolatedStorage {
44
45         // This is a terribly named class.  It doesn't actually represent a file as
46         // much as a directory
47
48
49         [ComVisible (true)]
50         // FIXME: Further limit the assertion when imperative Assert is implemented
51         [FileIOPermission (SecurityAction.Assert, Unrestricted = true)]
52         public sealed class IsolatedStorageFile : IsolatedStorage, IDisposable {
53 #if !MOBILE
54                 private bool _resolved;
55                 private ulong _maxSize;
56                 private Evidence _fullEvidences;
57                 private static readonly Mutex mutex = new Mutex ();
58 #endif
59                 
60                 private bool closed;
61                 private bool disposed;
62
63                 public static IEnumerator GetEnumerator (IsolatedStorageScope scope)
64                 {
65                         Demand (scope);
66
67                         switch (scope) {
68                         case IsolatedStorageScope.User:
69                         case IsolatedStorageScope.User | IsolatedStorageScope.Roaming:
70                         case IsolatedStorageScope.Machine:
71                                 break;
72                         default:
73                                 string msg = Locale.GetText ("Invalid scope, only User, User|Roaming and Machine are valid");
74                                 throw new ArgumentException (msg);
75                         }
76
77                         return new IsolatedStorageFileEnumerator (scope, GetIsolatedStorageRoot (scope));
78                 }
79
80                 public static IsolatedStorageFile GetStore (IsolatedStorageScope scope,
81                         Evidence domainEvidence, Type domainEvidenceType,
82                         Evidence assemblyEvidence, Type assemblyEvidenceType)
83                 {
84                         Demand (scope);
85
86                         bool domain = ((scope & IsolatedStorageScope.Domain) != 0);
87                         if (domain && (domainEvidence == null))
88                                 throw new ArgumentNullException ("domainEvidence");
89
90                         bool assembly = ((scope & IsolatedStorageScope.Assembly) != 0);
91                         if (assembly && (assemblyEvidence == null))
92                                 throw new ArgumentNullException ("assemblyEvidence");
93
94                         IsolatedStorageFile storageFile = new IsolatedStorageFile (scope);
95 #if !MOBILE
96                         if (domain) {
97                                 if (domainEvidenceType == null) {
98                                         storageFile._domainIdentity = GetDomainIdentityFromEvidence (domainEvidence);
99                                 } else {
100                                         storageFile._domainIdentity = GetTypeFromEvidence (domainEvidence, domainEvidenceType);
101                                 }
102
103                                 if (storageFile._domainIdentity == null)
104                                         throw new IsolatedStorageException (Locale.GetText ("Couldn't find domain identity."));
105                         }
106
107                         if (assembly) {
108                                 if (assemblyEvidenceType == null) {
109                                         storageFile._assemblyIdentity = GetAssemblyIdentityFromEvidence (assemblyEvidence);
110                                 } else {
111                                         storageFile._assemblyIdentity = GetTypeFromEvidence (assemblyEvidence, assemblyEvidenceType);
112                                 }
113
114                                 if (storageFile._assemblyIdentity == null)
115                                         throw new IsolatedStorageException (Locale.GetText ("Couldn't find assembly identity."));
116                         }
117 #endif
118                         storageFile.PostInit ();
119                         return storageFile;
120                 }
121
122                 public static IsolatedStorageFile GetStore (IsolatedStorageScope scope, object domainIdentity, object assemblyIdentity)
123                 {
124                         Demand (scope);
125
126                         if (((scope & IsolatedStorageScope.Domain) != 0) && (domainIdentity == null))
127                                 throw new ArgumentNullException ("domainIdentity");
128
129                         bool assembly = ((scope & IsolatedStorageScope.Assembly) != 0);
130                         if (assembly && (assemblyIdentity == null))
131                                 throw new ArgumentNullException ("assemblyIdentity");
132
133                         IsolatedStorageFile storageFile = new IsolatedStorageFile (scope);
134 #if !MOBILE
135                         if (assembly)
136                                 storageFile._fullEvidences = Assembly.GetCallingAssembly ().UnprotectedGetEvidence ();
137 #endif
138                         storageFile._domainIdentity = domainIdentity;
139                         storageFile._assemblyIdentity = assemblyIdentity;
140                         storageFile.PostInit ();
141                         return storageFile;
142                 }
143
144                 public static IsolatedStorageFile GetStore (IsolatedStorageScope scope, Type domainEvidenceType, Type assemblyEvidenceType)
145                 {
146                         Demand (scope);
147                         IsolatedStorageFile storageFile = new IsolatedStorageFile (scope);
148 #if !MOBILE
149                         if ((scope & IsolatedStorageScope.Domain) != 0) {
150                                 if (domainEvidenceType == null)
151                                         domainEvidenceType = typeof (Url);
152                                 storageFile._domainIdentity = GetTypeFromEvidence (AppDomain.CurrentDomain.Evidence, domainEvidenceType);
153                         }
154                         if ((scope & IsolatedStorageScope.Assembly) != 0) {
155                                 Evidence e = Assembly.GetCallingAssembly ().UnprotectedGetEvidence ();
156                                 storageFile._fullEvidences = e;
157                                 if ((scope & IsolatedStorageScope.Domain) != 0) {
158                                         if (assemblyEvidenceType == null)
159                                                 assemblyEvidenceType = typeof (Url);
160                                         storageFile._assemblyIdentity = GetTypeFromEvidence (e, assemblyEvidenceType);
161                                 } else {
162                                         storageFile._assemblyIdentity = GetAssemblyIdentityFromEvidence (e);
163                                 }
164                         }
165 #endif
166                         storageFile.PostInit ();
167                         return storageFile;
168                 }
169                 public static IsolatedStorageFile GetStore (IsolatedStorageScope scope, object applicationIdentity)
170                 {
171                         Demand (scope);
172                         if (applicationIdentity == null)
173                                 throw new ArgumentNullException ("applicationIdentity");
174
175                         IsolatedStorageFile storageFile = new IsolatedStorageFile (scope);
176                         storageFile._applicationIdentity = applicationIdentity;
177 #if !MOBILE
178                         storageFile._fullEvidences = Assembly.GetCallingAssembly ().UnprotectedGetEvidence ();
179 #endif
180                         storageFile.PostInit ();
181                         return storageFile;
182                 }
183
184                 public static IsolatedStorageFile GetStore (IsolatedStorageScope scope, Type applicationEvidenceType)
185                 {
186                         Demand (scope);
187                         IsolatedStorageFile storageFile = new IsolatedStorageFile (scope);
188                         storageFile.InitStore (scope, applicationEvidenceType);
189 #if !MOBILE
190                         storageFile._fullEvidences = Assembly.GetCallingAssembly ().UnprotectedGetEvidence ();
191 #endif
192                         storageFile.PostInit ();
193                         return storageFile;
194                 }
195
196                 [IsolatedStorageFilePermission (SecurityAction.Demand, UsageAllowed = IsolatedStorageContainment.ApplicationIsolationByMachine)]
197                 public static IsolatedStorageFile GetMachineStoreForApplication ()
198                 {
199                         IsolatedStorageScope scope = IsolatedStorageScope.Machine | IsolatedStorageScope.Application;
200                         IsolatedStorageFile storageFile = new IsolatedStorageFile (scope);
201                         storageFile.InitStore (scope, null);
202 #if !MOBILE
203                         storageFile._fullEvidences = Assembly.GetCallingAssembly ().UnprotectedGetEvidence ();
204 #endif
205                         storageFile.PostInit ();
206                         return storageFile;
207                 }
208
209                 [IsolatedStorageFilePermission (SecurityAction.Demand, UsageAllowed = IsolatedStorageContainment.AssemblyIsolationByMachine)]
210                 public static IsolatedStorageFile GetMachineStoreForAssembly ()
211                 {
212                         IsolatedStorageScope scope = IsolatedStorageScope.Machine | IsolatedStorageScope.Assembly;
213                         IsolatedStorageFile storageFile = new IsolatedStorageFile (scope);
214 #if !MOBILE
215                         Evidence e = Assembly.GetCallingAssembly ().UnprotectedGetEvidence ();
216                         storageFile._fullEvidences = e;
217                         storageFile._assemblyIdentity = GetAssemblyIdentityFromEvidence (e);
218 #endif
219                         storageFile.PostInit ();
220                         return storageFile;
221                 }
222
223                 [IsolatedStorageFilePermission (SecurityAction.Demand, UsageAllowed = IsolatedStorageContainment.DomainIsolationByMachine)]
224                 public static IsolatedStorageFile GetMachineStoreForDomain ()
225                 {
226                         IsolatedStorageScope scope = IsolatedStorageScope.Machine | IsolatedStorageScope.Domain | IsolatedStorageScope.Assembly;
227                         IsolatedStorageFile storageFile = new IsolatedStorageFile (scope);
228 #if !MOBILE
229                         storageFile._domainIdentity = GetDomainIdentityFromEvidence (AppDomain.CurrentDomain.Evidence);
230                         Evidence e = Assembly.GetCallingAssembly ().UnprotectedGetEvidence ();
231                         storageFile._fullEvidences = e;
232                         storageFile._assemblyIdentity = GetAssemblyIdentityFromEvidence (e);
233 #endif
234                         storageFile.PostInit ();
235                         return storageFile;
236                 }
237
238                 [IsolatedStorageFilePermission (SecurityAction.Demand, UsageAllowed = IsolatedStorageContainment.ApplicationIsolationByUser)]
239                 public static IsolatedStorageFile GetUserStoreForApplication ()
240                 {
241                         IsolatedStorageScope scope = IsolatedStorageScope.User | IsolatedStorageScope.Application;
242                         IsolatedStorageFile storageFile = new IsolatedStorageFile (scope);
243                         storageFile.InitStore (scope, null);
244 #if !MOBILE
245                         storageFile._fullEvidences = Assembly.GetCallingAssembly ().UnprotectedGetEvidence ();
246 #endif
247                         storageFile.PostInit ();
248                         return storageFile;
249                 }
250
251                 [IsolatedStorageFilePermission (SecurityAction.Demand, UsageAllowed = IsolatedStorageContainment.AssemblyIsolationByUser)]
252                 public static IsolatedStorageFile GetUserStoreForAssembly ()
253                 {
254                         IsolatedStorageScope scope = IsolatedStorageScope.User | IsolatedStorageScope.Assembly;
255                         IsolatedStorageFile storageFile = new IsolatedStorageFile (scope);
256 #if !MOBILE
257                         Evidence e = Assembly.GetCallingAssembly ().UnprotectedGetEvidence ();
258                         storageFile._fullEvidences = e;
259                         storageFile._assemblyIdentity = GetAssemblyIdentityFromEvidence (e);
260 #endif
261                         storageFile.PostInit ();
262                         return storageFile;
263                 }
264
265                 [IsolatedStorageFilePermission (SecurityAction.Demand, UsageAllowed = IsolatedStorageContainment.DomainIsolationByUser)]
266                 public static IsolatedStorageFile GetUserStoreForDomain ()
267                 {
268                         IsolatedStorageScope scope = IsolatedStorageScope.User | IsolatedStorageScope.Domain | IsolatedStorageScope.Assembly;
269                         IsolatedStorageFile storageFile = new IsolatedStorageFile (scope);
270 #if !MOBILE
271                         storageFile._domainIdentity = GetDomainIdentityFromEvidence (AppDomain.CurrentDomain.Evidence);
272                         Evidence e = Assembly.GetCallingAssembly ().UnprotectedGetEvidence ();
273                         storageFile._fullEvidences = e;
274                         storageFile._assemblyIdentity = GetAssemblyIdentityFromEvidence (e);
275 #endif
276                         storageFile.PostInit ();
277                         return storageFile;
278                 }
279
280                 [ComVisible (false)]
281                 public static IsolatedStorageFile GetUserStoreForSite ()
282                 {
283                         throw new NotSupportedException ();
284                 }
285
286                 public static void Remove (IsolatedStorageScope scope)
287                 {
288                         string dir = GetIsolatedStorageRoot (scope);
289                         if (!Directory.Exists (dir))
290                                 return;
291
292                         try {
293                                 Directory.Delete (dir, true);
294                         } catch (IOException) {
295                                 throw new IsolatedStorageException ("Could not remove storage.");
296                         }
297                 }
298
299                 // internal static stuff
300
301                 // Security Note: We're using InternalGetFolderPath because 
302                 // IsolatedStorage must be able to work even if we do not have
303                 // FileIOPermission's PathDiscovery permissions
304                 internal static string GetIsolatedStorageRoot (IsolatedStorageScope scope)
305                 {
306                         // IsolatedStorageScope mixes several flags into one.
307                         // This first level deals with the root directory - it
308                         // is decided based on User, User+Roaming or Machine
309                         string root = null;
310
311                         if ((scope & IsolatedStorageScope.User) != 0) {
312                                 if ((scope & IsolatedStorageScope.Roaming) != 0) {
313                                         root = Environment.UnixGetFolderPath (Environment.SpecialFolder.LocalApplicationData, Environment.SpecialFolderOption.Create);
314                                 } else {
315                                         root = Environment.UnixGetFolderPath (Environment.SpecialFolder.ApplicationData, Environment.SpecialFolderOption.Create);
316                                 }
317                         } else if ((scope & IsolatedStorageScope.Machine) != 0) {
318                                 root = Environment.UnixGetFolderPath (Environment.SpecialFolder.CommonApplicationData, Environment.SpecialFolderOption.Create);
319                         }
320
321                         if (root == null) {
322                                 string msg = Locale.GetText ("Couldn't access storage location for '{0}'.");
323                                 throw new IsolatedStorageException (String.Format (msg, scope));
324                         }
325
326                         return Path.Combine (root, ".isolated-storage");
327                 }
328
329                 private static void Demand (IsolatedStorageScope scope)
330                 {
331 #if !MOBILE
332                         if (SecurityManager.SecurityEnabled) {
333                                 IsolatedStorageFilePermission isfp = new IsolatedStorageFilePermission (PermissionState.None);
334                                 isfp.UsageAllowed = ScopeToContainment (scope);
335                                 isfp.Demand ();
336                         }
337 #endif
338                 }
339
340                 private static IsolatedStorageContainment ScopeToContainment (IsolatedStorageScope scope)
341                 {
342                         switch (scope) {
343                         case IsolatedStorageScope.Domain | IsolatedStorageScope.Assembly | IsolatedStorageScope.User:
344                                 return IsolatedStorageContainment.DomainIsolationByUser;
345                         case IsolatedStorageScope.Assembly | IsolatedStorageScope.User:
346                                 return IsolatedStorageContainment.AssemblyIsolationByUser;
347                         case IsolatedStorageScope.Domain | IsolatedStorageScope.Assembly | IsolatedStorageScope.User | IsolatedStorageScope.Roaming:
348                                 return IsolatedStorageContainment.DomainIsolationByRoamingUser;
349                         case IsolatedStorageScope.Assembly | IsolatedStorageScope.User | IsolatedStorageScope.Roaming:
350                                 return IsolatedStorageContainment.AssemblyIsolationByRoamingUser;
351                         case IsolatedStorageScope.Application | IsolatedStorageScope.User:
352                                 return IsolatedStorageContainment.ApplicationIsolationByUser;
353                         case IsolatedStorageScope.Domain | IsolatedStorageScope.Assembly | IsolatedStorageScope.Machine:
354                                 return IsolatedStorageContainment.DomainIsolationByMachine;
355                         case IsolatedStorageScope.Assembly | IsolatedStorageScope.Machine:
356                                 return IsolatedStorageContainment.AssemblyIsolationByMachine;
357                         case IsolatedStorageScope.Application | IsolatedStorageScope.Machine:
358                                 return IsolatedStorageContainment.ApplicationIsolationByMachine;
359                         case IsolatedStorageScope.Application | IsolatedStorageScope.User | IsolatedStorageScope.Roaming:
360                                 return IsolatedStorageContainment.ApplicationIsolationByRoamingUser;
361                         default:
362                                 // unknown ?!?! then ask for maximum (unrestricted)
363                                 return IsolatedStorageContainment.UnrestrictedIsolatedStorage;
364                         }
365                 }
366
367                 internal static ulong GetDirectorySize (DirectoryInfo di)
368                 {
369                         ulong size = 0;
370
371                         foreach (FileInfo fi in di.GetFiles ())
372                                 size += (ulong) fi.Length;
373
374                         foreach (DirectoryInfo d in di.GetDirectories ())
375                                 size += GetDirectorySize (d);
376
377                         return size;
378                 }
379
380                 // non-static stuff
381
382                 private DirectoryInfo directory;
383
384                 private IsolatedStorageFile (IsolatedStorageScope scope)
385                 {
386                         storage_scope = scope;
387                 }
388
389                 internal IsolatedStorageFile (IsolatedStorageScope scope, string location)
390                 {
391                         storage_scope = scope;
392                         directory = new DirectoryInfo (location);
393                         if (!directory.Exists) {
394                                 string msg = Locale.GetText ("Invalid storage.");
395                                 throw new IsolatedStorageException (msg);
396                         }
397                         // load the identities
398                 }
399
400                 ~IsolatedStorageFile ()
401                 {
402                 }
403
404                 private void PostInit ()
405                 {
406                         string root = GetIsolatedStorageRoot (Scope);
407                         string dir = null;
408 #if MOBILE
409                         dir = "";
410 #else
411                         if (_applicationIdentity != null) {
412                                 dir = String.Format ("a{0}{1}", SeparatorInternal, GetNameFromIdentity (_applicationIdentity));
413                         } else if (_domainIdentity != null) {
414                                 dir = String.Format ("d{0}{1}{0}{2}", SeparatorInternal,
415                                         GetNameFromIdentity (_domainIdentity), GetNameFromIdentity (_assemblyIdentity));
416                         } else if (_assemblyIdentity != null) {
417                                 dir = String.Format ("d{0}none{0}{1}", SeparatorInternal, GetNameFromIdentity (_assemblyIdentity));
418                         } else {
419                                 throw new IsolatedStorageException (Locale.GetText ("No code identity available."));
420                         }
421 #endif
422
423                         root = Path.Combine (root, dir);
424
425                         // identities have been selected
426                         directory = new DirectoryInfo (root);
427                         if (!directory.Exists) {
428                                 try {
429                                         directory.Create ();
430 #if !MOBILE
431                                         SaveIdentities (root);
432 #endif
433                                 }
434                                 catch (IOException) {
435                                 }
436                         }
437                 }
438
439                 [CLSCompliant(false)]
440                 [Obsolete]
441                 public override ulong CurrentSize {
442                         get { return GetDirectorySize (directory); }
443                 }
444
445                 [CLSCompliant(false)]
446                 [Obsolete]
447                 public override ulong MaximumSize {
448                         // return an ulong but default is signed long
449                         get {
450 #if MOBILE
451                                 // no security manager is active so the rest of the code is useless
452                                 return Int64.MaxValue;
453 #else
454                                 if (!SecurityManager.SecurityEnabled)
455                                         return Int64.MaxValue;
456
457                                 if (_resolved)
458                                         return _maxSize;
459
460                                 Evidence e = null;
461                                 if (_fullEvidences != null) {
462                                         // if possible use the complete evidences we had
463                                         // for computing the X identity
464                                         e = _fullEvidences;
465                                 } else {
466                                         e = new Evidence ();
467                                         // otherwise use what was provided
468                                         if (_assemblyIdentity != null)
469                                                 e.AddHost (_assemblyIdentity);
470                                 }
471                                 if (e.Count < 1) {
472                                         throw new InvalidOperationException (
473                                                 Locale.GetText ("Couldn't get the quota from the available evidences."));
474                                 }
475
476                                 PermissionSet denied = null;
477                                 PermissionSet ps = SecurityManager.ResolvePolicy (e, null, null, null, out denied);
478                                 IsolatedStoragePermission isp = GetPermission (ps);
479                                 if (isp == null) {
480                                         if (ps.IsUnrestricted ()) {
481                                                 _maxSize = Int64.MaxValue; /* default value */
482                                         } else {
483                                                 throw new InvalidOperationException (
484                                                         Locale.GetText ("No quota from the available evidences."));
485                                         }
486                                 } else {
487                                         _maxSize = (ulong) isp.UserQuota;
488                                 }
489                                 _resolved = true;
490                                 return _maxSize;
491 #endif
492                         }
493                 }
494
495                 internal string Root {
496                         get { return directory.FullName; }
497                 }
498
499                 [ComVisible (false)]
500                 public override long AvailableFreeSpace {
501                         get {
502                                 CheckOpen ();
503
504                                 // See the notes for 'Quota'
505                                 return Int64.MaxValue;
506
507                         }
508                 }
509
510                 [ComVisible (false)]
511                 public override long Quota {
512                         get {
513                                 CheckOpen ();
514
515                                 // Since we don't fully support CAS, we are likely
516                                 // going to return Int64.MaxValue always, but we return
517                                 // MaximumSize just in case.
518                                 return (long)MaximumSize;
519                         }
520                 }
521
522                 [ComVisible (false)]
523                 public override long UsedSize {
524                         get {
525                                 CheckOpen ();
526                                 return (long)GetDirectorySize (directory);
527                         }
528                 }
529
530                 [ComVisible (false)]
531                 public static bool IsEnabled {
532                         get {
533                                 return true;
534                         }
535                 }
536
537                 internal bool IsClosed {
538                         get {
539                                 return closed;
540                         }
541                 }
542
543                 internal bool IsDisposed {
544                         get {
545                                 return disposed;
546                         }
547                 }
548
549                 // methods
550
551                 public void Close ()
552                 {
553                         closed = true;
554                 }
555
556                 public void CreateDirectory (string dir)
557                 {
558                         if (dir == null)
559                                 throw new ArgumentNullException ("dir");
560
561                         if (dir.IndexOfAny (Path.PathSeparatorChars) < 0) {
562                                 if (directory.GetFiles (dir).Length > 0)
563                                         throw new IsolatedStorageException ("Unable to create directory.");
564                                 directory.CreateSubdirectory (dir);
565                         } else {
566                                 string[] dirs = dir.Split (Path.PathSeparatorChars, StringSplitOptions.RemoveEmptyEntries);
567                                 DirectoryInfo dinfo = directory;
568
569                                 for (int i = 0; i < dirs.Length; i++) {
570                                         if (dinfo.GetFiles (dirs [i]).Length > 0)
571                                                 throw new IsolatedStorageException ("Unable to create directory.");
572                                         dinfo = dinfo.CreateSubdirectory (dirs [i]);
573                                 }
574                         }
575                 }
576
577                 [ComVisible (false)]
578                 public void CopyFile (string sourceFileName, string destinationFileName)
579                 {
580                         CopyFile (sourceFileName, destinationFileName, false);
581                 }
582
583                 [ComVisible (false)]
584                 public void CopyFile (string sourceFileName, string destinationFileName, bool overwrite)
585                 {
586                         if (sourceFileName == null)
587                                 throw new ArgumentNullException ("sourceFileName");
588                         if (destinationFileName == null)
589                                 throw new ArgumentNullException ("destinationFileName");
590                         if (sourceFileName.Trim ().Length == 0)
591                                 throw new ArgumentException ("An empty file name is not valid.", "sourceFileName");
592                         if (destinationFileName.Trim ().Length == 0)
593                                 throw new ArgumentException ("An empty file name is not valid.", "destinationFileName");
594
595                         CheckOpen ();
596
597                         string source_full_path = Path.Combine (directory.FullName, sourceFileName);
598                         string dest_full_path = Path.Combine (directory.FullName, destinationFileName);
599
600                         if (!IsPathInStorage (source_full_path) || !IsPathInStorage (dest_full_path))
601                                 throw new IsolatedStorageException ("Operation not allowed.");
602                         // These excs can be thrown from File.Copy, but we can try to detect them from here.
603                         if (!Directory.Exists (Path.GetDirectoryName (source_full_path)))
604                                 throw new DirectoryNotFoundException ("Could not find a part of path '" + sourceFileName + "'.");
605                         if (!File.Exists (source_full_path))
606                                 throw new FileNotFoundException ("Could not find a part of path '" + sourceFileName + "'.");
607                         if (File.Exists (dest_full_path) && !overwrite)
608                                 throw new IsolatedStorageException ("Operation not allowed.");
609
610                         try {
611                                 File.Copy (source_full_path, dest_full_path, overwrite);
612                         } catch (IOException) {
613                                 throw new IsolatedStorageException ("Operation not allowed.");
614                         }
615                 }
616
617                 [ComVisible (false)]
618                 public IsolatedStorageFileStream CreateFile (string path)
619                 {
620                         return new IsolatedStorageFileStream (path, FileMode.Create, FileAccess.ReadWrite, FileShare.None, this);
621                 }
622
623                 public void DeleteDirectory (string dir)
624                 {
625                         try {
626                                 if (Path.IsPathRooted (dir))
627                                         dir = dir.Substring (1);
628                                 DirectoryInfo subdir = directory.CreateSubdirectory (dir);
629                                 subdir.Delete ();
630                         }
631                         catch {
632                                 // hide the real exception to avoid leaking the full path
633                                 throw new IsolatedStorageException (Locale.GetText ("Could not delete directory '{0}'", dir));
634                         }
635                 }
636
637                 public void DeleteFile (string file)
638                 {
639                         if (file == null)
640                                 throw new ArgumentNullException ("file");
641
642                         string full_path = Path.Combine (directory.FullName, file);
643                         if (!File.Exists (full_path))
644                                 throw new IsolatedStorageException (Locale.GetText ("Could not delete file '{0}'", file));
645
646                         try {
647                                 File.Delete (Path.Combine (directory.FullName, file));
648                         } catch {
649                                 // hide the internal exception, just as DeleteDirectory does.
650                                 throw new IsolatedStorageException (Locale.GetText ("Could not delete file '{0}'", file));
651                         }
652                 }
653
654                 public void Dispose ()
655                 {
656                         // Dispose may be calling Close, but we are not sure
657                         disposed = true;
658                         // nothing to dispose, anyway we want to please the tools
659                         GC.SuppressFinalize (this);
660                 }
661
662                 [ComVisible (false)]
663                 public bool DirectoryExists (string path)
664                 {
665                         if (path == null)
666                                 throw new ArgumentNullException ("path");
667
668                         CheckOpen ();
669
670                         string full_path = Path.Combine (directory.FullName, path);
671                         if (!IsPathInStorage (full_path))
672                                 return false;
673
674                         return Directory.Exists (full_path);
675                 }
676
677                 [ComVisible (false)]
678                 public bool FileExists (string path)
679                 {
680                         if (path == null)
681                                 throw new ArgumentNullException ("path");
682
683                         CheckOpen ();
684
685                         string full_path = Path.Combine (directory.FullName, path);
686                         if (!IsPathInStorage (full_path))
687                                 return false;
688
689                         return File.Exists (full_path);
690                 }
691
692                 [ComVisible (false)]
693                 public DateTimeOffset GetCreationTime (string path)
694                 {
695                         if (path == null)
696                                 throw new ArgumentNullException ("path");
697                         if (path.Trim ().Length == 0)
698                                 throw new ArgumentException ("An empty path is not valid.");
699
700                         CheckOpen ();
701
702                         string full_path = Path.Combine (directory.FullName, path);
703                         if (File.Exists (full_path))
704                                 return File.GetCreationTime (full_path);
705
706                         return Directory.GetCreationTime (full_path);
707                 }
708
709                 [ComVisible (false)]
710                 public DateTimeOffset GetLastAccessTime (string path)
711                 {
712                         if (path == null)
713                                 throw new ArgumentNullException ("path");
714                         if (path.Trim ().Length == 0)
715                                 throw new ArgumentException ("An empty path is not valid.");
716
717                         CheckOpen ();
718
719                         string full_path = Path.Combine (directory.FullName, path);
720                         if (File.Exists (full_path))
721                                 return File.GetLastAccessTime (full_path);
722
723                         return Directory.GetLastAccessTime (full_path);
724                 }
725
726                 [ComVisible (false)]
727                 public DateTimeOffset GetLastWriteTime (string path)
728                 {
729                         if (path == null)
730                                 throw new ArgumentNullException ("path");
731                         if (path.Trim ().Length == 0)
732                                 throw new ArgumentException ("An empty path is not valid.");
733
734                         CheckOpen ();
735
736                         string full_path = Path.Combine (directory.FullName, path);
737                         if (File.Exists (full_path))
738                                 return File.GetLastWriteTime (full_path);
739
740                         return Directory.GetLastWriteTime (full_path);
741                 }
742                 
743                 public string[] GetDirectoryNames (string searchPattern)
744                 {
745                         if (searchPattern == null)
746                                 throw new ArgumentNullException ("searchPattern");
747                         if (searchPattern.Contains (".."))
748                                 throw new ArgumentException ("Search pattern cannot contain '..' to move up directories.", "searchPattern");
749
750                         // note: IsolatedStorageFile accept a "dir/file" pattern which is not allowed by DirectoryInfo
751                         // so we need to split them to get the right results
752                         string path = Path.GetDirectoryName (searchPattern);
753                         string pattern = Path.GetFileName (searchPattern);
754                         DirectoryInfo[] adi = null;
755                         if (path == null || path.Length == 0) {
756                                 adi = directory.GetDirectories (searchPattern);
757                         } else {
758                                 // we're looking for a single result, identical to path (no pattern here)
759                                 DirectoryInfo[] subdirs = directory.GetDirectories (path);
760                                 DirectoryInfo di = subdirs [0];
761                                 // we're also looking for something under the current path (not outside isolated storage)
762                                 if (di.FullName.IndexOf (directory.FullName) >= 0) {
763                                         adi = di.GetDirectories (pattern);
764                                         string[] segments = path.Split (new char [] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries);
765                                         for (int i = segments.Length - 1; i >= 0; i--) {
766                                                 if (di.Name != segments [i]) {
767                                                         adi = null;
768                                                         break;
769                                                 }
770                                                 di = di.Parent;
771                                         }
772                                 }
773                         }
774                         // CAS, even in FullTrust, normally enforce IsolatedStorage
775                         if (adi == null)
776                                 throw new SecurityException ();
777                          
778                         return GetNames (adi);
779                 }
780
781                 [ComVisible (false)]
782                 public string [] GetDirectoryNames ()
783                 {
784                         return GetDirectoryNames ("*");
785                 }
786
787                 private string[] GetNames (FileSystemInfo[] afsi)
788                 {
789                         string[] r = new string[afsi.Length];
790                         for (int i = 0; i != afsi.Length; ++i)
791                                 r[i] = afsi[i].Name;
792                         return r;
793                 }
794
795                 public string[] GetFileNames (string searchPattern)
796                 {
797                         if (searchPattern == null)
798                                 throw new ArgumentNullException ("searchPattern");
799                         if (searchPattern.Contains (".."))
800                                 throw new ArgumentException ("Search pattern cannot contain '..' to move up directories.", "searchPattern");
801
802                         // note: IsolatedStorageFile accept a "dir/file" pattern which is not allowed by DirectoryInfo
803                         // so we need to split them to get the right results
804                         string path = Path.GetDirectoryName (searchPattern);
805                         string pattern = Path.GetFileName (searchPattern);
806                         FileInfo[] afi = null;
807                         if (path == null || path.Length == 0) {
808                                 afi = directory.GetFiles (searchPattern);
809                         } else {
810                                 DirectoryInfo[] subdirs = directory.GetDirectories (path);
811                                 // we're looking for a single result, identical to path (no pattern here)
812                                 // we're also looking for something under the current path (not outside isolated storage)
813                                 if ((subdirs.Length == 1) && (subdirs [0].Name == path) && (subdirs [0].FullName.IndexOf (directory.FullName) >= 0)) {
814                                         afi = subdirs [0].GetFiles (pattern);
815                                 } else {
816                                         // CAS, even in FullTrust, normally enforce IsolatedStorage
817                                         throw new SecurityException ();
818                                 }
819                         }
820
821                         return GetNames (afi);
822                 }
823
824                 [ComVisible (false)]
825                 public string [] GetFileNames ()
826                 {
827                         return GetFileNames ("*");
828                 }
829
830                 [ComVisible (false)]
831                 public override bool IncreaseQuotaTo (long newQuotaSize)
832                 {
833                         if (newQuotaSize < Quota)
834                                 throw new ArgumentException ();
835
836                         CheckOpen ();
837
838                         // .Net is supposed to be returning false, as mentioned in the docs.
839                         return false;
840                 }
841
842                 [ComVisible (false)]
843                 public void MoveDirectory (string sourceDirectoryName, string destinationDirectoryName)
844                 {
845                         if (sourceDirectoryName == null)
846                                 throw new ArgumentNullException ("sourceDirectoryName");
847                         if (destinationDirectoryName == null)
848                                 throw new ArgumentNullException ("sourceDirectoryName");
849                         if (sourceDirectoryName.Trim ().Length == 0)
850                                 throw new ArgumentException ("An empty directory name is not valid.", "sourceDirectoryName");
851                         if (destinationDirectoryName.Trim ().Length == 0)
852                                 throw new ArgumentException ("An empty directory name is not valid.", "destinationDirectoryName");
853
854                         CheckOpen ();
855
856                         string src_full_path = Path.Combine (directory.FullName, sourceDirectoryName);
857                         string dest_full_path = Path.Combine (directory.FullName, destinationDirectoryName);
858
859                         if (!IsPathInStorage (src_full_path) || !IsPathInStorage (dest_full_path))
860                                 throw new IsolatedStorageException ("Operation not allowed.");
861                         if (!Directory.Exists (src_full_path))
862                                 throw new DirectoryNotFoundException ("Could not find a part of path '" + sourceDirectoryName + "'.");
863                         if (!Directory.Exists (Path.GetDirectoryName (dest_full_path)))
864                                 throw new DirectoryNotFoundException ("Could not find a part of path '" + destinationDirectoryName + "'.");
865
866                         try {
867                                 Directory.Move (src_full_path, dest_full_path);
868                         } catch (IOException) {
869                                 throw new IsolatedStorageException ("Operation not allowed.");
870                         }
871                 }
872
873                 [ComVisible (false)]
874                 public void MoveFile (string sourceFileName, string destinationFileName)
875                 {
876                         if (sourceFileName == null)
877                                 throw new ArgumentNullException ("sourceFileName");
878                         if (destinationFileName == null)
879                                 throw new ArgumentNullException ("sourceFileName");
880                         if (sourceFileName.Trim ().Length == 0)
881                                 throw new ArgumentException ("An empty file name is not valid.", "sourceFileName");
882                         if (destinationFileName.Trim ().Length == 0)
883                                 throw new ArgumentException ("An empty file name is not valid.", "destinationFileName");
884
885                         CheckOpen ();
886
887                         string source_full_path = Path.Combine (directory.FullName, sourceFileName);
888                         string dest_full_path = Path.Combine (directory.FullName, destinationFileName);
889
890                         if (!IsPathInStorage (source_full_path) || !IsPathInStorage (dest_full_path))
891                                 throw new IsolatedStorageException ("Operation not allowed.");
892                         if (!File.Exists (source_full_path))
893                                 throw new FileNotFoundException ("Could not find a part of path '" + sourceFileName + "'.");
894                         // I expected a DirectoryNotFound exception.
895                         if (!Directory.Exists (Path.GetDirectoryName (dest_full_path)))
896                                 throw new IsolatedStorageException ("Operation not allowed.");
897
898                         try {
899                                 File.Move (source_full_path, dest_full_path);
900                         } catch (IOException) {
901                                 throw new IsolatedStorageException ("Operation not allowed.");
902                         }
903                 }
904
905                 [ComVisible (false)]
906                 public IsolatedStorageFileStream OpenFile (string path, FileMode mode)
907                 {
908                         return new IsolatedStorageFileStream (path, mode, this);
909                 }
910
911                 [ComVisible (false)]
912                 public IsolatedStorageFileStream OpenFile (string path, FileMode mode, FileAccess access)
913                 {
914                         return new IsolatedStorageFileStream (path, mode, access, this);
915                 }
916
917                 [ComVisible (false)]
918                 public IsolatedStorageFileStream OpenFile (string path, FileMode mode, FileAccess access, FileShare share)
919                 {
920                         return new IsolatedStorageFileStream (path, mode, access, share, this);
921                 }
922
923                 public override void Remove ()
924                 {
925                         CheckOpen (false);
926                         try {
927                                 directory.Delete (true);
928                         } catch {
929                                 throw new IsolatedStorageException ("Could not remove storage.");
930                         }
931
932                         // It seems .Net is calling Close from here.
933                         Close ();
934                 }
935
936
937                 protected override IsolatedStoragePermission GetPermission (PermissionSet ps)
938                 {
939                         if (ps == null)
940                                 return null;
941                         return (IsolatedStoragePermission) ps.GetPermission (typeof (IsolatedStorageFilePermission));
942                 }
943
944                 // internal stuff
945                 void CheckOpen ()
946                 {
947                         CheckOpen (true);
948                 }
949
950                 void CheckOpen (bool checkDirExists)
951                 {
952                         if (disposed)
953                                 throw new ObjectDisposedException ("IsolatedStorageFile");
954                         if (closed)
955                                 throw new InvalidOperationException ("Storage needs to be open for this operation.");
956                         if (checkDirExists && !Directory.Exists (directory.FullName))
957                                 throw new IsolatedStorageException ("Isolated storage has been removed or disabled.");
958                 }
959
960                 bool IsPathInStorage (string path)
961                 {
962                         return Path.GetFullPath (path).StartsWith (directory.FullName);
963                 }
964
965 #if !MOBILE
966                 private string GetNameFromIdentity (object identity)
967                 {
968                         // Note: Default evidences return an XML string with ToString
969                         byte[] id = Encoding.UTF8.GetBytes (identity.ToString ());
970                         SHA1 hash = SHA1.Create ();
971                         // this create an unique name for an identity - bad identities like Url
972                         // results in bad (i.e. changing) names.
973                         byte[] full = hash.ComputeHash (id, 0, id.Length);
974                         byte[] half = new byte [10];
975                         Buffer.BlockCopy (full, 0, half, 0, half.Length);
976                         return CryptoConvert.ToHex (half);
977                 }
978
979                 private static object GetTypeFromEvidence (Evidence e, Type t)
980                 {
981                         foreach (object o in e) {
982                                 if (o.GetType () == t)
983                                         return o;
984                         }
985                         return null;
986                 }
987
988                 internal static object GetAssemblyIdentityFromEvidence (Evidence e)
989                 {
990                         // we prefer...
991                         // a. a Publisher evidence
992                         object identity = GetTypeFromEvidence (e, typeof (Publisher));
993                         if (identity != null)
994                                 return identity;
995                         // b. a StrongName evidence
996                         identity = GetTypeFromEvidence (e, typeof (StrongName));
997                         if (identity != null)
998                                 return identity;
999                         // c. a Url evidence
1000                         return GetTypeFromEvidence (e, typeof (Url));
1001                 }
1002
1003                 internal static object GetDomainIdentityFromEvidence (Evidence e)
1004                 {
1005                         // we prefer...
1006                         // a. a ApplicationDirectory evidence
1007                         object identity = GetTypeFromEvidence (e, typeof (ApplicationDirectory));
1008                         if (identity != null)
1009                                 return identity;
1010                         // b. a Url evidence
1011                         return GetTypeFromEvidence (e, typeof (Url));
1012                 }
1013
1014                 [Serializable]
1015                 private struct Identities {
1016                         public object Application;
1017                         public object Assembly;
1018                         public object Domain;
1019
1020                         public Identities (object application, object assembly, object domain)
1021                         {
1022                                 Application = application;
1023                                 Assembly = assembly;
1024                                 Domain = domain;
1025                         }
1026                 }
1027 /*
1028                 [SecurityPermission (SecurityAction.Assert, SerializationFormatter = true)]
1029                 private void LoadIdentities (string root)
1030                 {
1031                         if (!File.Exists (root + ".storage"))
1032                                 throw new IsolatedStorageException (Locale.GetText ("Missing identities."));
1033
1034                         BinaryFormatter deformatter = new BinaryFormatter ();
1035                         using (FileStream fs = File.OpenRead (root + ".storage")) {
1036                                 Identities identities = (Identities) deformatter.Deserialize (fs);
1037
1038                                 _applicationIdentity = identities.Application;
1039                                 _assemblyIdentity = identities.Assembly;
1040                                 _domainIdentity = identities.Domain;
1041                         }
1042                 }
1043 */
1044                 [SecurityPermission (SecurityAction.Assert, SerializationFormatter = true)]
1045                 private void SaveIdentities (string root)
1046                 {
1047                         Identities identities = new Identities (_applicationIdentity, _assemblyIdentity, _domainIdentity);
1048                         BinaryFormatter formatter = new BinaryFormatter ();
1049                         mutex.WaitOne ();
1050                         try {
1051                                 using (FileStream fs = File.Create (root + ".storage")) {
1052                                         formatter.Serialize (fs, identities);
1053                                 }
1054                         }
1055                         finally {
1056                                 mutex.ReleaseMutex ();
1057                         }
1058                 }
1059 #endif
1060         }
1061 }