arnaucube
arnaucube/openEventsPlatformAppPublic Self-hosted repository
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}