2 // System.IO.IsolatedStorage.IsolatedQuotaGroup
5 // Sebastien Pouliot <sebastien@ximian.com>
7 // Copyright (C) 2009 Novell, Inc (http://www.novell.com)
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System
.Runtime
.InteropServices
;
34 using System
.Security
;
35 using System
.Security
.Cryptography
;
38 namespace System
.IO
.IsolatedStorage
{
40 static class IsolatedStorage
{
42 // NOTE: both the 'site' and 'application' share the same quota
43 internal const long DefaultQuota
= 1024 * 1024;
44 // Since we can extend more than AvailableFreeSize we need to substract the "safety" value out of it
45 private const int SafetyZone
= 1024;
48 static string site_root
;
49 static string site_config
;
50 static long site_quota
;
52 // this is similar to Silverlight hierarchy because differing too much would cause
53 // problems with the 260 character maximum allowed for paths
55 // Considering a 10 characters user name the following platform will allow:
56 // 109 characters path, under Windows Vista (Silverlight 2)
57 // 77 characters path, under Windows XP (Silverlight 2)
58 // 100 characters path, under Mac OSX (Silverlight 2)
59 // 159 characters path, under Linux (Moonlight 2)
61 // 1234567890123456789012345678901234567890123 = 43 + 28 + 1 + 28 +1 = 101
63 // /home/1234567890/.local/share/moonlight/is/{site-hash:28}/{app-hash:28}/
65 static IsolatedStorage ()
67 string isolated_root
= GetIsolatedStorageRoot ();
68 // enable/disable osilated storage - requires restart
69 Enabled
= !File
.Exists (Path
.Combine (isolated_root
, "disabled"));
71 // from System.Windows.Application we made "xap_uri" correspond to
72 // Application.Current.Host.Source.AbsoluteUri
73 string app
= (AppDomain
.CurrentDomain
.GetData ("xap_uri") as string);
74 if (app
.StartsWith ("file://")) {
75 // every path is a different site, the XAP is the application
76 Site
= Path
.GetDirectoryName (app
.Substring (7));
78 // for http[s] the "Site Identity" is built using:
79 // * the protocol (so http and https are different);
80 // * the host (so beta.moonlight.com is different from www.moonlight.com); and
81 // * the port (so 8080 is different from 80) but
82 // ** 80 and none are identical for HTTP
83 // ** 443 and none are identical for HTTPS
84 Site
= app
.Substring (0, app
.IndexOf ('/', 8));
87 // the "Site Identity"
88 string site_hash
= Hash (Site
);
89 site_root
= TryDirectory (Path
.Combine (isolated_root
, site_hash
));
90 SetupSite (site_root
);
91 SitePath
= TryDirectory (Path
.Combine (site_root
, site_hash
));
93 // the "Application Identity"
94 string app_hash
= Hash (app
);
95 SetupApplication (app
, app_hash
, site_root
);
96 ApplicationPath
= TryDirectory (Path
.Combine (site_root
, app_hash
));
99 static string GetIsolatedStorageRoot ()
101 // http://freedesktop.org/Standards/basedir-spec/basedir-spec-0.6.html
102 string xdg_data_home
= Environment
.GetEnvironmentVariable ("XDG_DATA_HOME");
103 if (String
.IsNullOrEmpty (xdg_data_home
)) {
104 xdg_data_home
= Environment
.GetFolderPath (Environment
.SpecialFolder
.LocalApplicationData
);
107 string moonlight
= TryDirectory (Path
.Combine (xdg_data_home
, "moonlight"));
108 return TryDirectory (Path
.Combine (moonlight
, "is"));
111 static void SetupSite (string dir
)
113 site_quota
= DefaultQuota
;
114 // read configuration file (e.g. quota) if it exists, otherwise write it
115 site_config
= Path
.Combine (dir
, "config");
116 if (File
.Exists (site_config
)) {
117 LoadConfiguration ();
119 SaveConfiguration ();
123 static void LoadConfiguration ()
125 // read quota, the rest is not useful to us
126 using (StreamReader sr
= new StreamReader (site_config
)) {
127 string line
= sr
.ReadLine ();
128 while (line
!= null) {
129 if (line
.StartsWith ("QUOTA = ")) {
130 if (!Int64
.TryParse (line
.Substring (8, line
.Length
- 8), out site_quota
))
131 Quota
= DefaultQuota
;
133 line
= sr
.ReadLine ();
138 static void SaveConfiguration ()
140 using (StreamWriter sw
= new StreamWriter (site_config
)) {
141 sw
.WriteLine ("URI = {0}", Site
);
142 sw
.WriteLine ("QUOTA = {0}", Quota
);
146 static void SetupApplication (string app
, string app_hash
, string dir
)
148 // save the application information (for the management UI)
149 string config
= Path
.Combine (dir
, app_hash
+ ".info");
150 if (File
.Exists (config
))
153 using (StreamWriter sw
= new StreamWriter (config
)) {
154 sw
.WriteLine ("URI = {0}", app
);
158 // goal: uniform length directory name
159 // non-goal: security by obsfucation
160 static string Hash (string name
)
163 using (SHA1Managed hash
= new SHA1Managed ()) {
164 byte[] digest
= hash
.ComputeHash (Encoding
.UTF8
.GetBytes (name
));
165 id
= Convert
.ToBase64String (digest
);
167 return (id
.IndexOf ('/') == -1) ? id
: id
.Replace ('/', '-');
170 static string TryDirectory (string path
)
173 Directory
.CreateDirectory (path
);
180 static internal void Remove (string dir
)
183 Directory
.Delete (dir
, true);
190 static internal bool CanExtend (long request
)
192 return (request
<= AvailableFreeSpace
+ SafetyZone
);
195 static public string ApplicationPath
{
199 static public string SitePath
{
203 static public long AvailableFreeSpace
{
204 get { return Quota - Current - SafetyZone; }
208 extern static long isolated_storage_get_current_usage (string root
);
210 static public long Current
{
211 get { return isolated_storage_get_current_usage (site_root); }
214 static public long Quota
{
215 get { return site_quota; }
218 SaveConfiguration ();
222 static public string Site
{
226 // it is possible, from the UI, to completely disable IsolatedStorage
227 static public bool Enabled { get; private set; }