openEventsPlatformApp/plugins/cordova-plugin-file/src/wp/File.cs
plugins/cordova-plugin-file/src/wp/File.csRaw
1/*
2 Licensed under the Apache License, Version 2.0 (the "License");
3 you may not use this file except in compliance with the License.
4 You may obtain a copy of the License at
5
6 http://www.apache.org/licenses/LICENSE-2.0
7
8 Unless required by applicable law or agreed to in writing, software
9 distributed under the License is distributed on an "AS IS" BASIS,
10 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 See the License for the specific language governing permissions and
12 limitations under the License.
13*/
14
15using System;
16using System.Collections.Generic;
17using System.Diagnostics;
18using System.IO;
19using System.IO.IsolatedStorage;
20using System.Runtime.Serialization;
21using System.Security;
22using System.Text;
23using System.Windows;
24using System.Windows.Resources;
25using WPCordovaClassLib.Cordova.JSON;
26
27namespace WPCordovaClassLib.Cordova.Commands
28{
29 /// <summary>
30 /// Provides access to isolated storage
31 /// </summary>
32 public class File : BaseCommand
33 {
34 // Error codes
35 public const int NOT_FOUND_ERR = 1;
36 public const int SECURITY_ERR = 2;
37 public const int ABORT_ERR = 3;
38 public const int NOT_READABLE_ERR = 4;
39 public const int ENCODING_ERR = 5;
40 public const int NO_MODIFICATION_ALLOWED_ERR = 6;
41 public const int INVALID_STATE_ERR = 7;
42 public const int SYNTAX_ERR = 8;
43 public const int INVALID_MODIFICATION_ERR = 9;
44 public const int QUOTA_EXCEEDED_ERR = 10;
45 public const int TYPE_MISMATCH_ERR = 11;
46 public const int PATH_EXISTS_ERR = 12;
47
48 // File system options
49 public const int TEMPORARY = 0;
50 public const int PERSISTENT = 1;
51 public const int RESOURCE = 2;
52 public const int APPLICATION = 3;
53
54 /// <summary>
55 /// Temporary directory name
56 /// </summary>
57 private readonly string TMP_DIRECTORY_NAME = "tmp";
58
59 /// <summary>
60 /// Represents error code for callback
61 /// </summary>
62 [DataContract]
63 public class ErrorCode
64 {
65 /// <summary>
66 /// Error code
67 /// </summary>
68 [DataMember(IsRequired = true, Name = "code")]
69 public int Code { get; set; }
70
71 /// <summary>
72 /// Creates ErrorCode object
73 /// </summary>
74 public ErrorCode(int code)
75 {
76 this.Code = code;
77 }
78 }
79
80 /// <summary>
81 /// Represents File action options.
82 /// </summary>
83 [DataContract]
84 public class FileOptions
85 {
86 /// <summary>
87 /// File path
88 /// </summary>
89 ///
90 private string _fileName;
91 [DataMember(Name = "fileName")]
92 public string FilePath
93 {
94 get
95 {
96 return this._fileName;
97 }
98
99 set
100 {
101 int index = value.IndexOfAny(new char[] { '#', '?' });
102 this._fileName = index > -1 ? value.Substring(0, index) : value;
103 }
104 }
105
106 /// <summary>
107 /// Full entryPath
108 /// </summary>
109 [DataMember(Name = "fullPath")]
110 public string FullPath { get; set; }
111
112 /// <summary>
113 /// Directory name
114 /// </summary>
115 [DataMember(Name = "dirName")]
116 public string DirectoryName { get; set; }
117
118 /// <summary>
119 /// Path to create file/directory
120 /// </summary>
121 [DataMember(Name = "path")]
122 public string Path { get; set; }
123
124 /// <summary>
125 /// The encoding to use to encode the file's content. Default is UTF8.
126 /// </summary>
127 [DataMember(Name = "encoding")]
128 public string Encoding { get; set; }
129
130 /// <summary>
131 /// Uri to get file
132 /// </summary>
133 ///
134 private string _uri;
135 [DataMember(Name = "uri")]
136 public string Uri
137 {
138 get
139 {
140 return this._uri;
141 }
142
143 set
144 {
145 int index = value.IndexOfAny(new char[] { '#', '?' });
146 this._uri = index > -1 ? value.Substring(0, index) : value;
147 }
148 }
149
150 /// <summary>
151 /// Size to truncate file
152 /// </summary>
153 [DataMember(Name = "size")]
154 public long Size { get; set; }
155
156 /// <summary>
157 /// Data to write in file
158 /// </summary>
159 [DataMember(Name = "data")]
160 public string Data { get; set; }
161
162 /// <summary>
163 /// Position the writing starts with
164 /// </summary>
165 [DataMember(Name = "position")]
166 public int Position { get; set; }
167
168 /// <summary>
169 /// Type of file system requested
170 /// </summary>
171 [DataMember(Name = "type")]
172 public int FileSystemType { get; set; }
173
174 /// <summary>
175 /// New file/directory name
176 /// </summary>
177 [DataMember(Name = "newName")]
178 public string NewName { get; set; }
179
180 /// <summary>
181 /// Destination directory to copy/move file/directory
182 /// </summary>
183 [DataMember(Name = "parent")]
184 public string Parent { get; set; }
185
186 /// <summary>
187 /// Options for getFile/getDirectory methods
188 /// </summary>
189 [DataMember(Name = "options")]
190 public CreatingOptions CreatingOpt { get; set; }
191
192 /// <summary>
193 /// Creates options object with default parameters
194 /// </summary>
195 public FileOptions()
196 {
197 this.SetDefaultValues(new StreamingContext());
198 }
199
200 /// <summary>
201 /// Initializes default values for class fields.
202 /// Implemented in separate method because default constructor is not invoked during deserialization.
203 /// </summary>
204 /// <param name="context"></param>
205 [OnDeserializing()]
206 public void SetDefaultValues(StreamingContext context)
207 {
208 this.Encoding = "UTF-8";
209 this.FilePath = "";
210 this.FileSystemType = -1;
211 }
212 }
213
214 /// <summary>
215 /// Stores image info
216 /// </summary>
217 [DataContract]
218 public class FileMetadata
219 {
220 [DataMember(Name = "fileName")]
221 public string FileName { get; set; }
222
223 [DataMember(Name = "fullPath")]
224 public string FullPath { get; set; }
225
226 [DataMember(Name = "type")]
227 public string Type { get; set; }
228
229 [DataMember(Name = "lastModifiedDate")]
230 public string LastModifiedDate { get; set; }
231
232 [DataMember(Name = "size")]
233 public long Size { get; set; }
234
235 public FileMetadata(string filePath)
236 {
237 if (string.IsNullOrEmpty(filePath))
238 {
239 throw new FileNotFoundException("File doesn't exist");
240 }
241
242 this.FullPath = filePath;
243 this.Size = 0;
244 this.FileName = string.Empty;
245
246 using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
247 {
248 bool IsFile = isoFile.FileExists(filePath);
249 bool IsDirectory = isoFile.DirectoryExists(filePath);
250
251 if (!IsDirectory)
252 {
253 if (!IsFile) // special case, if isoFile cannot find it, it might still be part of the app-package
254 {
255 // attempt to get it from the resources
256
257 Uri fileUri = new Uri(filePath, UriKind.Relative);
258 StreamResourceInfo streamInfo = Application.GetResourceStream(fileUri);
259 if (streamInfo != null)
260 {
261 this.Size = streamInfo.Stream.Length;
262 this.FileName = filePath.Substring(filePath.LastIndexOf("/") + 1);
263 }
264 else
265 {
266 throw new FileNotFoundException("File doesn't exist");
267 }
268 }
269 else
270 {
271 using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(filePath, FileMode.Open, FileAccess.Read, isoFile))
272 {
273 this.Size = stream.Length;
274 }
275
276 this.FileName = System.IO.Path.GetFileName(filePath);
277 this.LastModifiedDate = isoFile.GetLastWriteTime(filePath).DateTime.ToString();
278 }
279 }
280
281 this.Type = MimeTypeMapper.GetMimeType(this.FileName);
282 }
283 }
284 }
285
286 /// <summary>
287 /// Represents file or directory modification metadata
288 /// </summary>
289 [DataContract]
290 public class ModificationMetadata
291 {
292 /// <summary>
293 /// Modification time
294 /// </summary>
295 [DataMember]
296 public string modificationTime { get; set; }
297 }
298
299 /// <summary>
300 /// Represents file or directory entry
301 /// </summary>
302 [DataContract]
303 public class FileEntry
304 {
305
306 /// <summary>
307 /// File type
308 /// </summary>
309 [DataMember(Name = "isFile")]
310 public bool IsFile { get; set; }
311
312 /// <summary>
313 /// Directory type
314 /// </summary>
315 [DataMember(Name = "isDirectory")]
316 public bool IsDirectory { get; set; }
317
318 /// <summary>
319 /// File/directory name
320 /// </summary>
321 [DataMember(Name = "name")]
322 public string Name { get; set; }
323
324 /// <summary>
325 /// Full path to file/directory
326 /// </summary>
327 [DataMember(Name = "fullPath")]
328 public string FullPath { get; set; }
329
330 /// <summary>
331 /// URI encoded fullpath
332 /// </summary>
333 [DataMember(Name = "nativeURL")]
334 public string NativeURL
335 {
336 set { }
337 get
338 {
339 string escaped = Uri.EscapeUriString(this.FullPath);
340 escaped = escaped.Replace("//", "/");
341 if (escaped.StartsWith("/"))
342 {
343 escaped = escaped.Insert(0, "/");
344 }
345 return escaped;
346 }
347 }
348
349 public bool IsResource { get; set; }
350
351 public static FileEntry GetEntry(string filePath, bool bIsRes=false)
352 {
353 FileEntry entry = null;
354 try
355 {
356 entry = new FileEntry(filePath, bIsRes);
357
358 }
359 catch (Exception ex)
360 {
361 Debug.WriteLine("Exception in GetEntry for filePath :: " + filePath + " " + ex.Message);
362 }
363 return entry;
364 }
365
366 /// <summary>
367 /// Creates object and sets necessary properties
368 /// </summary>
369 /// <param name="filePath"></param>
370 public FileEntry(string filePath, bool bIsRes = false)
371 {
372 if (string.IsNullOrEmpty(filePath))
373 {
374 throw new ArgumentException();
375 }
376
377 if(filePath.Contains(" "))
378 {
379 Debug.WriteLine("FilePath with spaces :: " + filePath);
380 }
381
382 using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
383 {
384 IsResource = bIsRes;
385 IsFile = isoFile.FileExists(filePath);
386 IsDirectory = isoFile.DirectoryExists(filePath);
387 if (IsFile)
388 {
389 this.Name = Path.GetFileName(filePath);
390 }
391 else if (IsDirectory)
392 {
393 this.Name = this.GetDirectoryName(filePath);
394 if (string.IsNullOrEmpty(Name))
395 {
396 this.Name = "/";
397 }
398 }
399 else
400 {
401 if (IsResource)
402 {
403 this.Name = Path.GetFileName(filePath);
404 }
405 else
406 {
407 throw new FileNotFoundException();
408 }
409 }
410
411 try
412 {
413 this.FullPath = filePath.Replace('\\', '/'); // new Uri(filePath).LocalPath;
414 }
415 catch (Exception)
416 {
417 this.FullPath = filePath;
418 }
419 }
420 }
421
422 /// <summary>
423 /// Extracts directory name from path string
424 /// Path should refer to a directory, for example \foo\ or /foo.
425 /// </summary>
426 /// <param name="path"></param>
427 /// <returns></returns>
428 private string GetDirectoryName(string path)
429 {
430 if (String.IsNullOrEmpty(path))
431 {
432 return path;
433 }
434
435 string[] split = path.Split(new char[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
436 if (split.Length < 1)
437 {
438 return null;
439 }
440 else
441 {
442 return split[split.Length - 1];
443 }
444 }
445 }
446
447
448 /// <summary>
449 /// Represents info about requested file system
450 /// </summary>
451 [DataContract]
452 public class FileSystemInfo
453 {
454 /// <summary>
455 /// file system type
456 /// </summary>
457 [DataMember(Name = "name", IsRequired = true)]
458 public string Name { get; set; }
459
460 /// <summary>
461 /// Root directory entry
462 /// </summary>
463 [DataMember(Name = "root", EmitDefaultValue = false)]
464 public FileEntry Root { get; set; }
465
466 /// <summary>
467 /// Creates class instance
468 /// </summary>
469 /// <param name="name"></param>
470 /// <param name="rootEntry"> Root directory</param>
471 public FileSystemInfo(string name, FileEntry rootEntry = null)
472 {
473 Name = name;
474 Root = rootEntry;
475 }
476 }
477
478 [DataContract]
479 public class CreatingOptions
480 {
481 /// <summary>
482 /// Create file/directory if is doesn't exist
483 /// </summary>
484 [DataMember(Name = "create")]
485 public bool Create { get; set; }
486
487 /// <summary>
488 /// Generate an exception if create=true and file/directory already exists
489 /// </summary>
490 [DataMember(Name = "exclusive")]
491 public bool Exclusive { get; set; }
492
493
494 }
495
496 // returns null value if it fails.
497 private string[] getOptionStrings(string options)
498 {
499 string[] optStings = null;
500 try
501 {
502 optStings = JSON.JsonHelper.Deserialize<string[]>(options);
503 }
504 catch (Exception)
505 {
506 DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION), CurrentCommandCallbackId);
507 }
508 return optStings;
509 }
510
511 /// <summary>
512 /// Gets amount of free space available for Isolated Storage
513 /// </summary>
514 /// <param name="options">No options is needed for this method</param>
515 public void getFreeDiskSpace(string options)
516 {
517 string callbackId = getOptionStrings(options)[0];
518
519 try
520 {
521 using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
522 {
523 DispatchCommandResult(new PluginResult(PluginResult.Status.OK, isoFile.AvailableFreeSpace), callbackId);
524 }
525 }
526 catch (IsolatedStorageException)
527 {
528 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
529 }
530 catch (Exception ex)
531 {
532 if (!this.HandleException(ex))
533 {
534 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
535 }
536 }
537 }
538
539 /// <summary>
540 /// Check if file exists
541 /// </summary>
542 /// <param name="options">File path</param>
543 public void testFileExists(string options)
544 {
545 IsDirectoryOrFileExist(options, false);
546 }
547
548 /// <summary>
549 /// Check if directory exists
550 /// </summary>
551 /// <param name="options">directory name</param>
552 public void testDirectoryExists(string options)
553 {
554 IsDirectoryOrFileExist(options, true);
555 }
556
557 /// <summary>
558 /// Check if file or directory exist
559 /// </summary>
560 /// <param name="options">File path/Directory name</param>
561 /// <param name="isDirectory">Flag to recognize what we should check</param>
562 public void IsDirectoryOrFileExist(string options, bool isDirectory)
563 {
564 string[] args = getOptionStrings(options);
565 string callbackId = args[1];
566 FileOptions fileOptions = JSON.JsonHelper.Deserialize<FileOptions>(args[0]);
567 string filePath = args[0];
568
569 if (fileOptions == null)
570 {
571 DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION), callbackId);
572 }
573
574 try
575 {
576 using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
577 {
578 bool isExist;
579 if (isDirectory)
580 {
581 isExist = isoFile.DirectoryExists(fileOptions.DirectoryName);
582 }
583 else
584 {
585 isExist = isoFile.FileExists(fileOptions.FilePath);
586 }
587 DispatchCommandResult(new PluginResult(PluginResult.Status.OK, isExist), callbackId);
588 }
589 }
590 catch (IsolatedStorageException) // default handler throws INVALID_MODIFICATION_ERR
591 {
592 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
593 }
594 catch (Exception ex)
595 {
596 if (!this.HandleException(ex))
597 {
598 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
599 }
600 }
601
602 }
603
604 public void readAsDataURL(string options)
605 {
606 string[] optStrings = getOptionStrings(options);
607 string filePath = optStrings[0];
608 int startPos = int.Parse(optStrings[1]);
609 int endPos = int.Parse(optStrings[2]);
610 string callbackId = optStrings[3];
611
612 if (filePath != null)
613 {
614 try
615 {
616 string base64URL = null;
617
618 using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
619 {
620 if (!isoFile.FileExists(filePath))
621 {
622 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
623 return;
624 }
625 string mimeType = MimeTypeMapper.GetMimeType(filePath);
626
627 using (IsolatedStorageFileStream stream = isoFile.OpenFile(filePath, FileMode.Open, FileAccess.Read))
628 {
629 string base64String = GetFileContent(stream);
630 base64URL = "data:" + mimeType + ";base64," + base64String;
631 }
632 }
633
634 DispatchCommandResult(new PluginResult(PluginResult.Status.OK, base64URL), callbackId);
635 }
636 catch (Exception ex)
637 {
638 if (!this.HandleException(ex))
639 {
640 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
641 }
642 }
643 }
644 }
645
646 private byte[] readFileBytes(string filePath,int startPos,int endPos, IsolatedStorageFile isoFile)
647 {
648 byte[] buffer;
649 using (IsolatedStorageFileStream reader = isoFile.OpenFile(filePath, FileMode.Open, FileAccess.Read))
650 {
651 if (startPos < 0)
652 {
653 startPos = Math.Max((int)reader.Length + startPos, 0);
654 }
655 else if (startPos > 0)
656 {
657 startPos = Math.Min((int)reader.Length, startPos);
658 }
659 if (endPos > 0)
660 {
661 endPos = Math.Min((int)reader.Length, endPos);
662 }
663 else if (endPos < 0)
664 {
665 endPos = Math.Max(endPos + (int)reader.Length, 0);
666 }
667
668 buffer = new byte[endPos - startPos];
669 reader.Seek(startPos, SeekOrigin.Begin);
670 reader.Read(buffer, 0, buffer.Length);
671 }
672
673 return buffer;
674 }
675
676 public void readAsArrayBuffer(string options)
677 {
678 string[] optStrings = getOptionStrings(options);
679 string filePath = optStrings[0];
680 int startPos = int.Parse(optStrings[1]);
681 int endPos = int.Parse(optStrings[2]);
682 string callbackId = optStrings[3];
683
684 try
685 {
686 byte[] buffer;
687
688 using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
689 {
690 if (!isoFile.FileExists(filePath))
691 {
692 readResourceAsText(options);
693 return;
694 }
695 buffer = readFileBytes(filePath, startPos, endPos, isoFile);
696 }
697
698 DispatchCommandResult(new PluginResult(PluginResult.Status.OK, buffer), callbackId);
699 }
700 catch (Exception ex)
701 {
702 if (!this.HandleException(ex, callbackId))
703 {
704 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
705 }
706 }
707 }
708
709 public void readAsBinaryString(string options)
710 {
711 string[] optStrings = getOptionStrings(options);
712 string filePath = optStrings[0];
713 int startPos = int.Parse(optStrings[1]);
714 int endPos = int.Parse(optStrings[2]);
715 string callbackId = optStrings[3];
716
717 try
718 {
719 string result;
720
721 using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
722 {
723 if (!isoFile.FileExists(filePath))
724 {
725 readResourceAsText(options);
726 return;
727 }
728
729 byte[] buffer = readFileBytes(filePath, startPos, endPos, isoFile);
730 result = System.Text.Encoding.GetEncoding("iso-8859-1").GetString(buffer, 0, buffer.Length);
731
732 }
733
734 DispatchCommandResult(new PluginResult(PluginResult.Status.OK, result), callbackId);
735 }
736 catch (Exception ex)
737 {
738 if (!this.HandleException(ex, callbackId))
739 {
740 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
741 }
742 }
743 }
744
745 public void readAsText(string options)
746 {
747 string[] optStrings = getOptionStrings(options);
748 string filePath = optStrings[0];
749 string encStr = optStrings[1];
750 int startPos = int.Parse(optStrings[2]);
751 int endPos = int.Parse(optStrings[3]);
752 string callbackId = optStrings[4];
753
754 try
755 {
756 string text = "";
757
758 using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
759 {
760 if (!isoFile.FileExists(filePath))
761 {
762 readResourceAsText(options);
763 return;
764 }
765 Encoding encoding = Encoding.GetEncoding(encStr);
766
767 byte[] buffer = this.readFileBytes(filePath, startPos, endPos, isoFile);
768 text = encoding.GetString(buffer, 0, buffer.Length);
769 }
770
771 // JIRA: https://issues.apache.org/jira/browse/CB-8792
772 // Need to perform additional serialization here because NativeExecution is always trying
773 // to do JSON.parse() on command result. This leads to issue when trying to read JSON files
774 var resultText = JsonHelper.Serialize(text);
775 DispatchCommandResult(new PluginResult(PluginResult.Status.OK, resultText), callbackId);
776 }
777 catch (Exception ex)
778 {
779 if (!this.HandleException(ex, callbackId))
780 {
781 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
782 }
783 }
784 }
785
786 /// <summary>
787 /// Reads application resource as a text
788 /// </summary>
789 /// <param name="options">Path to a resource</param>
790 public void readResourceAsText(string options)
791 {
792 string[] optStrings = getOptionStrings(options);
793 string pathToResource = optStrings[0];
794 string encStr = optStrings[1];
795 int start = int.Parse(optStrings[2]);
796 int endMarker = int.Parse(optStrings[3]);
797 string callbackId = optStrings[4];
798
799 try
800 {
801 if (pathToResource.StartsWith("/"))
802 {
803 pathToResource = pathToResource.Remove(0, 1);
804 }
805
806 var resource = Application.GetResourceStream(new Uri(pathToResource, UriKind.Relative));
807
808 if (resource == null)
809 {
810 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
811 return;
812 }
813
814 string text;
815 StreamReader streamReader = new StreamReader(resource.Stream);
816 text = streamReader.ReadToEnd();
817
818 DispatchCommandResult(new PluginResult(PluginResult.Status.OK, text), callbackId);
819 }
820 catch (Exception ex)
821 {
822 if (!this.HandleException(ex, callbackId))
823 {
824 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
825 }
826 }
827 }
828
829 public void truncate(string options)
830 {
831 string[] optStrings = getOptionStrings(options);
832
833 string filePath = optStrings[0];
834 int size = int.Parse(optStrings[1]);
835 string callbackId = optStrings[2];
836
837 try
838 {
839 long streamLength = 0;
840
841 using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
842 {
843 if (!isoFile.FileExists(filePath))
844 {
845 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
846 return;
847 }
848
849 using (FileStream stream = new IsolatedStorageFileStream(filePath, FileMode.Open, FileAccess.ReadWrite, isoFile))
850 {
851 if (0 <= size && size <= stream.Length)
852 {
853 stream.SetLength(size);
854 }
855 streamLength = stream.Length;
856 }
857 }
858
859 DispatchCommandResult(new PluginResult(PluginResult.Status.OK, streamLength), callbackId);
860 }
861 catch (Exception ex)
862 {
863 if (!this.HandleException(ex, callbackId))
864 {
865 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
866 }
867 }
868 }
869
870 //write:[filePath,data,position,isBinary,callbackId]
871 public void write(string options)
872 {
873 string[] optStrings = getOptionStrings(options);
874
875 string filePath = optStrings[0];
876 string data = optStrings[1];
877 int position = int.Parse(optStrings[2]);
878 bool isBinary = bool.Parse(optStrings[3]);
879 string callbackId = optStrings[4];
880
881 try
882 {
883 if (string.IsNullOrEmpty(data))
884 {
885 Debug.WriteLine("Expected some data to be send in the write command to {0}", filePath);
886 DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION), callbackId);
887 return;
888 }
889
890 byte[] dataToWrite = isBinary ? JSON.JsonHelper.Deserialize<byte[]>(data) :
891 System.Text.Encoding.UTF8.GetBytes(data);
892
893 using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
894 {
895 // create the file if not exists
896 if (!isoFile.FileExists(filePath))
897 {
898 var file = isoFile.CreateFile(filePath);
899 file.Close();
900 }
901
902 using (FileStream stream = new IsolatedStorageFileStream(filePath, FileMode.Open, FileAccess.ReadWrite, isoFile))
903 {
904 if (0 <= position && position <= stream.Length)
905 {
906 stream.SetLength(position);
907 }
908 using (BinaryWriter writer = new BinaryWriter(stream))
909 {
910 writer.Seek(0, SeekOrigin.End);
911 writer.Write(dataToWrite);
912 }
913 }
914 }
915
916 DispatchCommandResult(new PluginResult(PluginResult.Status.OK, dataToWrite.Length), callbackId);
917 }
918 catch (Exception ex)
919 {
920 if (!this.HandleException(ex, callbackId))
921 {
922 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
923 }
924 }
925 }
926
927 /// <summary>
928 /// Look up metadata about this entry.
929 /// </summary>
930 /// <param name="options">filePath to entry</param>
931 public void getMetadata(string options)
932 {
933 string[] optStings = getOptionStrings(options);
934 string filePath = optStings[0];
935 string callbackId = optStings[1];
936
937 if (filePath != null)
938 {
939 try
940 {
941 using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
942 {
943 if (isoFile.FileExists(filePath))
944 {
945 DispatchCommandResult(new PluginResult(PluginResult.Status.OK,
946 new ModificationMetadata() { modificationTime = isoFile.GetLastWriteTime(filePath).DateTime.ToString() }), callbackId);
947 }
948 else if (isoFile.DirectoryExists(filePath))
949 {
950 string modTime = isoFile.GetLastWriteTime(filePath).DateTime.ToString();
951 DispatchCommandResult(new PluginResult(PluginResult.Status.OK, new ModificationMetadata() { modificationTime = modTime }), callbackId);
952 }
953 else
954 {
955 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
956 }
957
958 }
959 }
960 catch (IsolatedStorageException)
961 {
962 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
963 }
964 catch (Exception ex)
965 {
966 if (!this.HandleException(ex))
967 {
968 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
969 }
970 }
971 }
972
973 }
974
975
976 /// <summary>
977 /// Returns a File that represents the current state of the file that this FileEntry represents.
978 /// </summary>
979 /// <param name="filePath">filePath to entry</param>
980 /// <returns></returns>
981 public void getFileMetadata(string options)
982 {
983 string[] optStings = getOptionStrings(options);
984 string filePath = optStings[0];
985 string callbackId = optStings[1];
986
987 if (!string.IsNullOrEmpty(filePath))
988 {
989 try
990 {
991 FileMetadata metaData = new FileMetadata(filePath);
992 DispatchCommandResult(new PluginResult(PluginResult.Status.OK, metaData), callbackId);
993 }
994 catch (IsolatedStorageException)
995 {
996 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
997 }
998 catch (Exception ex)
999 {
1000 if (!this.HandleException(ex))
1001 {
1002 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId);
1003 }
1004 }
1005 }
1006 else
1007 {
1008 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
1009 }
1010 }
1011
1012 /// <summary>
1013 /// Look up the parent DirectoryEntry containing this Entry.
1014 /// If this Entry is the root of IsolatedStorage, its parent is itself.
1015 /// </summary>
1016 /// <param name="options"></param>
1017 public void getParent(string options)
1018 {
1019 string[] optStings = getOptionStrings(options);
1020 string filePath = optStings[0];
1021 string callbackId = optStings[1];
1022
1023 if (filePath != null)
1024 {
1025 try
1026 {
1027 if (string.IsNullOrEmpty(filePath))
1028 {
1029 DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION),callbackId);
1030 return;
1031 }
1032
1033 using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
1034 {
1035 FileEntry entry;
1036
1037 if (isoFile.FileExists(filePath) || isoFile.DirectoryExists(filePath))
1038 {
1039
1040
1041 string path = this.GetParentDirectory(filePath);
1042 entry = FileEntry.GetEntry(path);
1043 DispatchCommandResult(new PluginResult(PluginResult.Status.OK, entry),callbackId);
1044 }
1045 else
1046 {
1047 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR),callbackId);
1048 }
1049
1050 }
1051 }
1052 catch (Exception ex)
1053 {
1054 if (!this.HandleException(ex))
1055 {
1056 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR),callbackId);
1057 }
1058 }
1059 }
1060 }
1061
1062 public void remove(string options)
1063 {
1064 string[] args = getOptionStrings(options);
1065 string filePath = args[0];
1066 string callbackId = args[1];
1067
1068 if (filePath != null)
1069 {
1070 try
1071 {
1072 if (filePath == "/" || filePath == "" || filePath == @"\")
1073 {
1074 throw new Exception("Cannot delete root file system") ;
1075 }
1076 using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
1077 {
1078 if (isoFile.FileExists(filePath))
1079 {
1080 isoFile.DeleteFile(filePath);
1081 }
1082 else
1083 {
1084 if (isoFile.DirectoryExists(filePath))
1085 {
1086 isoFile.DeleteDirectory(filePath);
1087 }
1088 else
1089 {
1090 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR),callbackId);
1091 return;
1092 }
1093 }
1094 DispatchCommandResult(new PluginResult(PluginResult.Status.OK),callbackId);
1095 }
1096 }
1097 catch (Exception ex)
1098 {
1099 if (!this.HandleException(ex))
1100 {
1101 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR),callbackId);
1102 }
1103 }
1104 }
1105 }
1106
1107 public void removeRecursively(string options)
1108 {
1109 string[] args = getOptionStrings(options);
1110 string filePath = args[0];
1111 string callbackId = args[1];
1112
1113 if (filePath != null)
1114 {
1115 if (string.IsNullOrEmpty(filePath))
1116 {
1117 DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION),callbackId);
1118 }
1119 else
1120 {
1121 if (removeDirRecursively(filePath, callbackId))
1122 {
1123 DispatchCommandResult(new PluginResult(PluginResult.Status.OK), callbackId);
1124 }
1125 }
1126 }
1127 }
1128
1129 public void readEntries(string options)
1130 {
1131 string[] args = getOptionStrings(options);
1132 string filePath = args[0];
1133 string callbackId = args[1];
1134
1135 if (filePath != null)
1136 {
1137 try
1138 {
1139 if (string.IsNullOrEmpty(filePath))
1140 {
1141 DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION),callbackId);
1142 return;
1143 }
1144
1145 using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
1146 {
1147 if (isoFile.DirectoryExists(filePath))
1148 {
1149 string path = File.AddSlashToDirectory(filePath);
1150 List<FileEntry> entries = new List<FileEntry>();
1151 string[] files = isoFile.GetFileNames(path + "*");
1152 string[] dirs = isoFile.GetDirectoryNames(path + "*");
1153 foreach (string file in files)
1154 {
1155 entries.Add(FileEntry.GetEntry(path + file));
1156 }
1157 foreach (string dir in dirs)
1158 {
1159 entries.Add(FileEntry.GetEntry(path + dir + "/"));
1160 }
1161 DispatchCommandResult(new PluginResult(PluginResult.Status.OK, entries),callbackId);
1162 }
1163 else
1164 {
1165 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR),callbackId);
1166 }
1167 }
1168 }
1169 catch (Exception ex)
1170 {
1171 if (!this.HandleException(ex))
1172 {
1173 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR),callbackId);
1174 }
1175 }
1176 }
1177 }
1178
1179 public void requestFileSystem(string options)
1180 {
1181 // TODO: try/catch
1182 string[] optVals = getOptionStrings(options);
1183 //FileOptions fileOptions = new FileOptions();
1184 int fileSystemType = int.Parse(optVals[0]);
1185 double size = double.Parse(optVals[1]);
1186 string callbackId = optVals[2];
1187
1188
1189 IsolatedStorageFile.GetUserStoreForApplication();
1190
1191 if (size > (10 * 1024 * 1024)) // 10 MB, compier will clean this up!
1192 {
1193 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, QUOTA_EXCEEDED_ERR), callbackId);
1194 return;
1195 }
1196
1197 try
1198 {
1199 if (size != 0)
1200 {
1201 using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
1202 {
1203 long availableSize = isoFile.AvailableFreeSpace;
1204 if (size > availableSize)
1205 {
1206 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, QUOTA_EXCEEDED_ERR), callbackId);
1207 return;
1208 }
1209 }
1210 }
1211
1212 if (fileSystemType == PERSISTENT)
1213 {
1214 // TODO: this should be in it's own folder to prevent overwriting of the app assets, which are also in ISO
1215 DispatchCommandResult(new PluginResult(PluginResult.Status.OK, new FileSystemInfo("persistent", FileEntry.GetEntry("/"))), callbackId);
1216 }
1217 else if (fileSystemType == TEMPORARY)
1218 {
1219 using (IsolatedStorageFile isoStorage = IsolatedStorageFile.GetUserStoreForApplication())
1220 {
1221 if (!isoStorage.FileExists(TMP_DIRECTORY_NAME))
1222 {
1223 isoStorage.CreateDirectory(TMP_DIRECTORY_NAME);
1224 }
1225 }
1226
1227 string tmpFolder = "/" + TMP_DIRECTORY_NAME + "/";
1228
1229 DispatchCommandResult(new PluginResult(PluginResult.Status.OK, new FileSystemInfo("temporary", FileEntry.GetEntry(tmpFolder))), callbackId);
1230 }
1231 else if (fileSystemType == RESOURCE)
1232 {
1233 DispatchCommandResult(new PluginResult(PluginResult.Status.OK, new FileSystemInfo("resource")), callbackId);
1234 }
1235 else if (fileSystemType == APPLICATION)
1236 {
1237 DispatchCommandResult(new PluginResult(PluginResult.Status.OK, new FileSystemInfo("application")), callbackId);
1238 }
1239 else
1240 {
1241 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR), callbackId);
1242 }
1243
1244 }
1245 catch (Exception ex)
1246 {
1247 if (!this.HandleException(ex))
1248 {
1249 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR), callbackId);
1250 }
1251 }
1252 }
1253
1254 public void resolveLocalFileSystemURI(string options)
1255 {
1256
1257 string[] optVals = getOptionStrings(options);
1258 string uri = optVals[0].Split('?')[0];
1259 string callbackId = optVals[1];
1260
1261 if (uri != null)
1262 {
1263 // a single '/' is valid, however, '/someDir' is not, but '/tmp//somedir' and '///someDir' are valid
1264 if (uri.StartsWith("/") && uri.IndexOf("//") < 0 && uri != "/")
1265 {
1266 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ENCODING_ERR), callbackId);
1267 return;
1268 }
1269 try
1270 {
1271 // fix encoded spaces
1272 string path = Uri.UnescapeDataString(uri);
1273
1274 FileEntry uriEntry = FileEntry.GetEntry(path);
1275 if (uriEntry != null)
1276 {
1277 DispatchCommandResult(new PluginResult(PluginResult.Status.OK, uriEntry), callbackId);
1278 }
1279 else
1280 {
1281 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
1282 }
1283 }
1284 catch (Exception ex)
1285 {
1286 if (!this.HandleException(ex, callbackId))
1287 {
1288 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR), callbackId);
1289 }
1290 }
1291 }
1292 }
1293
1294 public void copyTo(string options)
1295 {
1296 TransferTo(options, false);
1297 }
1298
1299 public void moveTo(string options)
1300 {
1301 TransferTo(options, true);
1302 }
1303
1304 public void getFile(string options)
1305 {
1306 GetFileOrDirectory(options, false);
1307 }
1308
1309 public void getDirectory(string options)
1310 {
1311 GetFileOrDirectory(options, true);
1312 }
1313
1314 #region internal functionality
1315
1316 /// <summary>
1317 /// Retrieves the parent directory name of the specified path,
1318 /// </summary>
1319 /// <param name="path">Path</param>
1320 /// <returns>Parent directory name</returns>
1321 private string GetParentDirectory(string path)
1322 {
1323 if (String.IsNullOrEmpty(path) || path == "/")
1324 {
1325 return "/";
1326 }
1327
1328 if (path.EndsWith(@"/") || path.EndsWith(@"\"))
1329 {
1330 return this.GetParentDirectory(Path.GetDirectoryName(path));
1331 }
1332
1333 string result = Path.GetDirectoryName(path);
1334 if (result == null)
1335 {
1336 result = "/";
1337 }
1338
1339 return result;
1340 }
1341
1342 private bool removeDirRecursively(string fullPath,string callbackId)
1343 {
1344 try
1345 {
1346 if (fullPath == "/")
1347 {
1348 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR),callbackId);
1349 return false;
1350 }
1351
1352 using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
1353 {
1354 if (isoFile.DirectoryExists(fullPath))
1355 {
1356 string tempPath = File.AddSlashToDirectory(fullPath);
1357 string[] files = isoFile.GetFileNames(tempPath + "*");
1358 if (files.Length > 0)
1359 {
1360 foreach (string file in files)
1361 {
1362 isoFile.DeleteFile(tempPath + file);
1363 }
1364 }
1365 string[] dirs = isoFile.GetDirectoryNames(tempPath + "*");
1366 if (dirs.Length > 0)
1367 {
1368 foreach (string dir in dirs)
1369 {
1370 if (!removeDirRecursively(tempPath + dir, callbackId))
1371 {
1372 return false;
1373 }
1374 }
1375 }
1376 isoFile.DeleteDirectory(fullPath);
1377 }
1378 else
1379 {
1380 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR),callbackId);
1381 }
1382 }
1383 }
1384 catch (Exception ex)
1385 {
1386 if (!this.HandleException(ex))
1387 {
1388 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR),callbackId);
1389 return false;
1390 }
1391 }
1392 return true;
1393 }
1394
1395 private bool CanonicalCompare(string pathA, string pathB)
1396 {
1397 string a = pathA.Replace("//", "/");
1398 string b = pathB.Replace("//", "/");
1399
1400 return a.Equals(b, StringComparison.OrdinalIgnoreCase);
1401 }
1402
1403 /*
1404 * copyTo:["fullPath","parent", "newName"],
1405 * moveTo:["fullPath","parent", "newName"],
1406 */
1407 private void TransferTo(string options, bool move)
1408 {
1409 // TODO: try/catch
1410 string[] optStrings = getOptionStrings(options);
1411 string fullPath = optStrings[0];
1412 string parent = optStrings[1];
1413 string newFileName = optStrings[2];
1414 string callbackId = optStrings[3];
1415
1416 char[] invalids = Path.GetInvalidPathChars();
1417
1418 if (newFileName.IndexOfAny(invalids) > -1 || newFileName.IndexOf(":") > -1 )
1419 {
1420 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ENCODING_ERR), callbackId);
1421 return;
1422 }
1423
1424 try
1425 {
1426 if ((parent == null) || (string.IsNullOrEmpty(parent)) || (string.IsNullOrEmpty(fullPath)))
1427 {
1428 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
1429 return;
1430 }
1431
1432 string parentPath = File.AddSlashToDirectory(parent);
1433 string currentPath = fullPath;
1434
1435 using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
1436 {
1437 bool isFileExist = isoFile.FileExists(currentPath);
1438 bool isDirectoryExist = isoFile.DirectoryExists(currentPath);
1439 bool isParentExist = isoFile.DirectoryExists(parentPath);
1440
1441 if ( ( !isFileExist && !isDirectoryExist ) || !isParentExist )
1442 {
1443 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
1444 return;
1445 }
1446 string newName;
1447 string newPath;
1448 if (isFileExist)
1449 {
1450 newName = (string.IsNullOrEmpty(newFileName))
1451 ? Path.GetFileName(currentPath)
1452 : newFileName;
1453
1454 newPath = Path.Combine(parentPath, newName);
1455
1456 // sanity check ..
1457 // cannot copy file onto itself
1458 if (CanonicalCompare(newPath,currentPath)) //(parent + newFileName))
1459 {
1460 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, INVALID_MODIFICATION_ERR), callbackId);
1461 return;
1462 }
1463 else if (isoFile.DirectoryExists(newPath))
1464 {
1465 // there is already a folder with the same name, operation is not allowed
1466 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, INVALID_MODIFICATION_ERR), callbackId);
1467 return;
1468 }
1469 else if (isoFile.FileExists(newPath))
1470 { // remove destination file if exists, in other case there will be exception
1471 isoFile.DeleteFile(newPath);
1472 }
1473
1474 if (move)
1475 {
1476 isoFile.MoveFile(currentPath, newPath);
1477 }
1478 else
1479 {
1480 isoFile.CopyFile(currentPath, newPath, true);
1481 }
1482 }
1483 else
1484 {
1485 newName = (string.IsNullOrEmpty(newFileName))
1486 ? currentPath
1487 : newFileName;
1488
1489 newPath = Path.Combine(parentPath, newName);
1490
1491 if (move)
1492 {
1493 // remove destination directory if exists, in other case there will be exception
1494 // target directory should be empty
1495 if (!newPath.Equals(currentPath) && isoFile.DirectoryExists(newPath))
1496 {
1497 isoFile.DeleteDirectory(newPath);
1498 }
1499
1500 isoFile.MoveDirectory(currentPath, newPath);
1501 }
1502 else
1503 {
1504 CopyDirectory(currentPath, newPath, isoFile);
1505 }
1506 }
1507 FileEntry entry = FileEntry.GetEntry(newPath);
1508 if (entry != null)
1509 {
1510 DispatchCommandResult(new PluginResult(PluginResult.Status.OK, entry), callbackId);
1511 }
1512 else
1513 {
1514 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
1515 }
1516 }
1517
1518 }
1519 catch (Exception ex)
1520 {
1521 if (!this.HandleException(ex, callbackId))
1522 {
1523 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR), callbackId);
1524 }
1525 }
1526 }
1527
1528 private bool HandleException(Exception ex, string cbId="")
1529 {
1530 bool handled = false;
1531 string callbackId = String.IsNullOrEmpty(cbId) ? this.CurrentCommandCallbackId : cbId;
1532 if (ex is SecurityException)
1533 {
1534 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, SECURITY_ERR), callbackId);
1535 handled = true;
1536 }
1537 else if (ex is FileNotFoundException)
1538 {
1539 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
1540 handled = true;
1541 }
1542 else if (ex is ArgumentException)
1543 {
1544 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ENCODING_ERR), callbackId);
1545 handled = true;
1546 }
1547 else if (ex is IsolatedStorageException)
1548 {
1549 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, INVALID_MODIFICATION_ERR), callbackId);
1550 handled = true;
1551 }
1552 else if (ex is DirectoryNotFoundException)
1553 {
1554 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
1555 handled = true;
1556 }
1557 return handled;
1558 }
1559
1560 private void CopyDirectory(string sourceDir, string destDir, IsolatedStorageFile isoFile)
1561 {
1562 string path = File.AddSlashToDirectory(sourceDir);
1563
1564 bool bExists = isoFile.DirectoryExists(destDir);
1565
1566 if (!bExists)
1567 {
1568 isoFile.CreateDirectory(destDir);
1569 }
1570
1571 destDir = File.AddSlashToDirectory(destDir);
1572
1573 string[] files = isoFile.GetFileNames(path + "*");
1574
1575 if (files.Length > 0)
1576 {
1577 foreach (string file in files)
1578 {
1579 isoFile.CopyFile(path + file, destDir + file,true);
1580 }
1581 }
1582 string[] dirs = isoFile.GetDirectoryNames(path + "*");
1583 if (dirs.Length > 0)
1584 {
1585 foreach (string dir in dirs)
1586 {
1587 CopyDirectory(path + dir, destDir + dir, isoFile);
1588 }
1589 }
1590 }
1591
1592 private string RemoveExtraSlash(string path) {
1593 if (path.StartsWith("//")) {
1594 path = path.Remove(0, 1);
1595 path = RemoveExtraSlash(path);
1596 }
1597 return path;
1598 }
1599
1600 private string ResolvePath(string parentPath, string path)
1601 {
1602 string absolutePath = null;
1603
1604 if (path.Contains(".."))
1605 {
1606 if (parentPath.Length > 1 && parentPath.StartsWith("/") && parentPath !="/")
1607 {
1608 parentPath = RemoveExtraSlash(parentPath);
1609 }
1610
1611 string fullPath = Path.GetFullPath(Path.Combine(parentPath, path));
1612 absolutePath = fullPath.Replace(Path.GetPathRoot(fullPath), @"//");
1613 }
1614 else
1615 {
1616 absolutePath = Path.Combine(parentPath + "/", path);
1617 }
1618 return absolutePath;
1619 }
1620
1621 private void GetFileOrDirectory(string options, bool getDirectory)
1622 {
1623 FileOptions fOptions = new FileOptions();
1624 string[] args = getOptionStrings(options);
1625
1626 fOptions.FullPath = args[0];
1627 fOptions.Path = args[1];
1628
1629 string callbackId = args[3];
1630
1631 try
1632 {
1633 fOptions.CreatingOpt = JSON.JsonHelper.Deserialize<CreatingOptions>(args[2]);
1634 }
1635 catch (Exception)
1636 {
1637 DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION), callbackId);
1638 return;
1639 }
1640
1641 try
1642 {
1643 if ((string.IsNullOrEmpty(fOptions.Path)) || (string.IsNullOrEmpty(fOptions.FullPath)))
1644 {
1645 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
1646 return;
1647 }
1648
1649 string path;
1650
1651 if (fOptions.Path.Split(':').Length > 2)
1652 {
1653 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ENCODING_ERR), callbackId);
1654 return;
1655 }
1656
1657 try
1658 {
1659 path = ResolvePath(fOptions.FullPath, fOptions.Path);
1660 }
1661 catch (Exception)
1662 {
1663 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ENCODING_ERR), callbackId);
1664 return;
1665 }
1666
1667 using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
1668 {
1669 bool isFile = isoFile.FileExists(path);
1670 bool isDirectory = isoFile.DirectoryExists(path);
1671 bool create = (fOptions.CreatingOpt == null) ? false : fOptions.CreatingOpt.Create;
1672 bool exclusive = (fOptions.CreatingOpt == null) ? false : fOptions.CreatingOpt.Exclusive;
1673 if (create)
1674 {
1675 if (exclusive && (isoFile.FileExists(path) || isoFile.DirectoryExists(path)))
1676 {
1677 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, PATH_EXISTS_ERR), callbackId);
1678 return;
1679 }
1680
1681 // need to make sure the parent exists
1682 // it is an error to create a directory whose immediate parent does not yet exist
1683 // see issue: https://issues.apache.org/jira/browse/CB-339
1684 string[] pathParts = path.Split('/');
1685 string builtPath = pathParts[0];
1686 for (int n = 1; n < pathParts.Length - 1; n++)
1687 {
1688 builtPath += "/" + pathParts[n];
1689 if (!isoFile.DirectoryExists(builtPath))
1690 {
1691 Debug.WriteLine(String.Format("Error :: Parent folder \"{0}\" does not exist, when attempting to create \"{1}\"",builtPath,path));
1692 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
1693 return;
1694 }
1695 }
1696
1697 if ((getDirectory) && (!isDirectory))
1698 {
1699 isoFile.CreateDirectory(path);
1700 }
1701 else
1702 {
1703 if ((!getDirectory) && (!isFile))
1704 {
1705
1706 IsolatedStorageFileStream fileStream = isoFile.CreateFile(path);
1707 fileStream.Close();
1708 }
1709 }
1710 }
1711 else // (not create)
1712 {
1713 if ((!isFile) && (!isDirectory))
1714 {
1715 if (path.IndexOf("//www") == 0)
1716 {
1717 Uri fileUri = new Uri(path.Remove(0,2), UriKind.Relative);
1718 StreamResourceInfo streamInfo = Application.GetResourceStream(fileUri);
1719 if (streamInfo != null)
1720 {
1721 FileEntry _entry = FileEntry.GetEntry(fileUri.OriginalString,true);
1722
1723 DispatchCommandResult(new PluginResult(PluginResult.Status.OK, _entry), callbackId);
1724
1725 //using (BinaryReader br = new BinaryReader(streamInfo.Stream))
1726 //{
1727 // byte[] data = br.ReadBytes((int)streamInfo.Stream.Length);
1728
1729 //}
1730
1731 }
1732 else
1733 {
1734 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
1735 }
1736
1737
1738 }
1739 else
1740 {
1741 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
1742 }
1743 return;
1744 }
1745 if (((getDirectory) && (!isDirectory)) || ((!getDirectory) && (!isFile)))
1746 {
1747 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, TYPE_MISMATCH_ERR), callbackId);
1748 return;
1749 }
1750 }
1751 FileEntry entry = FileEntry.GetEntry(path);
1752 if (entry != null)
1753 {
1754 DispatchCommandResult(new PluginResult(PluginResult.Status.OK, entry), callbackId);
1755 }
1756 else
1757 {
1758 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId);
1759 }
1760 }
1761 }
1762 catch (Exception ex)
1763 {
1764 if (!this.HandleException(ex))
1765 {
1766 DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR), callbackId);
1767 }
1768 }
1769 }
1770
1771 private static string AddSlashToDirectory(string dirPath)
1772 {
1773 if (dirPath.EndsWith("/"))
1774 {
1775 return dirPath;
1776 }
1777 else
1778 {
1779 return dirPath + "/";
1780 }
1781 }
1782
1783 /// <summary>
1784 /// Returns file content in a form of base64 string
1785 /// </summary>
1786 /// <param name="stream">File stream</param>
1787 /// <returns>Base64 representation of the file</returns>
1788 private string GetFileContent(Stream stream)
1789 {
1790 int streamLength = (int)stream.Length;
1791 byte[] fileData = new byte[streamLength + 1];
1792 stream.Read(fileData, 0, streamLength);
1793 stream.Close();
1794 return Convert.ToBase64String(fileData);
1795 }
1796
1797 #endregion
1798
1799 }
1800}