Double <-> Int64 conversion is much faster now.
[mono.git] / mcs / class / corlib / System.IO.IsolatedStorage / MoonIsolatedStorageFile.cs
1 //
2 // System.IO.IsolatedStorage.MoonIsolatedStorageFile
3 //
4 // Moonlight's implementation for the IsolatedStorageFile
5 // 
6 // Authors
7 //      Miguel de Icaza (miguel@novell.com)
8 //      Sebastien Pouliot  <sebastien@ximian.com>
9 //
10 // Copyright (C) 2007, 2008, 2009 Novell, Inc (http://www.novell.com)
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31 #if MOONLIGHT
32 using System;
33 using System.IO;
34 using System.Runtime.InteropServices;
35 using System.Security;
36
37 namespace System.IO.IsolatedStorage {
38
39         // Most of the time there will only be a single instance of both 
40         // * Application Store (GetUserStoreForApplication)
41         // * Site Store (GetUserStoreForSite)
42         // However both can have multiple concurrent uses, e.g.
43         // * another instance of the same application (same URL) running in another Moonlight instance
44         // * another application on the same site (i.e. host) for a site store
45         // and share the some quota, i.e. a site and all applications on the sites share the same space
46
47         // notes:
48         // * quota seems computed in (disk) blocks, i.e. a small file will have a (non-small) size
49         // e.g. every files and directories entries takes 1KB
50
51         public sealed class IsolatedStorageFile : IDisposable {
52
53                 static object locker = new object ();
54         
55                 private string basedir;
56                 private long used;
57                 private bool removed = false;
58                 private bool disposed = false;
59
60                 internal IsolatedStorageFile (string root)
61                 {
62                         basedir = root;
63                 }
64                 
65                 internal void PreCheck ()
66                 {
67                         if (disposed)
68                                 throw new ObjectDisposedException ("Storage was disposed");
69                         if (removed)
70                                 throw new IsolatedStorageException ("Storage was removed");
71                 }
72
73                 public static IsolatedStorageFile GetUserStoreForApplication ()
74                 {
75                         return new IsolatedStorageFile (IsolatedStorage.ApplicationPath);
76                 }
77
78                 public static IsolatedStorageFile GetUserStoreForSite ()
79                 {
80                         return new IsolatedStorageFile (IsolatedStorage.SitePath);
81                 }
82
83                 internal string Verify (string path)
84                 {
85                         // special case: 'path' would be returned (instead of combined)
86                         if ((path.Length > 0) && (path [0] == '/'))
87                                 path = path.Substring (1, path.Length - 1);
88
89                         // outside of try/catch since we want to get things like
90                         //      ArgumentException for invalid characters
91                         string combined = Path.Combine (basedir, path);
92                         try {
93                                 string full = Path.GetFullPath (combined);
94                                 if (full.StartsWith (basedir))
95                                         return full;
96                         } catch {
97                                 // we do not supply an inner exception since it could contains details about the path
98                                 throw new IsolatedStorageException ();
99                         }
100                         throw new IsolatedStorageException ();
101                 }
102
103                 [MonoTODO ("always return true since this was the only behavior in Silverlight 3")]
104                 public static bool IsEnabled {
105                         get {
106                                 return true;
107                         }
108                 }
109
110                 public void CreateDirectory (string dir)
111                 {
112                         PreCheck ();
113                         if (dir == null)
114                                 throw new ArgumentNullException ("dir");
115                         // empty dir is ignored
116                         if (dir.Length > 0)
117                                 Directory.CreateDirectory (Verify (dir));
118                 }
119
120                 public IsolatedStorageFileStream CreateFile (string path)
121                 {
122                         PreCheck ();
123                         try {
124                                 return new IsolatedStorageFileStream (path, FileMode.Create, this);
125                         }
126                         catch (DirectoryNotFoundException) {
127                                 // this can happen if the supplied path includes an unexisting directory
128                                 throw new IsolatedStorageException ();
129                         }
130                 }
131                 
132                 public void DeleteDirectory (string dir)
133                 {
134                         PreCheck ();
135                         if (dir == null)
136                                 throw new ArgumentNullException ("dir");
137                         Directory.Delete (Verify (dir));
138                 }
139
140                 public void DeleteFile (string file)
141                 {
142                         PreCheck ();
143                         if (file == null)
144                                 throw new ArgumentNullException ("file");
145                         string checked_filename = Verify (file);
146                         if (!File.Exists (checked_filename))
147                                 throw new IsolatedStorageException ("File does not exists");
148                         File.Delete (checked_filename);
149                 }
150
151                 public void Dispose ()
152                 {
153                         disposed = true;
154                 }
155
156                 public bool DirectoryExists (string path)
157                 {
158                         PreCheck ();
159                         return Directory.Exists (Verify (path));
160                 }
161
162                 public bool FileExists (string path)
163                 {
164                         PreCheck ();
165                         return File.Exists (Verify (path));
166                 }
167
168                 public DateTimeOffset GetCreationTime (string path)
169                 {
170                         throw new NotImplementedException ();
171                 }
172
173                 public DateTimeOffset GetLastAccessTime (string path)
174                 {
175                         throw new NotImplementedException ();
176                 }
177
178                 public DateTimeOffset GetLastWriteTime (string path)
179                 {
180                         throw new NotImplementedException ();
181                 }
182
183                 private string HideAppDir (string path)
184                 {
185                         // remove the "isolated" part of the path (and the extra '/')
186                         return path.Substring (basedir.Length + 1);
187                 }
188
189                 private string [] HideAppDirs (string[] paths)
190                 {
191                         for (int i=0; i < paths.Length; i++)
192                                 paths [i] = HideAppDir (paths [i]);
193                         return paths;
194                 }
195
196                 private void CheckSearchPattern (string searchPattern)
197                 {
198                         if (searchPattern == null)
199                                 throw new ArgumentNullException ("searchPattern");
200                         if (searchPattern.Length == 0)
201                                 throw new IsolatedStorageException ("searchPattern");
202                         if (searchPattern.IndexOfAny (Path.GetInvalidPathChars ()) != -1)
203                                 throw new ArgumentException ("searchPattern");
204                 }
205
206                 public string [] GetDirectoryNames ()
207                 {
208                         return HideAppDirs (Directory.GetDirectories (basedir));
209                 }
210
211                 public string [] GetDirectoryNames (string searchPattern)
212                 {
213                         CheckSearchPattern (searchPattern);
214
215                         // note: IsolatedStorageFile accept a "dir/file" pattern which is not allowed by DirectoryInfo
216                         // so we need to split them to get the right results
217                         string path = Path.GetDirectoryName (searchPattern);
218                         string pattern = Path.GetFileName (searchPattern);
219                         string [] afi = null;
220
221                         if (path == null || path.Length == 0) {
222                                 return HideAppDirs (Directory.GetDirectories (basedir, searchPattern));
223                         } else {
224                                 // we're looking for a single result, identical to path (no pattern here)
225                                 // we're also looking for something under the current path (not outside isolated storage)
226
227                                 string [] subdirs = Directory.GetDirectories (basedir, path);
228                                 if (subdirs.Length != 1 || subdirs [0].IndexOf (basedir) < 0)
229                                         throw new IsolatedStorageException ();
230
231                                 DirectoryInfo dir = new DirectoryInfo (subdirs [0]);
232                                 if (dir.Name != path)
233                                         throw new IsolatedStorageException ();
234
235                                 return GetNames (dir.GetDirectories (pattern));
236                         }
237                 }
238
239                 public string [] GetFileNames ()
240                 {
241                         return HideAppDirs (Directory.GetFiles (basedir));
242                 }
243
244                 public string [] GetFileNames (string searchPattern)
245                 {
246                         CheckSearchPattern (searchPattern);
247
248                         // note: IsolatedStorageFile accept a "dir/file" pattern which is not allowed by DirectoryInfo
249                         // so we need to split them to get the right results
250                         string path = Path.GetDirectoryName (searchPattern);
251                         string pattern = Path.GetFileName (searchPattern);
252                         string [] afi = null;
253
254                         if (path == null || path.Length == 0) {
255                                 return HideAppDirs (Directory.GetFiles (basedir, searchPattern));
256                         } else {
257                                 // we're looking for a single result, identical to path (no pattern here)
258                                 // we're also looking for something under the current path (not outside isolated storage)
259
260                                 string [] subdirs = Directory.GetDirectories (basedir, path);
261                                 if (subdirs.Length != 1 || subdirs [0].IndexOf (basedir) < 0)
262                                         throw new IsolatedStorageException ();
263
264                                 DirectoryInfo dir = new DirectoryInfo (subdirs [0]);
265                                 if (dir.Name != path)
266                                         throw new IsolatedStorageException ();
267
268                                 return GetNames (dir.GetFiles (pattern));
269                         }
270                 }
271
272                 // Return the file name portion of a full path
273                 private string[] GetNames (FileSystemInfo[] afsi)
274                 {
275                         string[] r = new string[afsi.Length];
276                         for (int i = 0; i != afsi.Length; ++i)
277                                 r[i] = afsi[i].Name;
278                         return r;
279                 }
280
281                 public IsolatedStorageFileStream OpenFile (string path, FileMode mode)
282                 {
283                         return OpenFile (path, mode, FileAccess.ReadWrite, FileShare.None);
284                 }
285
286                 public IsolatedStorageFileStream OpenFile (string path, FileMode mode, FileAccess access)
287                 {
288                         return OpenFile (path, mode, access, FileShare.None);
289                 }
290
291                 public IsolatedStorageFileStream OpenFile (string path, FileMode mode, FileAccess access, FileShare share)
292                 {
293                         PreCheck ();
294                         return new IsolatedStorageFileStream (path, mode, access, share, this);
295                 }
296
297                 public void Remove ()
298                 {
299                         PreCheck ();
300                         IsolatedStorage.Remove (basedir);
301                         removed = true;
302                 }
303
304                 // note: available free space could be changed from another application (same URL, another ML instance) or
305                 // another application on the same site
306                 public long AvailableFreeSpace {
307                         get {
308                                 PreCheck ();
309                                 return IsolatedStorage.AvailableFreeSpace;
310                         }
311                 }
312
313                 // note: quota could be changed from another application (same URL, another ML instance) or
314                 // another application on the same site
315                 public long Quota {
316                         get {
317                                 PreCheck ();
318                                 return IsolatedStorage.Quota;
319                         }
320                 }
321
322                 [DllImport ("moon")]
323                 [return: MarshalAs (UnmanagedType.Bool)]
324                 extern static bool isolated_storage_increase_quota_to (string primary_text, string secondary_text);
325
326                 const long mb = 1024 * 1024;
327
328                 public bool IncreaseQuotaTo (long newQuotaSize)
329                 {
330                         PreCheck ();
331
332                         if (newQuotaSize <= Quota)
333                                 throw new ArgumentException ("newQuotaSize", "Only increases are possible");
334
335                         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>.",
336                                 IsolatedStorage.Site, IsolatedStorage.Current / mb, IsolatedStorage.Quota / mb);
337                         string question = String.Format ("Do you want to increase the web site quota to a new maximum of <b>{0:F1} MB</b> ?", 
338                                 newQuotaSize / mb);
339                         bool result = isolated_storage_increase_quota_to (message, question);
340                         if (result)
341                                 IsolatedStorage.Quota = newQuotaSize;
342                         return result;
343                 }
344                 
345                 public void CopyFile (string sourceFileName, string destinationFileName)
346                 {
347                         throw new NotImplementedException ();
348                 }
349
350                 public void CopyFile (string sourceFileName, string destinationFileName, bool overwrite)
351                 {
352                         throw new NotImplementedException ();
353                 }
354
355                 public void MoveDirectory (string sourceDirectoryName, string destinationDirectoryName)
356                 {
357                         throw new NotImplementedException ();
358                 }
359
360                 public void MoveFile (string sourceFileName, string destinationFileName)
361                 {
362                         throw new NotImplementedException ();
363                 }
364
365                 public long UsedSize {
366                         get {
367                                 throw new NotImplementedException ();
368                         }
369                 }
370         }
371 }
372 #endif