Return-Path: X-Original-To: apmail-incubator-callback-commits-archive@minotaur.apache.org Delivered-To: apmail-incubator-callback-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id A49139981 for ; Sat, 14 Jul 2012 00:24:45 +0000 (UTC) Received: (qmail 45025 invoked by uid 500); 14 Jul 2012 00:24:44 -0000 Delivered-To: apmail-incubator-callback-commits-archive@incubator.apache.org Received: (qmail 44978 invoked by uid 500); 14 Jul 2012 00:24:43 -0000 Mailing-List: contact callback-commits-help@incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: callback-dev@incubator.apache.org Delivered-To: mailing list callback-commits@incubator.apache.org Received: (qmail 44396 invoked by uid 99); 14 Jul 2012 00:24:43 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 14 Jul 2012 00:24:43 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id C9FD213FD2; Sat, 14 Jul 2012 00:24:42 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: purplecabbage@apache.org To: callback-commits@incubator.apache.org X-Mailer: ASF-Git Admin Mailer Subject: [33/46] rejigger to the new structure Message-Id: <20120714002442.C9FD213FD2@tyr.zones.apache.org> Date: Sat, 14 Jul 2012 00:24:42 +0000 (UTC) http://git-wip-us.apache.org/repos/asf/incubator-cordova-wp7/blob/62d32605/templates/standalone/cordovalib/Commands/File.cs ---------------------------------------------------------------------- diff --git a/templates/standalone/cordovalib/Commands/File.cs b/templates/standalone/cordovalib/Commands/File.cs new file mode 100644 index 0000000..83492cf --- /dev/null +++ b/templates/standalone/cordovalib/Commands/File.cs @@ -0,0 +1,1406 @@ +/* + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.IsolatedStorage; +using System.Runtime.Serialization; +using System.Security; +using System.Text; +using System.Diagnostics; + +namespace WP7CordovaClassLib.Cordova.Commands +{ + /// + /// Provides access to isolated storage + /// + public class File : BaseCommand + { + // Error codes + public const int NOT_FOUND_ERR = 1; + public const int SECURITY_ERR = 2; + public const int ABORT_ERR = 3; + public const int NOT_READABLE_ERR = 4; + public const int ENCODING_ERR = 5; + public const int NO_MODIFICATION_ALLOWED_ERR = 6; + public const int INVALID_STATE_ERR = 7; + public const int SYNTAX_ERR = 8; + public const int INVALID_MODIFICATION_ERR = 9; + public const int QUOTA_EXCEEDED_ERR = 10; + public const int TYPE_MISMATCH_ERR = 11; + public const int PATH_EXISTS_ERR = 12; + + // File system options + public const int TEMPORARY = 0; + public const int PERSISTENT = 1; + public const int RESOURCE = 2; + public const int APPLICATION = 3; + + /// + /// Temporary directory name + /// + private readonly string TMP_DIRECTORY_NAME = "tmp"; + + /// + /// Represents error code for callback + /// + [DataContract] + public class ErrorCode + { + /// + /// Error code + /// + [DataMember(IsRequired = true, Name = "code")] + public int Code { get; set; } + + /// + /// Creates ErrorCode object + /// + public ErrorCode(int code) + { + this.Code = code; + } + } + + /// + /// Represents File action options. + /// + [DataContract] + public class FileOptions + { + /// + /// File path + /// + /// + private string _fileName; + [DataMember(Name = "fileName")] + public string FilePath + { + get + { + return this._fileName; + } + + set + { + int index = value.IndexOfAny(new char[] { '#', '?' }); + this._fileName = index > -1 ? value.Substring(0, index) : value; + } + } + + /// + /// Full entryPath + /// + [DataMember(Name = "fullPath")] + public string FullPath { get; set; } + + /// + /// Directory name + /// + [DataMember(Name = "dirName")] + public string DirectoryName { get; set; } + + /// + /// Path to create file/directory + /// + [DataMember(Name = "path")] + public string Path { get; set; } + + /// + /// The encoding to use to encode the file's content. Default is UTF8. + /// + [DataMember(Name = "encoding")] + public string Encoding { get; set; } + + /// + /// Uri to get file + /// + /// + private string _uri; + [DataMember(Name = "uri")] + public string Uri + { + get + { + return this._uri; + } + + set + { + int index = value.IndexOfAny(new char[] { '#', '?' }); + this._uri = index > -1 ? value.Substring(0, index) : value; + } + } + + /// + /// Size to truncate file + /// + [DataMember(Name = "size")] + public long Size { get; set; } + + /// + /// Data to write in file + /// + [DataMember(Name = "data")] + public string Data { get; set; } + + /// + /// Position the writing starts with + /// + [DataMember(Name = "position")] + public int Position { get; set; } + + /// + /// Type of file system requested + /// + [DataMember(Name = "type")] + public int FileSystemType { get; set; } + + /// + /// New file/directory name + /// + [DataMember(Name = "newName")] + public string NewName { get; set; } + + /// + /// Destination directory to copy/move file/directory + /// + [DataMember(Name = "parent")] + public string Parent { get; set; } + + /// + /// Options for getFile/getDirectory methods + /// + [DataMember(Name = "options")] + public CreatingOptions CreatingOpt { get; set; } + + /// + /// Creates options object with default parameters + /// + public FileOptions() + { + this.SetDefaultValues(new StreamingContext()); + } + + /// + /// Initializes default values for class fields. + /// Implemented in separate method because default constructor is not invoked during deserialization. + /// + /// + [OnDeserializing()] + public void SetDefaultValues(StreamingContext context) + { + this.Encoding = "UTF-8"; + this.FilePath = ""; + this.FileSystemType = -1; + } + } + + /// + /// Stores image info + /// + [DataContract] + public class FileMetadata + { + [DataMember(Name = "fileName")] + public string FileName { get; set; } + + [DataMember(Name = "fullPath")] + public string FullPath { get; set; } + + [DataMember(Name = "type")] + public string Type { get; set; } + + [DataMember(Name = "lastModifiedDate")] + public string LastModifiedDate { get; set; } + + [DataMember(Name = "size")] + public long Size { get; set; } + + public FileMetadata(string filePath) + { + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + if ((string.IsNullOrEmpty(filePath)) || (!isoFile.FileExists(filePath))) + { + throw new FileNotFoundException("File doesn't exist"); + } + //TODO get file size the other way if possible + using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(filePath, FileMode.Open, FileAccess.Read, isoFile)) + { + this.Size = stream.Length; + } + this.FullPath = filePath; + this.FileName = System.IO.Path.GetFileName(filePath); + this.Type = MimeTypeMapper.GetMimeType(filePath); + this.LastModifiedDate = isoFile.GetLastWriteTime(filePath).DateTime.ToString(); + } + } + } + + /// + /// Represents file or directory modification metadata + /// + [DataContract] + public class ModificationMetadata + { + /// + /// Modification time + /// + [DataMember] + public string modificationTime { get; set; } + } + + /// + /// Represents file or directory entry + /// + [DataContract] + public class FileEntry + { + + /// + /// File type + /// + [DataMember(Name = "isFile")] + public bool IsFile { get; set; } + + /// + /// Directory type + /// + [DataMember(Name = "isDirectory")] + public bool IsDirectory { get; set; } + + /// + /// File/directory name + /// + [DataMember(Name = "name")] + public string Name { get; set; } + + /// + /// Full path to file/directory + /// + [DataMember(Name = "fullPath")] + public string FullPath { get; set; } + + public static FileEntry GetEntry(string filePath) + { + FileEntry entry = null; + try + { + entry = new FileEntry(filePath); + + } + catch (Exception) + { + Debug.WriteLine("Exception in GetEntry for filePath :: " + filePath); + } + return entry; + } + + /// + /// Creates object and sets necessary properties + /// + /// + public FileEntry(string filePath) + { + if (string.IsNullOrEmpty(filePath)) + { + throw new ArgumentException(); + } + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + this.IsFile = isoFile.FileExists(filePath); + this.IsDirectory = isoFile.DirectoryExists(filePath); + if (IsFile) + { + this.Name = Path.GetFileName(filePath); + } + else if (IsDirectory) + { + this.Name = this.GetDirectoryName(filePath); + if (string.IsNullOrEmpty(Name)) + { + this.Name = "/"; + } + } + else + { + throw new FileNotFoundException(); + } + + this.FullPath = new Uri(filePath).LocalPath; + } + } + + /// + /// Extracts directory name from path string + /// Path should refer to a directory, for example \foo\ or /foo. + /// + /// + /// + private string GetDirectoryName(string path) + { + if (String.IsNullOrEmpty(path)) + { + return path; + } + + string[] split = path.Split(new char[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries); + if (split.Length < 1) + { + return null; + } + else + { + return split[split.Length - 1]; + } + } + } + + + /// + /// Represents info about requested file system + /// + [DataContract] + public class FileSystemInfo + { + /// + /// file system type + /// + [DataMember(Name = "name", IsRequired = true)] + public string Name { get; set; } + + /// + /// Root directory entry + /// + [DataMember(Name = "root", EmitDefaultValue = false)] + public FileEntry Root { get; set; } + + /// + /// Creates class instance + /// + /// + /// Root directory + public FileSystemInfo(string name, FileEntry rootEntry = null) + { + Name = name; + Root = rootEntry; + } + } + + [DataContract] + public class CreatingOptions + { + /// + /// Create file/directory if is doesn't exist + /// + [DataMember(Name = "create")] + public bool Create { get; set; } + + /// + /// Generate an exception if create=true and file/directory already exists + /// + [DataMember(Name = "exclusive")] + public bool Exclusive { get; set; } + + + } + + /// + /// File options + /// + private FileOptions fileOptions; + + private bool LoadFileOptions(string options) + { + try + { + fileOptions = JSON.JsonHelper.Deserialize(options); + } + catch (Exception) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION)); + return false; + } + return true; + } + + /// + /// Gets amount of free space available for Isolated Storage + /// + /// No options is needed for this method + public void getFreeDiskSpace(string options) + { + try + { + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, isoFile.AvailableFreeSpace)); + } + } + catch (IsolatedStorageException) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR)); + } + catch (Exception ex) + { + if (!this.HandleException(ex)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR)); + } + } + } + + /// + /// Check if file exists + /// + /// File path + public void testFileExists(string options) + { + IsDirectoryOrFileExist(options, false); + } + + /// + /// Check if directory exists + /// + /// directory name + public void testDirectoryExists(string options) + { + IsDirectoryOrFileExist(options, true); + } + + /// + /// Check if file or directory exist + /// + /// File path/Directory name + /// Flag to recognize what we should check + public void IsDirectoryOrFileExist(string options, bool isDirectory) + { + if (!LoadFileOptions(options)) + { + return; + } + + try + { + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + bool isExist; + if (isDirectory) + { + isExist = isoFile.DirectoryExists(fileOptions.DirectoryName); + } + else + { + isExist = isoFile.FileExists(fileOptions.FilePath); + } + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, isExist)); + } + } + catch (IsolatedStorageException) // default handler throws INVALID_MODIFICATION_ERR + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR)); + } + catch (Exception ex) + { + if (!this.HandleException(ex)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR)); + } + } + + } + + public void readAsDataURL(string options) + { + if (!LoadFileOptions(options)) + { + return; + } + + try + { + string base64URL = null; + + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + if (!isoFile.FileExists(fileOptions.FilePath)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR)); + return; + } + string mimeType = MimeTypeMapper.GetMimeType(fileOptions.FilePath); + + using (IsolatedStorageFileStream stream = isoFile.OpenFile(fileOptions.FilePath, FileMode.Open, FileAccess.Read)) + { + string base64String = GetFileContent(stream); + base64URL = "data:" + mimeType + ";base64," + base64String; + } + } + + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, base64URL)); + } + catch (Exception ex) + { + if (!this.HandleException(ex)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR)); + } + } + } + + public void readAsText(string options) + { + if (!LoadFileOptions(options)) + { + return; + } + + try + { + string text; + + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + if (!isoFile.FileExists(fileOptions.FilePath)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR)); + return; + } + Encoding encoding = Encoding.GetEncoding(fileOptions.Encoding); + + using (TextReader reader = new StreamReader(isoFile.OpenFile(fileOptions.FilePath, FileMode.Open, FileAccess.Read), encoding)) + { + text = reader.ReadToEnd(); + } + } + + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, text)); + } + catch (Exception ex) + { + if (!this.HandleException(ex)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR)); + } + } + } + + + public void truncate(string options) + { + if (!LoadFileOptions(options)) + { + return; + } + + try + { + long streamLength = 0; + + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + if (!isoFile.FileExists(fileOptions.FilePath)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR)); + return; + } + + using (FileStream stream = new IsolatedStorageFileStream(fileOptions.FilePath, FileMode.Open, FileAccess.ReadWrite, isoFile)) + { + if (0 <= fileOptions.Size && fileOptions.Size < stream.Length) + { + stream.SetLength(fileOptions.Size); + } + + streamLength = stream.Length; + } + } + + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, streamLength)); + } + catch (Exception ex) + { + if (!this.HandleException(ex)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR)); + } + } + } + + public void write(string options) + { + if (!LoadFileOptions(options)) + { + return; + } + + try + { + if (string.IsNullOrEmpty(fileOptions.Data)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION)); + return; + } + + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + // create the file if not exists + if (!isoFile.FileExists(fileOptions.FilePath)) + { + var file = isoFile.CreateFile(fileOptions.FilePath); + file.Close(); + } + + using (FileStream stream = new IsolatedStorageFileStream(fileOptions.FilePath, FileMode.Open, FileAccess.ReadWrite, isoFile)) + { + if (0 <= fileOptions.Position && fileOptions.Position < stream.Length) + { + stream.SetLength(fileOptions.Position); + } + using (BinaryWriter writer = new BinaryWriter(stream)) + { + writer.Seek(0, SeekOrigin.End); + writer.Write(fileOptions.Data.ToCharArray()); + } + } + } + + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, fileOptions.Data.Length)); + } + catch (Exception ex) + { + if (!this.HandleException(ex)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR)); + } + } + } + + /// + /// Look up metadata about this entry. + /// + /// filePath to entry + public void getMetadata(string options) + { + if (!LoadFileOptions(options)) + { + return; + } + + try + { + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + if (isoFile.FileExists(fileOptions.FullPath)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, + new ModificationMetadata() { modificationTime = isoFile.GetLastWriteTime(fileOptions.FullPath).DateTime.ToString() })); + } + else if (isoFile.DirectoryExists(fileOptions.FullPath)) + { + string modTime = isoFile.GetLastWriteTime(fileOptions.FullPath).DateTime.ToString(); + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, new ModificationMetadata() { modificationTime = modTime })); + } + else + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR)); + } + + } + } + catch (IsolatedStorageException) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR)); + } + catch (Exception ex) + { + if (!this.HandleException(ex)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR)); + } + } + + } + + + /// + /// Returns a File that represents the current state of the file that this FileEntry represents. + /// + /// filePath to entry + /// + public void getFileMetadata(string options) + { + if (!LoadFileOptions(options)) + { + return; + } + + try + { + FileMetadata metaData = new FileMetadata(fileOptions.FullPath); + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, metaData)); + } + catch (IsolatedStorageException) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR)); + } + catch (Exception ex) + { + if (!this.HandleException(ex)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR)); + } + } + } + + /// + /// Look up the parent DirectoryEntry containing this Entry. + /// If this Entry is the root of IsolatedStorage, its parent is itself. + /// + /// + public void getParent(string options) + { + if (!LoadFileOptions(options)) + { + return; + } + + try + { + + if (string.IsNullOrEmpty(fileOptions.FullPath)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR)); + return; + } + + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + FileEntry entry; + + if (isoFile.FileExists(fileOptions.FullPath) || isoFile.DirectoryExists(fileOptions.FullPath)) + { + string path = this.GetParentDirectory(fileOptions.FullPath); + entry = FileEntry.GetEntry(path); + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, entry)); + } + else + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR)); + } + + } + } + catch (Exception ex) + { + if (!this.HandleException(ex)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR)); + } + } + } + + public void remove(string options) + { + if (!LoadFileOptions(options)) + { + return; + } + + try + { + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + if (isoFile.FileExists(fileOptions.FullPath)) + { + isoFile.DeleteFile(fileOptions.FullPath); + } + else + { + if (isoFile.DirectoryExists(fileOptions.FullPath)) + { + isoFile.DeleteDirectory(fileOptions.FullPath); + } + else + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR)); + return; + } + } + DispatchCommandResult(new PluginResult(PluginResult.Status.OK)); + } + } + catch (Exception ex) + { + if (!this.HandleException(ex)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR)); + } + } + } + + public void removeRecursively(string options) + { + if (!LoadFileOptions(options)) + { + return; + } + + if (string.IsNullOrEmpty(fileOptions.FullPath)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR)); + return; + } + removeDirRecursively(fileOptions.FullPath); + DispatchCommandResult(new PluginResult(PluginResult.Status.OK)); + } + + public void readEntries(string options) + { + if (!LoadFileOptions(options)) + { + return; + } + + try + { + if (string.IsNullOrEmpty(fileOptions.FullPath)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR)); + return; + } + + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + if (isoFile.DirectoryExists(fileOptions.FullPath)) + { + string path = File.AddSlashToDirectory(fileOptions.FullPath); + List entries = new List(); + string[] files = isoFile.GetFileNames(path + "*"); + string[] dirs = isoFile.GetDirectoryNames(path + "*"); + foreach (string file in files) + { + entries.Add(FileEntry.GetEntry(path + file)); + } + foreach (string dir in dirs) + { + entries.Add(FileEntry.GetEntry(path + dir + "/")); + } + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, entries)); + } + else + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR)); + } + } + } + //catch (SecurityException) + //{ + // DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, SECURITY_ERR)); + //} + catch (Exception ex) + { + if (!this.HandleException(ex)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR)); + } + } + } + + public void requestFileSystem(string options) + { + if (!LoadFileOptions(options)) + { + return; + } + + try + { + if (fileOptions.Size != 0) + { + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + long availableSize = isoFile.AvailableFreeSpace; + if (fileOptions.Size > availableSize) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, QUOTA_EXCEEDED_ERR)); + return; + } + } + } + + if (fileOptions.FileSystemType == PERSISTENT) + { + // TODO: this should be in it's own folder to prevent overwriting of the app assets, which are also in ISO + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, new FileSystemInfo("persistent", FileEntry.GetEntry("/")))); + } + else if (fileOptions.FileSystemType == TEMPORARY) + { + using (IsolatedStorageFile isoStorage = IsolatedStorageFile.GetUserStoreForApplication()) + { + if (!isoStorage.FileExists(TMP_DIRECTORY_NAME)) + { + isoStorage.CreateDirectory(TMP_DIRECTORY_NAME); + } + } + + string tmpFolder = "/" + TMP_DIRECTORY_NAME + "/"; + + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, new FileSystemInfo("temporary", FileEntry.GetEntry(tmpFolder)))); + } + else if (fileOptions.FileSystemType == RESOURCE) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, new FileSystemInfo("resource"))); + } + else if (fileOptions.FileSystemType == APPLICATION) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, new FileSystemInfo("application"))); + } + else + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR)); + } + + } + //catch (SecurityException) + //{ + // DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, SECURITY_ERR)); + //} + //catch (FileNotFoundException) + //{ + // DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR)); + //} + catch (Exception ex) + { + if (!this.HandleException(ex)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR)); + } + } + } + + public void resolveLocalFileSystemURI(string options) + { + if (!LoadFileOptions(options)) + { + return; + } + + try + { + if (!Uri.IsWellFormedUriString(fileOptions.Uri, UriKind.Absolute)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ENCODING_ERR)); + return; + } + + Uri fileUri = new Uri(Uri.UnescapeDataString(fileOptions.Uri)); + string path = fileUri.LocalPath; + + // TODO: research this : + //if (Uri.UriSchemeFile == fileUri.Scheme) + //{ + //} + + FileEntry uriEntry = FileEntry.GetEntry(path); + if (uriEntry != null) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, uriEntry)); + } + else + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR)); + } + } + //catch (SecurityException) + //{ + // DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, SECURITY_ERR)); + //} + catch (Exception ex) + { + if (!this.HandleException(ex)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR)); + } + } + } + + public void copyTo(string options) + { + TransferTo(options, false); + } + + public void moveTo(string options) + { + TransferTo(options, true); + } + + public void getFile(string options) + { + GetFileOrDirectory(options, false); + } + + public void getDirectory(string options) + { + GetFileOrDirectory(options, true); + } + + #region internal functionality + + /// + /// Retrieves the parent directory name of the specified path, + /// + /// Path + /// Parent directory name + private string GetParentDirectory(string path) + { + if (String.IsNullOrEmpty(path) || path == "/") + { + return "/"; + } + + if (path.EndsWith(@"/") || path.EndsWith(@"\")) + { + return this.GetParentDirectory(Path.GetDirectoryName(path)); + } + + return Path.GetDirectoryName(path); + } + + private void removeDirRecursively(string fullPath) + { + try + { + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + if (isoFile.DirectoryExists(fullPath)) + { + string path = File.AddSlashToDirectory(fullPath); + string[] files = isoFile.GetFileNames(path + "*"); + if (files.Length > 0) + { + foreach (string file in files) + { + isoFile.DeleteFile(path + file); + } + } + string[] dirs = isoFile.GetDirectoryNames(path + "*"); + if (dirs.Length > 0) + { + foreach (string dir in dirs) + { + removeDirRecursively(path + dir + "/"); + } + } + isoFile.DeleteDirectory(Path.GetDirectoryName(path)); + } + else + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR)); + } + } + } + catch (Exception ex) + { + if (!this.HandleException(ex)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR)); + } + } + } + + private void TransferTo(string options, bool move) + { + if (!LoadFileOptions(options)) + { + return; + } + + try + { + if ((fileOptions.Parent == null) || (string.IsNullOrEmpty(fileOptions.Parent)) || (string.IsNullOrEmpty(fileOptions.FullPath))) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR)); + return; + } + + string parentPath = File.AddSlashToDirectory(fileOptions.Parent); + string currentPath = fileOptions.FullPath; + + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + bool isFileExist = isoFile.FileExists(currentPath); + bool isDirectoryExist = isoFile.DirectoryExists(currentPath); + bool isParentExist = isoFile.DirectoryExists(parentPath); + + if (((!isFileExist) && (!isDirectoryExist)) || (!isParentExist)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR)); + return; + } + string newName; + string newPath; + if (isFileExist) + { + newName = (string.IsNullOrEmpty(fileOptions.NewName)) + ? Path.GetFileName(currentPath) + : fileOptions.NewName; + + newPath = Path.Combine(parentPath, newName); + + // remove destination file if exists, in other case there will be exception + if (!newPath.Equals(currentPath) && isoFile.FileExists(newPath)) + { + isoFile.DeleteFile(newPath); + } + + if (move) + { + isoFile.MoveFile(currentPath, newPath); + } + else + { + isoFile.CopyFile(currentPath, newPath, true); + } + } + else + { + newName = (string.IsNullOrEmpty(fileOptions.NewName)) + ? currentPath + : fileOptions.NewName; + + newPath = Path.Combine(parentPath, newName); + + if (move) + { + + // remove destination directory if exists, in other case there will be exception + // target directory should be empty + if (!newPath.Equals(currentPath) && isoFile.DirectoryExists(newPath)) + { + isoFile.DeleteDirectory(newPath); + } + + isoFile.MoveDirectory(currentPath, newPath); + } + else + { + this.CopyDirectory(currentPath, newPath, isoFile); + } + } + FileEntry entry = FileEntry.GetEntry(newPath); + if (entry != null) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, entry)); + } + else + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR)); + } + } + + } + catch (Exception ex) + { + if (!this.HandleException(ex)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR)); + } + } + } + + private bool HandleException(Exception ex) + { + bool handled = false; + if (ex is SecurityException) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, SECURITY_ERR)); + handled = true; + } + else if (ex is FileNotFoundException) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR)); + handled = true; + } + else if (ex is ArgumentException) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ENCODING_ERR)); + handled = true; + } + else if (ex is IsolatedStorageException) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, INVALID_MODIFICATION_ERR)); + handled = true; + } + else if (ex is DirectoryNotFoundException) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR)); + handled = true; + } + return handled; + } + + private void CopyDirectory(string sourceDir, string destDir, IsolatedStorageFile isoFile) + { + string path = File.AddSlashToDirectory(sourceDir); + + if (!isoFile.DirectoryExists(destDir)) + { + isoFile.CreateDirectory(destDir); + } + destDir = File.AddSlashToDirectory(destDir); + string[] files = isoFile.GetFileNames(path + "*"); + if (files.Length > 0) + { + foreach (string file in files) + { + isoFile.CopyFile(path + file, destDir + file); + } + } + string[] dirs = isoFile.GetDirectoryNames(path + "*"); + if (dirs.Length > 0) + { + foreach (string dir in dirs) + { + CopyDirectory(path + dir, destDir + dir, isoFile); + } + } + } + + private void GetFileOrDirectory(string options, bool getDirectory) + { + if (!LoadFileOptions(options)) + { + return; + } + if (fileOptions == null) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION)); + return; + } + + try + { + if ((string.IsNullOrEmpty(fileOptions.Path)) || (string.IsNullOrEmpty(fileOptions.FullPath))) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR)); + return; + } + + string path; + + try + { + path = Path.Combine(fileOptions.FullPath + "/", fileOptions.Path); + } + catch (Exception) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ENCODING_ERR)); + return; + } + + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + bool isFile = isoFile.FileExists(path); + bool isDirectory = isoFile.DirectoryExists(path); + bool create = (fileOptions.CreatingOpt == null) ? false : fileOptions.CreatingOpt.Create; + bool exclusive = (fileOptions.CreatingOpt == null) ? false : fileOptions.CreatingOpt.Exclusive; + if (create) + { + if (exclusive && (isoFile.FileExists(path) || isoFile.DirectoryExists(path))) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, PATH_EXISTS_ERR)); + return; + } + + if ((getDirectory) && (!isDirectory)) + { + isoFile.CreateDirectory(path); + } + else + { + if ((!getDirectory) && (!isFile)) + { + + IsolatedStorageFileStream fileStream = isoFile.CreateFile(path); + fileStream.Close(); + } + } + + } + else + { + if ((!isFile) && (!isDirectory)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR)); + return; + } + if (((getDirectory) && (!isDirectory)) || ((!getDirectory) && (!isFile))) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, TYPE_MISMATCH_ERR)); + return; + } + } + FileEntry entry = FileEntry.GetEntry(path); + if (entry != null) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, entry)); + } + else + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR)); + } + } + } + catch (Exception ex) + { + if (!this.HandleException(ex)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR)); + } + } + } + + private static string AddSlashToDirectory(string dirPath) + { + if (dirPath.EndsWith("/")) + { + return dirPath; + } + else + { + return dirPath + "/"; + } + } + + /// + /// Returns file content in a form of base64 string + /// + /// File stream + /// Base64 representation of the file + private string GetFileContent(Stream stream) + { + int streamLength = (int)stream.Length; + byte[] fileData = new byte[streamLength + 1]; + stream.Read(fileData, 0, streamLength); + stream.Close(); + return Convert.ToBase64String(fileData); + } + + #endregion + + } +} http://git-wip-us.apache.org/repos/asf/incubator-cordova-wp7/blob/62d32605/templates/standalone/cordovalib/Commands/FileTransfer.cs ---------------------------------------------------------------------- diff --git a/templates/standalone/cordovalib/Commands/FileTransfer.cs b/templates/standalone/cordovalib/Commands/FileTransfer.cs new file mode 100644 index 0000000..dbdc8ab --- /dev/null +++ b/templates/standalone/cordovalib/Commands/FileTransfer.cs @@ -0,0 +1,521 @@ +/* + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.IsolatedStorage; +using System.Net; +using System.Runtime.Serialization; +using System.Windows; +using System.Security; +using System.Diagnostics; + +namespace WP7CordovaClassLib.Cordova.Commands +{ + public class FileTransfer : BaseCommand + { + public class DownloadRequestState + { + // This class stores the State of the request. + public HttpWebRequest request; + public DownloadOptions options; + + public DownloadRequestState() + { + request = null; + options = null; + } + } + + /// + /// Boundary symbol + /// + private string Boundary = "----------------------------" + DateTime.Now.Ticks.ToString("x"); + + // Error codes + public const int FileNotFoundError = 1; + public const int InvalidUrlError = 2; + public const int ConnectionError = 3; + + /// + /// Options for downloading file + /// + [DataContract] + public class DownloadOptions + { + /// + /// File path to download to + /// + [DataMember(Name = "filePath", IsRequired = true)] + public string FilePath { get; set; } + + /// + /// Server address to the file to download + /// + [DataMember(Name = "url", IsRequired = true)] + public string Url { get; set; } + } + + /// + /// Options for uploading file + /// + [DataContract] + public class UploadOptions + { + /// + /// File path to upload + /// + [DataMember(Name = "filePath", IsRequired = true)] + public string FilePath { get; set; } + + /// + /// Server address + /// + [DataMember(Name = "server", IsRequired = true)] + public string Server { get; set; } + + /// + /// File key + /// + [DataMember(Name = "fileKey")] + public string FileKey { get; set; } + + /// + /// File name on the server + /// + [DataMember(Name = "fileName")] + public string FileName { get; set; } + + /// + /// File Mime type + /// + [DataMember(Name = "mimeType")] + public string MimeType { get; set; } + + + /// + /// Additional options + /// + [DataMember(Name = "params")] + public string Params { get; set; } + + /// + /// Flag to recognize if we should trust every host (only in debug environments) + /// + [DataMember(Name = "debug")] + public bool Debug { get; set; } + + /// + /// Creates options object with default parameters + /// + public UploadOptions() + { + this.SetDefaultValues(new StreamingContext()); + } + + /// + /// Initializes default values for class fields. + /// Implemented in separate method because default constructor is not invoked during deserialization. + /// + /// + [OnDeserializing()] + public void SetDefaultValues(StreamingContext context) + { + this.FileKey = "file"; + this.FileName = "image.jpg"; + this.MimeType = "image/jpeg"; + } + + } + + /// + /// Uploading response info + /// + [DataContract] + public class FileUploadResult + { + /// + /// Amount of sent bytes + /// + [DataMember(Name = "bytesSent")] + public long BytesSent { get; set; } + + /// + /// Server response code + /// + [DataMember(Name = "responseCode")] + public long ResponseCode { get; set; } + + /// + /// Server response + /// + [DataMember(Name = "response", EmitDefaultValue = false)] + public string Response { get; set; } + + /// + /// Creates FileUploadResult object with response values + /// + /// Amount of sent bytes + /// Server response code + /// Server response + public FileUploadResult(long bytesSent, long responseCode, string response) + { + this.BytesSent = bytesSent; + this.ResponseCode = responseCode; + this.Response = response; + } + } + + /// + /// Represents transfer error codes for callback + /// + [DataContract] + public class FileTransferError + { + /// + /// Error code + /// + [DataMember(Name = "code", IsRequired = true)] + public int Code { get; set; } + + /// + /// The source URI + /// + [DataMember(Name = "source", IsRequired = true)] + public string Source { get; set; } + + /// + /// The target URI + /// + [DataMember(Name = "target", IsRequired = true)] + public string Target { get; set; } + + /// + /// The http status code response from the remote URI + /// + [DataMember(Name = "http_status", IsRequired = true)] + public int HttpStatus { get; set; } + + /// + /// Creates FileTransferError object + /// + /// Error code + public FileTransferError(int errorCode) + { + this.Code = errorCode; + this.Source = null; + this.Target = null; + this.HttpStatus = 0; + } + public FileTransferError(int errorCode, string source, string target, int status) + { + this.Code = errorCode; + this.Source = source; + this.Target = target; + this.HttpStatus = status; + } + } + + /// + /// Upload options + /// + private UploadOptions uploadOptions; + + /// + /// Bytes sent + /// + private long bytesSent; + + /// + /// sends a file to a server + /// + /// Upload options + public void upload(string options) + { + Debug.WriteLine("options = " + options); + options = options.Replace("{}", "null"); + + try + { + try + { + uploadOptions = JSON.JsonHelper.Deserialize(options); + } + catch (Exception) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION)); + return; + } + + Uri serverUri; + try + { + serverUri = new Uri(uploadOptions.Server); + } + catch (Exception) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(InvalidUrlError, uploadOptions.Server, null, 0))); + return; + } + HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(serverUri); + webRequest.ContentType = "multipart/form-data;boundary=" + Boundary; + webRequest.Method = "POST"; + webRequest.BeginGetRequestStream(WriteCallback, webRequest); + } + catch (Exception) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(ConnectionError))); + } + } + + public void download(string options) + { + DownloadOptions downloadOptions = null; + HttpWebRequest webRequest = null; + + try + { + downloadOptions = JSON.JsonHelper.Deserialize(options); + } + catch (Exception) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION)); + return; + } + + try + { + webRequest = (HttpWebRequest)WebRequest.Create(downloadOptions.Url); + } + catch (Exception) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(InvalidUrlError, downloadOptions.Url, null, 0))); + return; + } + + if (downloadOptions != null && webRequest != null) + { + DownloadRequestState state = new DownloadRequestState(); + state.options = downloadOptions; + state.request = webRequest; + webRequest.BeginGetResponse(new AsyncCallback(downloadCallback), state); + } + + + + } + + /// + /// + /// + /// + private void downloadCallback(IAsyncResult asynchronousResult) + { + DownloadRequestState reqState = (DownloadRequestState)asynchronousResult.AsyncState; + HttpWebRequest request = reqState.request; + + try + { + HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asynchronousResult); + + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + // create the file if not exists + if (!isoFile.FileExists(reqState.options.FilePath)) + { + var file = isoFile.CreateFile(reqState.options.FilePath); + file.Close(); + } + + using (FileStream fileStream = new IsolatedStorageFileStream(reqState.options.FilePath, FileMode.Open, FileAccess.Write, isoFile)) + { + long totalBytes = response.ContentLength; + int bytesRead = 0; + using (BinaryReader reader = new BinaryReader(response.GetResponseStream())) + { + + using (BinaryWriter writer = new BinaryWriter(fileStream)) + { + int BUFFER_SIZE = 1024; + byte[] buffer; + + while (true) + { + buffer = reader.ReadBytes(BUFFER_SIZE); + // fire a progress event ? + bytesRead += buffer.Length; + if (buffer.Length > 0) + { + writer.Write(buffer); + } + else + { + writer.Close(); + reader.Close(); + fileStream.Close(); + break; + } + } + } + + } + + + } + } + WP7CordovaClassLib.Cordova.Commands.File.FileEntry entry = new WP7CordovaClassLib.Cordova.Commands.File.FileEntry(reqState.options.FilePath); + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, entry)); + } + catch (IsolatedStorageException) + { + // Trying to write the file somewhere within the IsoStorage. + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(FileNotFoundError))); + } + catch (SecurityException) + { + // Trying to write the file somewhere not allowed. + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(FileNotFoundError))); + } + catch (WebException webex) + { + // TODO: probably need better work here to properly respond with all http status codes back to JS + // Right now am jumping through hoops just to detect 404. + if ((webex.Status == WebExceptionStatus.ProtocolError && ((HttpWebResponse)webex.Response).StatusCode == HttpStatusCode.NotFound) || webex.Status == WebExceptionStatus.UnknownError) + { + // Weird MSFT detection of 404... seriously... just give us the f(*&#$@ status code as a number ffs!!! + // "Numbers for HTTP status codes? Nah.... let's create our own set of enums/structs to abstract that stuff away." + // FACEPALM + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(ConnectionError, null, null, 404))); + } + else + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(ConnectionError))); + } + } + catch (Exception) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(FileNotFoundError))); + } + } + + + + /// + /// Read file from Isolated Storage and sends it to server + /// + /// + private void WriteCallback(IAsyncResult asynchronousResult) + { + try + { + HttpWebRequest webRequest = (HttpWebRequest)asynchronousResult.AsyncState; + using (Stream requestStream = (webRequest.EndGetRequestStream(asynchronousResult))) + { + string lineStart = "--"; + string lineEnd = Environment.NewLine; + byte[] boundaryBytes = System.Text.Encoding.UTF8.GetBytes(lineStart + Boundary + lineEnd); + string formdataTemplate = "Content-Disposition: form-data; name=\"{0}\"" + lineEnd + lineEnd + "{1}" + lineEnd; + + if (uploadOptions.Params != null) + { + + string[] arrParams = uploadOptions.Params.Split(new[] { '&' }, StringSplitOptions.RemoveEmptyEntries); + + foreach (string param in arrParams) + { + string[] split = param.Split('='); + string key = split[0]; + string val = split[1]; + requestStream.Write(boundaryBytes, 0, boundaryBytes.Length); + string formItem = string.Format(formdataTemplate, key, val); + byte[] formItemBytes = System.Text.Encoding.UTF8.GetBytes(formItem); + requestStream.Write(formItemBytes, 0, formItemBytes.Length); + } + requestStream.Write(boundaryBytes, 0, boundaryBytes.Length); + } + using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) + { + if (!isoFile.FileExists(uploadOptions.FilePath)) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(FileNotFoundError, uploadOptions.Server, uploadOptions.FilePath, 0))); + return; + } + + using (FileStream fileStream = new IsolatedStorageFileStream(uploadOptions.FilePath, FileMode.Open, isoFile)) + { + string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"" + lineEnd + "Content-Type: {2}" + lineEnd + lineEnd; + string header = string.Format(headerTemplate, uploadOptions.FileKey, uploadOptions.FileName, uploadOptions.MimeType); + byte[] headerBytes = System.Text.Encoding.UTF8.GetBytes(header); + requestStream.Write(boundaryBytes, 0, boundaryBytes.Length); + requestStream.Write(headerBytes, 0, headerBytes.Length); + byte[] buffer = new byte[4096]; + int bytesRead = 0; + + while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0) + { + requestStream.Write(buffer, 0, bytesRead); + bytesSent += bytesRead; + } + } + byte[] endRequest = System.Text.Encoding.UTF8.GetBytes(lineEnd + lineStart + Boundary + lineStart + lineEnd); + requestStream.Write(endRequest, 0, endRequest.Length); + } + } + webRequest.BeginGetResponse(ReadCallback, webRequest); + } + catch (Exception) + { + Deployment.Current.Dispatcher.BeginInvoke(() => + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(ConnectionError))); + }); + } + } + + /// + /// Reads response into FileUploadResult + /// + /// + private void ReadCallback(IAsyncResult asynchronousResult) + { + try + { + HttpWebRequest webRequest = (HttpWebRequest)asynchronousResult.AsyncState; + using (HttpWebResponse response = (HttpWebResponse)webRequest.EndGetResponse(asynchronousResult)) + { + using (Stream streamResponse = response.GetResponseStream()) + { + using (StreamReader streamReader = new StreamReader(streamResponse)) + { + string responseString = streamReader.ReadToEnd(); + Deployment.Current.Dispatcher.BeginInvoke(() => + { + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, new FileUploadResult(bytesSent, (long)response.StatusCode, responseString))); + }); + } + } + } + } + catch (Exception) + { + Deployment.Current.Dispatcher.BeginInvoke(() => + { + FileTransferError transferError = new FileTransferError(ConnectionError, uploadOptions.Server, uploadOptions.FilePath, 403); + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, transferError)); + }); + } + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-cordova-wp7/blob/62d32605/templates/standalone/cordovalib/Commands/GeoLocation.cs ---------------------------------------------------------------------- diff --git a/templates/standalone/cordovalib/Commands/GeoLocation.cs b/templates/standalone/cordovalib/Commands/GeoLocation.cs new file mode 100644 index 0000000..49a4787 --- /dev/null +++ b/templates/standalone/cordovalib/Commands/GeoLocation.cs @@ -0,0 +1,34 @@ +/* + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Threading; +using System.Device.Location; + +namespace WP7CordovaClassLib.Cordova.Commands +{ + /// + /// This is a command stub, the browser provides the correct implementation. We use this to trigger the static analyzer that we require this permission + /// + public class GeoLocation + { + /* Unreachable code, by design -jm */ + private void triggerGeoInclusion() + { + new GeoCoordinateWatcher(); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-cordova-wp7/blob/62d32605/templates/standalone/cordovalib/Commands/ImageExifHelper.cs ---------------------------------------------------------------------- diff --git a/templates/standalone/cordovalib/Commands/ImageExifHelper.cs b/templates/standalone/cordovalib/Commands/ImageExifHelper.cs new file mode 100644 index 0000000..626726b --- /dev/null +++ b/templates/standalone/cordovalib/Commands/ImageExifHelper.cs @@ -0,0 +1,222 @@ +/* + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + * + * Portions of this code are based on Simon McKenzie's ExifLib + * http://www.codeproject.com/Articles/36342/ExifLib-A-Fast-Exif-Data-Extractor-for-NET-2-0 +*/ + + + +using System; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; +using System.IO; +using System.Windows.Media.Imaging; +using System.Diagnostics; + +namespace WP7CordovaClassLib.Cordova.Commands +{ + public class ImageExifOrientation + { + public const int Portrait = 1; + public const int PortraitUpsideDown = 3; + public const int LandscapeLeft = 6; + public const int LandscapeRight = 8; + } + + public class ImageExifHelper + { + + public static Stream RotateStream(Stream stream, int angle) + { + stream.Position = 0; + if (angle % 90 != 0 || angle < 0) + { + throw new ArgumentException(); + } + if (angle % 360 == 0) + { + return stream; + } + + angle = angle % 360; + + BitmapImage bitmap = new BitmapImage(); + bitmap.SetSource(stream); + WriteableBitmap wbSource = new WriteableBitmap(bitmap); + + WriteableBitmap wbTarget = null; + + int srcPixelWidth = wbSource.PixelWidth; + int srcPixelHeight = wbSource.PixelHeight; + + if (angle % 180 == 0) + { + wbTarget = new WriteableBitmap(srcPixelWidth, srcPixelHeight); + } + else + { + wbTarget = new WriteableBitmap(srcPixelHeight, srcPixelWidth); + } + + int destPixelWidth = wbTarget.PixelWidth; + int[] srcPxls = wbSource.Pixels; + int[] destPxls = wbTarget.Pixels; + + // this ugly if/else is to avoid a conditional check for every pixel + if (angle == 90) + { + for (int x = 0; x < srcPixelWidth; x++) + { + for (int y = 0; y < srcPixelHeight; y++) + { + destPxls[(srcPixelHeight - y - 1) + (x * destPixelWidth)] = srcPxls[x + y * srcPixelWidth]; + } + } + } + else if (angle == 180) + { + for (int x = 0; x < srcPixelWidth; x++) + { + for (int y = 0; y < srcPixelHeight; y++) + { + destPxls[(srcPixelWidth - x - 1) + (srcPixelHeight - y - 1) * srcPixelWidth] = srcPxls[x + y * srcPixelWidth]; + } + } + } + else if (angle == 270) + { + for (int x = 0; x < srcPixelWidth; x++) + { + for (int y = 0; y < srcPixelHeight; y++) + { + destPxls[y + (srcPixelWidth - x - 1) * destPixelWidth] = srcPxls[x + y * srcPixelWidth]; + } + } + } + + MemoryStream targetStream = new MemoryStream(); + wbTarget.SaveJpeg(targetStream, destPixelWidth, wbTarget.PixelHeight, 0, 100); + return targetStream; + } + + public static int getImageOrientationFromStream(Stream imgStream) + { + + // 0xFFD8 : jpgHeader + // 0xFFE1 : + // 0x???? : length of exif data + // 0x????, 0x???? : Chars 'E','x','i','f' + // 0x0000 : 2 empty bytes + // <== mark beginning of tags SIZE:ID:VALUE + // 0x???? : 'II' or 'MM' for Intel or Motorola ( always getting II on my WP7 devices ), determins littleEndian-ness + // 0x002A : marker value + // 0x???? : offset to the Image File Data + + // XXXX possible space before actual tag data ... we skip to mark + offset + + // 0x???? number of exif tags present + + // make sure we are at the begining + imgStream.Seek(0, SeekOrigin.Begin); + BinaryReader reader = new BinaryReader(imgStream); + + byte[] jpgHdr = reader.ReadBytes(2); // always (0xFFD8) + + byte start = reader.ReadByte(); // 0xFF + byte index = reader.ReadByte(); // 0xE1 + + while (start == 0xFF && index != 0xE1) // This never seems to happen, todo: optimize + { + // Get the data length + ushort dLen = BitConverter.ToUInt16(reader.ReadBytes(2), 0); + // skip along + reader.BaseStream.Seek(dLen - 2, SeekOrigin.Current); + start = reader.ReadByte(); + index = reader.ReadByte(); + } + + // It's only success if we found the 0xFFE1 marker + if (start != 0xFF || index != 0xE1) + { + // throw new Exception("Could not find Exif data block"); + Debug.WriteLine("Did not find EXIF data"); + return 0; + } + + // read 2 byte length of EXIF data + ushort exifLen = BitConverter.ToUInt16(reader.ReadBytes(2), 0); + String exif = ""; // build the string + for (var n = 0; n < 4; n++) + { + exif += reader.ReadChar(); + } + if (exif != "Exif") + { + // did not find exif data ... + Debug.WriteLine("Did not find EXIF data"); + return 0; + } + + // read 2 empty bytes + //ushort emptyBytes = BitConverter.ToUInt16(reader.ReadBytes(2), 0); + reader.ReadBytes(2); + + long headerMark = reader.BaseStream.Position; // where are we now <== + + //bool isLEndian = (reader.ReadChar() + "" + reader.ReadChar()) == "II"; + reader.ReadBytes(2); // 'II' or 'MM', but we don't care + + if (0x002A != BitConverter.ToUInt16(reader.ReadBytes(2), 0)) + { + Debug.WriteLine("Error in data != 0x002A"); + return 0; + } + + // Get the offset to the IFD (image file directory) + ushort imgOffset = BitConverter.ToUInt16(reader.ReadBytes(2), 0); + + imgStream.Position = headerMark + imgOffset; + ushort tagCount = BitConverter.ToUInt16(reader.ReadBytes(2), 0); + for (ushort x = 0; x < tagCount; x++) + { + // Orientation = 0x112, aka 274 + if (0x112 == BitConverter.ToUInt16(reader.ReadBytes(2), 0)) + { + ushort dType = BitConverter.ToUInt16(reader.ReadBytes(2), 0); + // don't care .. + uint comps = reader.ReadUInt32(); + byte[] tagData = reader.ReadBytes(4); + int orientation = (int)tagData[0]; + Debug.WriteLine("orientation = " + orientation.ToString()); + return orientation; + // 6 means rotate clockwise 90 deg + // 8 means rotate counter-clockwise 90 deg + // 1 means all is good + // 3 means flip vertical + } + // skip to the next item, 12 bytes each + reader.BaseStream.Seek(10, SeekOrigin.Current); + } + return 0; + } + + } +}