//
// 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