New tests.
[mono.git] / mcs / class / corlib / System.IO.IsolatedStorage / MoonIsolatedStorageFile.cs
index 35757cac367fbf2aaaf03a4ec3d38062186eb813..0c0fe270416bd8036b89ad985dff5e382d012f1f 100644 (file)
@@ -5,8 +5,9 @@
 // 
 // Authors
 //      Miguel de Icaza (miguel@novell.com)
+//     Sebastien Pouliot  <sebastien@ximian.com>
 //
-// Copyright (C) 2007 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2007, 2008, 2009 Novell, Inc (http://www.novell.com)
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
-#if NET_2_1
+#if MOONLIGHT
 using System;
 using System.IO;
+using System.Runtime.InteropServices;
 using System.Security;
 
 namespace System.IO.IsolatedStorage {
 
+       // Most of the time there will only be a single instance of both 
+       // * Application Store (GetUserStoreForApplication)
+       // * Site Store (GetUserStoreForSite)
+       // However both can have multiple concurrent uses, e.g.
+       // * another instance of the same application (same URL) running in another Moonlight instance
+       // * another application on the same site (i.e. host) for a site store
+       // and share the some quota, i.e. a site and all applications on the sites share the same space
+
+       // notes:
+       // * quota seems computed in (disk) blocks, i.e. a small file will have a (non-small) size
+       // e.g. every files and directories entries takes 1KB
+
        public sealed class IsolatedStorageFile : IDisposable {
-               static string appdir;
-               
-               static string TryDirectory (string path)
+
+               static object locker = new object ();
+       
+               private string basedir;
+               private long used;
+               private bool removed = false;
+               private bool disposed = false;
+
+               internal IsolatedStorageFile (string root)
                {
-                       try {
-                               Directory.CreateDirectory (path);
-                               return path;
-                       } catch {
-                               return null;
-                       }
+                       basedir = root;
                }
                
-               static IsolatedStorageFile ()
-               {
-                        string xdg_data_home = Environment.GetEnvironmentVariable ("XDG_DATA_HOME");
-                        if (xdg_data_home == null || xdg_data_home == String.Empty){
-                                xdg_data_home = Environment.GetFolderPath (Environment.SpecialFolder.LocalApplicationData);
-                        }
-
-                       string basedir;
-                       basedir = TryDirectory (Path.Combine (xdg_data_home, "moonlight"));
-                       if (basedir == null){
-                               //
-                               // Maybe try a few others?
-                               //
-                               return;
-                       }
-
-                       // FIXME: Use the actual url from the plugin for this.
-                       appdir = Path.Combine (basedir, "url");
-                       try {
-                               Directory.CreateDirectory (appdir);
-                       } catch {
-                               appdir = null;
-                       }
+               internal void PreCheck ()
+               {
+                       if (disposed)
+                               throw new ObjectDisposedException ("Storage was disposed");
+                       if (removed)
+                               throw new IsolatedStorageException ("Storage was removed");
                }
 
-               internal IsolatedStorageFile (string basedir)
+               public static IsolatedStorageFile GetUserStoreForApplication ()
                {
+                       return new IsolatedStorageFile (IsolatedStorage.ApplicationPath);
                }
-               
-               [SecuritySafeCritical]
-               public static IsolatedStorageFile GetUserStoreForApplication ()
+
+               public static IsolatedStorageFile GetUserStoreForSite ()
                {
-                       if (appdir == null)
-                               throw new SecurityException ();
-                       
-                       return new IsolatedStorageFile (appdir);
+                       return new IsolatedStorageFile (IsolatedStorage.SitePath);
                }
 
-               internal static string Verify (string path)
+               internal string Verify (string path)
                {
+                       // special case: 'path' would be returned (instead of combined)
+                       if ((path.Length > 0) && (path [0] == '/'))
+                               path = path.Substring (1, path.Length - 1);
+
+                       // outside of try/catch since we want to get things like
+                       //      ArgumentException for invalid characters
+                       string combined = Path.Combine (basedir, path);
                        try {
-                               string full = Path.GetFullPath (Path.Combine (appdir, path));
-                               if (full.StartsWith (appdir + Path.DirectorySeparatorChar))
+                               string full = Path.GetFullPath (combined);
+                               if (full.StartsWith (basedir))
                                        return full;
                        } catch {
+                               // we do not supply an inner exception since it could contains details about the path
                                throw new IsolatedStorageException ();
                        }
                        throw new IsolatedStorageException ();
                }
                
-               [SecuritySafeCritical]
                public void CreateDirectory (string dir)
                {
-                       Verify (dir);
-                       Directory.CreateDirectory (dir);
-               }    
+                       PreCheck ();
+                       if (dir == null)
+                               throw new ArgumentNullException ("dir");
+                       // empty dir is ignored
+                       if (dir.Length > 0)
+                               Directory.CreateDirectory (Verify (dir));
+               }
+
+               public IsolatedStorageFileStream CreateFile (string path)
+               {
+                       PreCheck ();
+                       try {
+                               return new IsolatedStorageFileStream (path, FileMode.Create, this);
+                       }
+                       catch (DirectoryNotFoundException) {
+                               // this can happen if the supplied path includes an unexisting directory
+                               throw new IsolatedStorageException ();
+                       }
+               }
                
-               [SecuritySafeCritical]
                public void DeleteDirectory (string dir)
                {
-                       Verify (dir);
-                       Directory.Delete (dir);
+                       PreCheck ();
+                       if (dir == null)
+                               throw new ArgumentNullException ("dir");
+                       Directory.Delete (Verify (dir));
+               }
+
+               public void DeleteFile (string file)
+               {
+                       PreCheck ();
+                       if (file == null)
+                               throw new ArgumentNullException ("file");
+                       string checked_filename = Verify (file);
+                       if (!File.Exists (checked_filename))
+                               throw new IsolatedStorageException ("File does not exists");
+                       File.Delete (checked_filename);
+               }
+
+               public void Dispose ()
+               {
+                       disposed = true;
+               }
+
+               public bool DirectoryExists (string path)
+               {
+                       PreCheck ();
+                       return Directory.Exists (Verify (path));
+               }
+
+               public bool FileExists (string path)
+               {
+                       PreCheck ();
+                       return File.Exists (Verify (path));
+               }
+
+               private string HideAppDir (string path)
+               {
+                       // remove the "isolated" part of the path (and the extra '/')
+                       return path.Substring (basedir.Length + 1);
+               }
+
+               private string [] HideAppDirs (string[] paths)
+               {
+                       for (int i=0; i < paths.Length; i++)
+                               paths [i] = HideAppDir (paths [i]);
+                       return paths;
+               }
+
+               private void CheckSearchPattern (string searchPattern)
+               {
+                       if (searchPattern == null)
+                               throw new ArgumentNullException ("searchPattern");
+                       if (searchPattern.Length == 0)
+                               throw new IsolatedStorageException ("searchPattern");
+                       if (searchPattern.IndexOfAny (Path.GetInvalidPathChars ()) != -1)
+                               throw new ArgumentException ("searchPattern");
+               }
+
+               public string [] GetDirectoryNames ()
+               {
+                       return HideAppDirs (Directory.GetDirectories (basedir));
                }
 
-               [SecuritySafeCritical]
                public string [] GetDirectoryNames (string searchPattern)
                {
-                       if (searchPattern.IndexOf ('/') != -1)
-                               throw new IsolatedStorageException ();
-                       
-                       return Directory.GetDirectories (appdir, searchPattern);
+                       CheckSearchPattern (searchPattern);
+
+                       // note: IsolatedStorageFile accept a "dir/file" pattern which is not allowed by DirectoryInfo
+                       // so we need to split them to get the right results
+                       string path = Path.GetDirectoryName (searchPattern);
+                       string pattern = Path.GetFileName (searchPattern);
+                       string [] afi = null;
+
+                       if (path == null || path.Length == 0) {
+                               return HideAppDirs (Directory.GetDirectories (basedir, searchPattern));
+                       } else {
+                               // we're looking for a single result, identical to path (no pattern here)
+                               // we're also looking for something under the current path (not outside isolated storage)
+
+                               string [] subdirs = Directory.GetDirectories (basedir, path);
+                               if (subdirs.Length != 1 || subdirs [0].IndexOf (basedir) < 0)
+                                       throw new IsolatedStorageException ();
+
+                               DirectoryInfo dir = new DirectoryInfo (subdirs [0]);
+                               if (dir.Name != path)
+                                       throw new IsolatedStorageException ();
+
+                               return GetNames (dir.GetDirectories (pattern));
+                       }
+               }
+
+               public string [] GetFileNames ()
+               {
+                       return HideAppDirs (Directory.GetFiles (basedir));
                }
 
-               [SecuritySafeCritical]
                public string [] GetFileNames (string searchPattern)
                {
-                       if (searchPattern.IndexOf ('/') != -1)
-                               throw new IsolatedStorageException ();
-                       
-                       return Directory.GetDirectories (appdir, searchPattern);
+                       CheckSearchPattern (searchPattern);
+
+                       // note: IsolatedStorageFile accept a "dir/file" pattern which is not allowed by DirectoryInfo
+                       // so we need to split them to get the right results
+                       string path = Path.GetDirectoryName (searchPattern);
+                       string pattern = Path.GetFileName (searchPattern);
+                       string [] afi = null;
+
+                       if (path == null || path.Length == 0) {
+                               return HideAppDirs (Directory.GetFiles (basedir, searchPattern));
+                       } else {
+                               // we're looking for a single result, identical to path (no pattern here)
+                               // we're also looking for something under the current path (not outside isolated storage)
+
+                               string [] subdirs = Directory.GetDirectories (basedir, path);
+                               if (subdirs.Length != 1 || subdirs [0].IndexOf (basedir) < 0)
+                                       throw new IsolatedStorageException ();
+
+                               DirectoryInfo dir = new DirectoryInfo (subdirs [0]);
+                               if (dir.Name != path)
+                                       throw new IsolatedStorageException ();
+
+                               return GetNames (dir.GetFiles (pattern));
+                       }
                }
 
-               [SecuritySafeCritical]
-               public void DeleteFile (string file)
+               // Return the file name portion of a full path
+               private string[] GetNames (FileSystemInfo[] afsi)
                {
-                       Verify (file);
+                       string[] r = new string[afsi.Length];
+                       for (int i = 0; i != afsi.Length; ++i)
+                               r[i] = afsi[i].Name;
+                       return r;
                }
-               
-               public void Dispose ()
+
+               public IsolatedStorageFileStream OpenFile (string path, FileMode mode)
+               {
+                       return OpenFile (path, mode, FileAccess.ReadWrite, FileShare.None);
+               }
+
+               public IsolatedStorageFileStream OpenFile (string path, FileMode mode, FileAccess access)
                {
+                       return OpenFile (path, mode, access, FileShare.None);
                }
 
-               [SecuritySafeCritical]
-               public void Close ()
+               public IsolatedStorageFileStream OpenFile (string path, FileMode mode, FileAccess access, FileShare share)
                {
+                       PreCheck ();
+                       return new IsolatedStorageFileStream (path, mode, access, share, this);
                }
 
-               [CLSCompliant(false)]
-               public ulong CurrentSize {
+               public void Remove ()
+               {
+                       PreCheck ();
+                       IsolatedStorage.Remove (basedir);
+                       removed = true;
+               }
+
+               // note: available free space could be changed from another application (same URL, another ML instance) or
+               // another application on the same site
+               public long AvailableFreeSpace {
                        get {
-                               return 0;
+                               PreCheck ();
+                               return IsolatedStorage.AvailableFreeSpace;
                        }
                }
 
-               [CLSCompliant(false)]
-               public ulong MaximumSize {
+               // note: quota could be changed from another application (same URL, another ML instance) or
+               // another application on the same site
+               public long Quota {
                        get {
-                               return 1024*1024;
+                               PreCheck ();
+                               return IsolatedStorage.Quota;
                        }
                }
+
+               [DllImport ("moon")]
+               [return: MarshalAs (UnmanagedType.Bool)]
+               extern static bool isolated_storage_increase_quota_to (string primary_text, string secondary_text);
+
+               const long mb = 1024 * 1024;
+
+               public bool IncreaseQuotaTo (long newQuotaSize)
+               {
+                       PreCheck ();
+
+                       if (newQuotaSize <= Quota)
+                               throw new ArgumentException ("newQuotaSize", "Only increases are possible");
+
+                       string message = String.Format ("This web site, <u>{0}</u>, is requesting an increase of its local storage capacity on your computer. It is currently using <b>{1:F1} MB</b> out of a maximum of <b>{2:F1} MB</b>.",
+                               IsolatedStorage.Site, IsolatedStorage.Current / mb, IsolatedStorage.Quota / mb);
+                       string question = String.Format ("Do you want to increase the web site quota to a new maximum of <b>{0:F1} MB</b> ?", 
+                               newQuotaSize / mb);
+                       bool result = isolated_storage_increase_quota_to (message, question);
+                       if (result)
+                               IsolatedStorage.Quota = newQuotaSize;
+                       return result;
+               }
        }
 }
-#endif
\ No newline at end of file
+#endif