Double <-> Int64 conversion is much faster now.
[mono.git] / mcs / class / corlib / System.IO.IsolatedStorage / MoonIsolatedStorage.cs
1 //
2 // System.IO.IsolatedStorage.IsolatedQuotaGroup
3 //
4 // Authors
5 //      Sebastien Pouliot  <sebastien@ximian.com>
6 //
7 // Copyright (C) 2009 Novell, Inc (http://www.novell.com)
8 //
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:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
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.
27 //
28
29 #if MOONLIGHT
30
31 using System;
32 using System.IO;
33 using System.Runtime.InteropServices;
34 using System.Security;
35 using System.Security.Cryptography;
36 using System.Text;
37
38 namespace System.IO.IsolatedStorage {
39
40         internal static class IsolatedStorage {
41
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;
46
47
48                 static string site_root;
49                 static string site_config;
50                 static long site_quota;
51
52                 // this is similar to Silverlight hierarchy because differing too much would cause
53                 // problems with the 260 character maximum allowed for paths
54
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)
60
61                 // 1234567890123456789012345678901234567890123  = 43 + 28 + 1 + 28 +1 = 101
62                 //          1         2         3         4   
63                 // /home/1234567890/.local/share/moonlight/is/{site-hash:28}/{app-hash:28}/
64
65                 static IsolatedStorage ()
66                 {
67                         string isolated_root = GetIsolatedStorageRoot ();
68                         // enable/disable osilated storage - requires restart
69                         Enabled = !File.Exists (Path.Combine (isolated_root, "disabled"));
70
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));
77                         } else {
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));
85                         }
86
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));
92
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));
97                 }
98
99                 static string GetIsolatedStorageRoot ()
100                 {
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);
105                         }
106
107                         string moonlight = TryDirectory (Path.Combine (xdg_data_home, "moonlight"));
108                         return TryDirectory (Path.Combine (moonlight, "is"));
109                 }
110
111                 static void SetupSite (string dir)
112                 {
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 ();
118                         } else {
119                                 SaveConfiguration ();
120                         }
121                 }
122
123                 static void LoadConfiguration ()
124                 {
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;
132                                         }
133                                         line = sr.ReadLine ();
134                                 }
135                         }
136                 }
137
138                 static void SaveConfiguration ()
139                 {
140                         using (StreamWriter sw = new StreamWriter (site_config)) {
141                                 sw.WriteLine ("URI = {0}", Site);
142                                 sw.WriteLine ("QUOTA = {0}", Quota);
143                         }
144                 }
145
146                 static void SetupApplication (string app, string app_hash, string dir)
147                 {
148                         // save the application information (for the management UI)
149                         string config = Path.Combine (dir, app_hash + ".info");
150                         if (File.Exists (config))
151                                 return;
152
153                         using (StreamWriter sw = new StreamWriter (config)) {
154                                 sw.WriteLine ("URI = {0}", app);
155                         }
156                 }
157
158                 // goal: uniform length directory name
159                 // non-goal: security by obsfucation
160                 static string Hash (string name)
161                 {
162                         string id;
163                         using (SHA1Managed hash = new SHA1Managed ()) {
164                                 byte[] digest = hash.ComputeHash (Encoding.UTF8.GetBytes (name));
165                                 id = Convert.ToBase64String (digest);
166                         }
167                         return (id.IndexOf ('/') == -1) ? id : id.Replace ('/', '-');
168                 }
169
170                 static string TryDirectory (string path)
171                 {
172                         try {
173                                 Directory.CreateDirectory (path);
174                                 return path;
175                         } catch {
176                                 return null;
177                         }
178                 }
179
180                 static internal void Remove (string dir)
181                 {
182                         try {
183                                 Directory.Delete (dir, true);
184                         }
185                         finally {
186                                 TryDirectory (dir);
187                         }
188                 }
189
190                 static internal bool CanExtend (long request)
191                 {
192                         return (request <= AvailableFreeSpace + SafetyZone);
193                 }
194
195                 static public string ApplicationPath {
196                         get; private set;
197                 }
198
199                 static public string SitePath {
200                         get; private set;
201                 }
202
203                 static public long AvailableFreeSpace {
204                         get { return Quota - Current - SafetyZone; }
205                 }
206
207                 [DllImport ("moon")]
208                 extern static long isolated_storage_get_current_usage (string root);
209
210                 static public long Current {
211                         get { return isolated_storage_get_current_usage (site_root); }
212                 }
213
214                 static public long Quota { 
215                         get { return site_quota; }
216                         set {
217                                 site_quota = value;
218                                 SaveConfiguration ();
219                         }
220                 }
221
222                 static public string Site {
223                         get; private set;
224                 }
225
226                 // it is possible, from the UI, to completely disable IsolatedStorage
227                 static public bool Enabled { get; private set; }
228         }
229 }
230
231 #endif
232