diff options
Diffstat (limited to 'plugins/cordova-plugin-file/src')
26 files changed, 0 insertions, 10816 deletions
diff --git a/plugins/cordova-plugin-file/src/android/AssetFilesystem.java b/plugins/cordova-plugin-file/src/android/AssetFilesystem.java deleted file mode 100644 index f501b279..00000000 --- a/plugins/cordova-plugin-file/src/android/AssetFilesystem.java +++ /dev/null @@ -1,283 +0,0 @@ -/* - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you 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. - */ -package org.apache.cordova.file; - -import android.content.res.AssetManager; -import android.net.Uri; -import android.util.Log; - -import org.apache.cordova.CordovaResourceApi; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.util.HashMap; -import java.util.Map; - -public class AssetFilesystem extends Filesystem { - - private final AssetManager assetManager; - - // A custom gradle hook creates the cdvasset.manifest file, which speeds up asset listing a tonne. - // See: http://stackoverflow.com/questions/16911558/android-assetmanager-list-incredibly-slow - private static Object listCacheLock = new Object(); - private static boolean listCacheFromFile; - private static Map<String, String[]> listCache; - private static Map<String, Long> lengthCache; - - private void lazyInitCaches() { - synchronized (listCacheLock) { - if (listCache == null) { - ObjectInputStream ois = null; - try { - ois = new ObjectInputStream(assetManager.open("cdvasset.manifest")); - listCache = (Map<String, String[]>) ois.readObject(); - lengthCache = (Map<String, Long>) ois.readObject(); - listCacheFromFile = true; - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } catch (IOException e) { - // Asset manifest won't exist if the gradle hook isn't set up correctly. - } finally { - if (ois != null) { - try { - ois.close(); - } catch (IOException e) { - } - } - } - if (listCache == null) { - Log.w("AssetFilesystem", "Asset manifest not found. Recursive copies and directory listing will be slow."); - listCache = new HashMap<String, String[]>(); - } - } - } - } - - private String[] listAssets(String assetPath) throws IOException { - if (assetPath.startsWith("/")) { - assetPath = assetPath.substring(1); - } - lazyInitCaches(); - String[] ret = listCache.get(assetPath); - if (ret == null) { - if (listCacheFromFile) { - ret = new String[0]; - } else { - ret = assetManager.list(assetPath); - listCache.put(assetPath, ret); - } - } - return ret; - } - - private long getAssetSize(String assetPath) throws FileNotFoundException { - if (assetPath.startsWith("/")) { - assetPath = assetPath.substring(1); - } - lazyInitCaches(); - if (lengthCache != null) { - Long ret = lengthCache.get(assetPath); - if (ret == null) { - throw new FileNotFoundException("Asset not found: " + assetPath); - } - return ret; - } - CordovaResourceApi.OpenForReadResult offr = null; - try { - offr = resourceApi.openForRead(nativeUriForFullPath(assetPath)); - long length = offr.length; - if (length < 0) { - // available() doesn't always yield the file size, but for assets it does. - length = offr.inputStream.available(); - } - return length; - } catch (IOException e) { - throw new FileNotFoundException("File not found: " + assetPath); - } finally { - if (offr != null) { - try { - offr.inputStream.close(); - } catch (IOException e) { - } - } - } - } - - public AssetFilesystem(AssetManager assetManager, CordovaResourceApi resourceApi) { - super(Uri.parse("file:///android_asset/"), "assets", resourceApi); - this.assetManager = assetManager; - } - - @Override - public Uri toNativeUri(LocalFilesystemURL inputURL) { - return nativeUriForFullPath(inputURL.path); - } - - @Override - public LocalFilesystemURL toLocalUri(Uri inputURL) { - if (!"file".equals(inputURL.getScheme())) { - return null; - } - File f = new File(inputURL.getPath()); - // Removes and duplicate /s (e.g. file:///a//b/c) - Uri resolvedUri = Uri.fromFile(f); - String rootUriNoTrailingSlash = rootUri.getEncodedPath(); - rootUriNoTrailingSlash = rootUriNoTrailingSlash.substring(0, rootUriNoTrailingSlash.length() - 1); - if (!resolvedUri.getEncodedPath().startsWith(rootUriNoTrailingSlash)) { - return null; - } - String subPath = resolvedUri.getEncodedPath().substring(rootUriNoTrailingSlash.length()); - // Strip leading slash - if (!subPath.isEmpty()) { - subPath = subPath.substring(1); - } - Uri.Builder b = new Uri.Builder() - .scheme(LocalFilesystemURL.FILESYSTEM_PROTOCOL) - .authority("localhost") - .path(name); - if (!subPath.isEmpty()) { - b.appendEncodedPath(subPath); - } - if (isDirectory(subPath) || inputURL.getPath().endsWith("/")) { - // Add trailing / for directories. - b.appendEncodedPath(""); - } - return LocalFilesystemURL.parse(b.build()); - } - - private boolean isDirectory(String assetPath) { - try { - return listAssets(assetPath).length != 0; - } catch (IOException e) { - return false; - } - } - - @Override - public LocalFilesystemURL[] listChildren(LocalFilesystemURL inputURL) throws FileNotFoundException { - String pathNoSlashes = inputURL.path.substring(1); - if (pathNoSlashes.endsWith("/")) { - pathNoSlashes = pathNoSlashes.substring(0, pathNoSlashes.length() - 1); - } - - String[] files; - try { - files = listAssets(pathNoSlashes); - } catch (IOException e) { - throw new FileNotFoundException(); - } - - LocalFilesystemURL[] entries = new LocalFilesystemURL[files.length]; - for (int i = 0; i < files.length; ++i) { - entries[i] = localUrlforFullPath(new File(inputURL.path, files[i]).getPath()); - } - return entries; - } - - @Override - public JSONObject getFileForLocalURL(LocalFilesystemURL inputURL, - String path, JSONObject options, boolean directory) - throws FileExistsException, IOException, TypeMismatchException, EncodingException, JSONException { - if (options != null && options.optBoolean("create")) { - throw new UnsupportedOperationException("Assets are read-only"); - } - - // Check whether the supplied path is absolute or relative - if (directory && !path.endsWith("/")) { - path += "/"; - } - - LocalFilesystemURL requestedURL; - if (path.startsWith("/")) { - requestedURL = localUrlforFullPath(normalizePath(path)); - } else { - requestedURL = localUrlforFullPath(normalizePath(inputURL.path + "/" + path)); - } - - // Throws a FileNotFoundException if it doesn't exist. - getFileMetadataForLocalURL(requestedURL); - - boolean isDir = isDirectory(requestedURL.path); - if (directory && !isDir) { - throw new TypeMismatchException("path doesn't exist or is file"); - } else if (!directory && isDir) { - throw new TypeMismatchException("path doesn't exist or is directory"); - } - - // Return the directory - return makeEntryForURL(requestedURL); - } - - @Override - public JSONObject getFileMetadataForLocalURL(LocalFilesystemURL inputURL) throws FileNotFoundException { - JSONObject metadata = new JSONObject(); - long size = inputURL.isDirectory ? 0 : getAssetSize(inputURL.path); - try { - metadata.put("size", size); - metadata.put("type", inputURL.isDirectory ? "text/directory" : resourceApi.getMimeType(toNativeUri(inputURL))); - metadata.put("name", new File(inputURL.path).getName()); - metadata.put("fullPath", inputURL.path); - metadata.put("lastModifiedDate", 0); - } catch (JSONException e) { - return null; - } - return metadata; - } - - @Override - public boolean canRemoveFileAtLocalURL(LocalFilesystemURL inputURL) { - return false; - } - - @Override - long writeToFileAtURL(LocalFilesystemURL inputURL, String data, int offset, boolean isBinary) throws NoModificationAllowedException, IOException { - throw new NoModificationAllowedException("Assets are read-only"); - } - - @Override - long truncateFileAtURL(LocalFilesystemURL inputURL, long size) throws IOException, NoModificationAllowedException { - throw new NoModificationAllowedException("Assets are read-only"); - } - - @Override - String filesystemPathForURL(LocalFilesystemURL url) { - return null; - } - - @Override - LocalFilesystemURL URLforFilesystemPath(String path) { - return null; - } - - @Override - boolean removeFileAtLocalURL(LocalFilesystemURL inputURL) throws InvalidModificationException, NoModificationAllowedException { - throw new NoModificationAllowedException("Assets are read-only"); - } - - @Override - boolean recursiveRemoveFileAtLocalURL(LocalFilesystemURL inputURL) throws NoModificationAllowedException { - throw new NoModificationAllowedException("Assets are read-only"); - } - -} diff --git a/plugins/cordova-plugin-file/src/android/ContentFilesystem.java b/plugins/cordova-plugin-file/src/android/ContentFilesystem.java deleted file mode 100644 index 883e7cf5..00000000 --- a/plugins/cordova-plugin-file/src/android/ContentFilesystem.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you 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. - */ -package org.apache.cordova.file; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.OutputStream; - -import org.apache.cordova.CordovaResourceApi; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import android.content.ContentResolver; -import android.content.Context; -import android.database.Cursor; -import android.net.Uri; -import android.provider.MediaStore; -import android.provider.OpenableColumns; - -public class ContentFilesystem extends Filesystem { - - private final Context context; - - public ContentFilesystem(Context context, CordovaResourceApi resourceApi) { - super(Uri.parse("content://"), "content", resourceApi); - this.context = context; - } - - @Override - public Uri toNativeUri(LocalFilesystemURL inputURL) { - String authorityAndPath = inputURL.uri.getEncodedPath().substring(this.name.length() + 2); - if (authorityAndPath.length() < 2) { - return null; - } - String ret = "content://" + authorityAndPath; - String query = inputURL.uri.getEncodedQuery(); - if (query != null) { - ret += '?' + query; - } - String frag = inputURL.uri.getEncodedFragment(); - if (frag != null) { - ret += '#' + frag; - } - return Uri.parse(ret); - } - - @Override - public LocalFilesystemURL toLocalUri(Uri inputURL) { - if (!"content".equals(inputURL.getScheme())) { - return null; - } - String subPath = inputURL.getEncodedPath(); - if (subPath.length() > 0) { - subPath = subPath.substring(1); - } - Uri.Builder b = new Uri.Builder() - .scheme(LocalFilesystemURL.FILESYSTEM_PROTOCOL) - .authority("localhost") - .path(name) - .appendPath(inputURL.getAuthority()); - if (subPath.length() > 0) { - b.appendEncodedPath(subPath); - } - Uri localUri = b.encodedQuery(inputURL.getEncodedQuery()) - .encodedFragment(inputURL.getEncodedFragment()) - .build(); - return LocalFilesystemURL.parse(localUri); - } - - @Override - public JSONObject getFileForLocalURL(LocalFilesystemURL inputURL, - String fileName, JSONObject options, boolean directory) throws IOException, TypeMismatchException, JSONException { - throw new UnsupportedOperationException("getFile() not supported for content:. Use resolveLocalFileSystemURL instead."); - } - - @Override - public boolean removeFileAtLocalURL(LocalFilesystemURL inputURL) - throws NoModificationAllowedException { - Uri contentUri = toNativeUri(inputURL); - try { - context.getContentResolver().delete(contentUri, null, null); - } catch (UnsupportedOperationException t) { - // Was seeing this on the File mobile-spec tests on 4.0.3 x86 emulator. - // The ContentResolver applies only when the file was registered in the - // first case, which is generally only the case with images. - throw new NoModificationAllowedException("Deleting not supported for content uri: " + contentUri); - } - return true; - } - - @Override - public boolean recursiveRemoveFileAtLocalURL(LocalFilesystemURL inputURL) - throws NoModificationAllowedException { - throw new NoModificationAllowedException("Cannot remove content url"); - } - - @Override - public LocalFilesystemURL[] listChildren(LocalFilesystemURL inputURL) throws FileNotFoundException { - throw new UnsupportedOperationException("readEntriesAtLocalURL() not supported for content:. Use resolveLocalFileSystemURL instead."); - } - - @Override - public JSONObject getFileMetadataForLocalURL(LocalFilesystemURL inputURL) throws FileNotFoundException { - long size = -1; - long lastModified = 0; - Uri nativeUri = toNativeUri(inputURL); - String mimeType = resourceApi.getMimeType(nativeUri); - Cursor cursor = openCursorForURL(nativeUri); - try { - if (cursor != null && cursor.moveToFirst()) { - size = resourceSizeForCursor(cursor); - lastModified = lastModifiedDateForCursor(cursor); - } else { - // Some content providers don't support cursors at all! - CordovaResourceApi.OpenForReadResult offr = resourceApi.openForRead(nativeUri); - size = offr.length; - } - } catch (IOException e) { - throw new FileNotFoundException(); - } finally { - if (cursor != null) - cursor.close(); - } - - JSONObject metadata = new JSONObject(); - try { - metadata.put("size", size); - metadata.put("type", mimeType); - metadata.put("name", name); - metadata.put("fullPath", inputURL.path); - metadata.put("lastModifiedDate", lastModified); - } catch (JSONException e) { - return null; - } - return metadata; - } - - @Override - public long writeToFileAtURL(LocalFilesystemURL inputURL, String data, - int offset, boolean isBinary) throws NoModificationAllowedException { - throw new NoModificationAllowedException("Couldn't write to file given its content URI"); - } - @Override - public long truncateFileAtURL(LocalFilesystemURL inputURL, long size) - throws NoModificationAllowedException { - throw new NoModificationAllowedException("Couldn't truncate file given its content URI"); - } - - protected Cursor openCursorForURL(Uri nativeUri) { - ContentResolver contentResolver = context.getContentResolver(); - try { - return contentResolver.query(nativeUri, null, null, null, null); - } catch (UnsupportedOperationException e) { - return null; - } - } - - private Long resourceSizeForCursor(Cursor cursor) { - int columnIndex = cursor.getColumnIndex(OpenableColumns.SIZE); - if (columnIndex != -1) { - String sizeStr = cursor.getString(columnIndex); - if (sizeStr != null) { - return Long.parseLong(sizeStr); - } - } - return null; - } - - protected Long lastModifiedDateForCursor(Cursor cursor) { - final String[] LOCAL_FILE_PROJECTION = { MediaStore.MediaColumns.DATE_MODIFIED }; - int columnIndex = cursor.getColumnIndex(LOCAL_FILE_PROJECTION[0]); - if (columnIndex != -1) { - String dateStr = cursor.getString(columnIndex); - if (dateStr != null) { - return Long.parseLong(dateStr); - } - } - return null; - } - - @Override - public String filesystemPathForURL(LocalFilesystemURL url) { - File f = resourceApi.mapUriToFile(toNativeUri(url)); - return f == null ? null : f.getAbsolutePath(); - } - - @Override - public LocalFilesystemURL URLforFilesystemPath(String path) { - // Returns null as we don't support reverse mapping back to content:// URLs - return null; - } - - @Override - public boolean canRemoveFileAtLocalURL(LocalFilesystemURL inputURL) { - return true; - } -} diff --git a/plugins/cordova-plugin-file/src/android/DirectoryManager.java b/plugins/cordova-plugin-file/src/android/DirectoryManager.java deleted file mode 100644 index bcc005b2..00000000 --- a/plugins/cordova-plugin-file/src/android/DirectoryManager.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you 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. -*/ -package org.apache.cordova.file; - -import android.os.Environment; -import android.os.StatFs; - -import java.io.File; - -/** - * This class provides file directory utilities. - * All file operations are performed on the SD card. - * - * It is used by the FileUtils class. - */ -public class DirectoryManager { - - @SuppressWarnings("unused") - private static final String LOG_TAG = "DirectoryManager"; - - /** - * Determine if a file or directory exists. - * @param name The name of the file to check. - * @return T=exists, F=not found - */ - public static boolean testFileExists(String name) { - boolean status; - - // If SD card exists - if ((testSaveLocationExists()) && (!name.equals(""))) { - File path = Environment.getExternalStorageDirectory(); - File newPath = constructFilePaths(path.toString(), name); - status = newPath.exists(); - } - // If no SD card - else { - status = false; - } - return status; - } - - /** - * Get the free disk space - * - * @return Size in KB or -1 if not available - */ - public static long getFreeDiskSpace(boolean checkInternal) { - String status = Environment.getExternalStorageState(); - long freeSpace = 0; - - // If SD card exists - if (status.equals(Environment.MEDIA_MOUNTED)) { - freeSpace = freeSpaceCalculation(Environment.getExternalStorageDirectory().getPath()); - } - else if (checkInternal) { - freeSpace = freeSpaceCalculation("/"); - } - // If no SD card and we haven't been asked to check the internal directory then return -1 - else { - return -1; - } - - return freeSpace; - } - - /** - * Given a path return the number of free KB - * - * @param path to the file system - * @return free space in KB - */ - private static long freeSpaceCalculation(String path) { - StatFs stat = new StatFs(path); - long blockSize = stat.getBlockSize(); - long availableBlocks = stat.getAvailableBlocks(); - return availableBlocks * blockSize / 1024; - } - - /** - * Determine if SD card exists. - * - * @return T=exists, F=not found - */ - public static boolean testSaveLocationExists() { - String sDCardStatus = Environment.getExternalStorageState(); - boolean status; - - // If SD card is mounted - if (sDCardStatus.equals(Environment.MEDIA_MOUNTED)) { - status = true; - } - - // If no SD card - else { - status = false; - } - return status; - } - - /** - * Create a new file object from two file paths. - * - * @param file1 Base file path - * @param file2 Remaining file path - * @return File object - */ - private static File constructFilePaths (String file1, String file2) { - File newPath; - if (file2.startsWith(file1)) { - newPath = new File(file2); - } - else { - newPath = new File(file1 + "/" + file2); - } - return newPath; - } -} diff --git a/plugins/cordova-plugin-file/src/android/EncodingException.java b/plugins/cordova-plugin-file/src/android/EncodingException.java deleted file mode 100644 index e9e1653b..00000000 --- a/plugins/cordova-plugin-file/src/android/EncodingException.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you 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. -*/ - -package org.apache.cordova.file; - -@SuppressWarnings("serial") -public class EncodingException extends Exception { - - public EncodingException(String message) { - super(message); - } - -} diff --git a/plugins/cordova-plugin-file/src/android/FileExistsException.java b/plugins/cordova-plugin-file/src/android/FileExistsException.java deleted file mode 100644 index 5c4d83dc..00000000 --- a/plugins/cordova-plugin-file/src/android/FileExistsException.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you 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. -*/ - -package org.apache.cordova.file; - -@SuppressWarnings("serial") -public class FileExistsException extends Exception { - - public FileExistsException(String msg) { - super(msg); - } - -} diff --git a/plugins/cordova-plugin-file/src/android/FileUtils.java b/plugins/cordova-plugin-file/src/android/FileUtils.java deleted file mode 100644 index f57d26b3..00000000 --- a/plugins/cordova-plugin-file/src/android/FileUtils.java +++ /dev/null @@ -1,1027 +0,0 @@ -/* - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you 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. - */ -package org.apache.cordova.file; - -import android.app.Activity; -import android.content.Context; -import android.net.Uri; -import android.os.Environment; -import android.util.Base64; -import android.util.Log; - -import org.apache.cordova.CallbackContext; -import org.apache.cordova.CordovaInterface; -import org.apache.cordova.CordovaPlugin; -import org.apache.cordova.CordovaWebView; -import org.apache.cordova.PluginResult; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; - -/** - * This class provides file and directory services to JavaScript. - */ -public class FileUtils extends CordovaPlugin { - private static final String LOG_TAG = "FileUtils"; - - public static int NOT_FOUND_ERR = 1; - public static int SECURITY_ERR = 2; - public static int ABORT_ERR = 3; - - public static int NOT_READABLE_ERR = 4; - public static int ENCODING_ERR = 5; - public static int NO_MODIFICATION_ALLOWED_ERR = 6; - public static int INVALID_STATE_ERR = 7; - public static int SYNTAX_ERR = 8; - public static int INVALID_MODIFICATION_ERR = 9; - public static int QUOTA_EXCEEDED_ERR = 10; - public static int TYPE_MISMATCH_ERR = 11; - public static int PATH_EXISTS_ERR = 12; - - public static int UNKNOWN_ERR = 1000; - - private boolean configured = false; - - // This field exists only to support getEntry, below, which has been deprecated - private static FileUtils filePlugin; - - private interface FileOp { - void run(JSONArray args) throws Exception; - } - - private ArrayList<Filesystem> filesystems; - - public void registerFilesystem(Filesystem fs) { - if (fs != null && filesystemForName(fs.name)== null) { - this.filesystems.add(fs); - } - } - - private Filesystem filesystemForName(String name) { - for (Filesystem fs:filesystems) { - if (fs != null && fs.name != null && fs.name.equals(name)) { - return fs; - } - } - return null; - } - - protected String[] getExtraFileSystemsPreference(Activity activity) { - String fileSystemsStr = activity.getIntent().getStringExtra("androidextrafilesystems"); - if (fileSystemsStr == null) { - fileSystemsStr = "files,files-external,documents,sdcard,cache,cache-external,root"; - } - return fileSystemsStr.split(","); - } - - protected void registerExtraFileSystems(String[] filesystems, HashMap<String, String> availableFileSystems) { - HashSet<String> installedFileSystems = new HashSet<String>(); - - /* Register filesystems in order */ - for (String fsName : filesystems) { - if (!installedFileSystems.contains(fsName)) { - String fsRoot = availableFileSystems.get(fsName); - if (fsRoot != null) { - File newRoot = new File(fsRoot); - if (newRoot.mkdirs() || newRoot.isDirectory()) { - registerFilesystem(new LocalFilesystem(fsName, webView.getContext(), webView.getResourceApi(), newRoot)); - installedFileSystems.add(fsName); - } else { - Log.d(LOG_TAG, "Unable to create root dir for filesystem \"" + fsName + "\", skipping"); - } - } else { - Log.d(LOG_TAG, "Unrecognized extra filesystem identifier: " + fsName); - } - } - } - } - - protected HashMap<String, String> getAvailableFileSystems(Activity activity) { - Context context = activity.getApplicationContext(); - HashMap<String, String> availableFileSystems = new HashMap<String,String>(); - - availableFileSystems.put("files", context.getFilesDir().getAbsolutePath()); - availableFileSystems.put("documents", new File(context.getFilesDir(), "Documents").getAbsolutePath()); - availableFileSystems.put("cache", context.getCacheDir().getAbsolutePath()); - availableFileSystems.put("root", "/"); - if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - try { - availableFileSystems.put("files-external", context.getExternalFilesDir(null).getAbsolutePath()); - availableFileSystems.put("sdcard", Environment.getExternalStorageDirectory().getAbsolutePath()); - availableFileSystems.put("cache-external", context.getExternalCacheDir().getAbsolutePath()); - } - catch(NullPointerException e) { - Log.d(LOG_TAG, "External storage unavailable, check to see if USB Mass Storage Mode is on"); - } - } - - return availableFileSystems; - } - - @Override - public void initialize(CordovaInterface cordova, CordovaWebView webView) { - super.initialize(cordova, webView); - this.filesystems = new ArrayList<Filesystem>(); - - String tempRoot = null; - String persistentRoot = null; - - Activity activity = cordova.getActivity(); - String packageName = activity.getPackageName(); - - String location = activity.getIntent().getStringExtra("androidpersistentfilelocation"); - if (location == null) { - location = "compatibility"; - } - tempRoot = activity.getCacheDir().getAbsolutePath(); - if ("internal".equalsIgnoreCase(location)) { - persistentRoot = activity.getFilesDir().getAbsolutePath() + "/files/"; - this.configured = true; - } else if ("compatibility".equalsIgnoreCase(location)) { - /* - * Fall-back to compatibility mode -- this is the logic implemented in - * earlier versions of this plugin, and should be maintained here so - * that apps which were originally deployed with older versions of the - * plugin can continue to provide access to files stored under those - * versions. - */ - if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - persistentRoot = Environment.getExternalStorageDirectory().getAbsolutePath(); - tempRoot = Environment.getExternalStorageDirectory().getAbsolutePath() + - "/Android/data/" + packageName + "/cache/"; - } else { - persistentRoot = "/data/data/" + packageName; - } - this.configured = true; - } - - if (this.configured) { - // Create the directories if they don't exist. - File tmpRootFile = new File(tempRoot); - File persistentRootFile = new File(persistentRoot); - tmpRootFile.mkdirs(); - persistentRootFile.mkdirs(); - - // Register initial filesystems - // Note: The temporary and persistent filesystems need to be the first two - // registered, so that they will match window.TEMPORARY and window.PERSISTENT, - // per spec. - this.registerFilesystem(new LocalFilesystem("temporary", webView.getContext(), webView.getResourceApi(), tmpRootFile)); - this.registerFilesystem(new LocalFilesystem("persistent", webView.getContext(), webView.getResourceApi(), persistentRootFile)); - this.registerFilesystem(new ContentFilesystem(webView.getContext(), webView.getResourceApi())); - this.registerFilesystem(new AssetFilesystem(webView.getContext().getAssets(), webView.getResourceApi())); - - registerExtraFileSystems(getExtraFileSystemsPreference(activity), getAvailableFileSystems(activity)); - - // Initialize static plugin reference for deprecated getEntry method - if (filePlugin == null) { - FileUtils.filePlugin = this; - } - } else { - Log.e(LOG_TAG, "File plugin configuration error: Please set AndroidPersistentFileLocation in config.xml to one of \"internal\" (for new applications) or \"compatibility\" (for compatibility with previous versions)"); - activity.finish(); - } - } - - public static FileUtils getFilePlugin() { - return filePlugin; - } - - private Filesystem filesystemForURL(LocalFilesystemURL localURL) { - if (localURL == null) return null; - return filesystemForName(localURL.fsName); - } - - @Override - public Uri remapUri(Uri uri) { - // Remap only cdvfile: URLs (not content:). - if (!LocalFilesystemURL.FILESYSTEM_PROTOCOL.equals(uri.getScheme())) { - return null; - } - try { - LocalFilesystemURL inputURL = LocalFilesystemURL.parse(uri); - Filesystem fs = this.filesystemForURL(inputURL); - if (fs == null) { - return null; - } - String path = fs.filesystemPathForURL(inputURL); - if (path != null) { - return Uri.parse("file://" + fs.filesystemPathForURL(inputURL)); - } - return null; - } catch (IllegalArgumentException e) { - return null; - } - } - - public boolean execute(String action, final String rawArgs, final CallbackContext callbackContext) { - if (!configured) { - callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, "File plugin is not configured. Please see the README.md file for details on how to update config.xml")); - return true; - } - if (action.equals("testSaveLocationExists")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) { - boolean b = DirectoryManager.testSaveLocationExists(); - callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, b)); - } - }, rawArgs, callbackContext); - } - else if (action.equals("getFreeDiskSpace")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) { - long l = DirectoryManager.getFreeDiskSpace(false); - callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, l)); - } - }, rawArgs, callbackContext); - } - else if (action.equals("testFileExists")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws JSONException { - String fname=args.getString(0); - boolean b = DirectoryManager.testFileExists(fname); - callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, b)); - } - }, rawArgs, callbackContext); - } - else if (action.equals("testDirectoryExists")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws JSONException { - String fname=args.getString(0); - boolean b = DirectoryManager.testFileExists(fname); - callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, b)); - } - }, rawArgs, callbackContext); - } - else if (action.equals("readAsText")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws JSONException, MalformedURLException { - String encoding = args.getString(1); - int start = args.getInt(2); - int end = args.getInt(3); - String fname=args.getString(0); - readFileAs(fname, start, end, callbackContext, encoding, PluginResult.MESSAGE_TYPE_STRING); - } - }, rawArgs, callbackContext); - } - else if (action.equals("readAsDataURL")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws JSONException, MalformedURLException { - int start = args.getInt(1); - int end = args.getInt(2); - String fname=args.getString(0); - readFileAs(fname, start, end, callbackContext, null, -1); - } - }, rawArgs, callbackContext); - } - else if (action.equals("readAsArrayBuffer")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws JSONException, MalformedURLException { - int start = args.getInt(1); - int end = args.getInt(2); - String fname=args.getString(0); - readFileAs(fname, start, end, callbackContext, null, PluginResult.MESSAGE_TYPE_ARRAYBUFFER); - } - }, rawArgs, callbackContext); - } - else if (action.equals("readAsBinaryString")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws JSONException, MalformedURLException { - int start = args.getInt(1); - int end = args.getInt(2); - String fname=args.getString(0); - readFileAs(fname, start, end, callbackContext, null, PluginResult.MESSAGE_TYPE_BINARYSTRING); - } - }, rawArgs, callbackContext); - } - else if (action.equals("write")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws JSONException, FileNotFoundException, IOException, NoModificationAllowedException { - String fname=args.getString(0); - String data=args.getString(1); - int offset=args.getInt(2); - Boolean isBinary=args.getBoolean(3); - long fileSize = write(fname, data, offset, isBinary); - callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, fileSize)); - } - }, rawArgs, callbackContext); - } - else if (action.equals("truncate")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws JSONException, FileNotFoundException, IOException, NoModificationAllowedException { - String fname=args.getString(0); - int offset=args.getInt(1); - long fileSize = truncateFile(fname, offset); - callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, fileSize)); - } - }, rawArgs, callbackContext); - } - else if (action.equals("requestAllFileSystems")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws IOException, JSONException { - callbackContext.success(requestAllFileSystems()); - } - }, rawArgs, callbackContext); - } else if (action.equals("requestAllPaths")) { - cordova.getThreadPool().execute( - new Runnable() { - public void run() { - try { - callbackContext.success(requestAllPaths()); - } catch (JSONException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - } - ); - } else if (action.equals("requestFileSystem")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws IOException, JSONException { - int fstype=args.getInt(0); - long size = args.optLong(1); - if (size != 0 && size > (DirectoryManager.getFreeDiskSpace(true) * 1024)) { - callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, FileUtils.QUOTA_EXCEEDED_ERR)); - } else { - JSONObject obj = requestFileSystem(fstype); - callbackContext.success(obj); - } - } - }, rawArgs, callbackContext); - } - else if (action.equals("resolveLocalFileSystemURI")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws IOException, JSONException { - String fname=args.getString(0); - JSONObject obj = resolveLocalFileSystemURI(fname); - callbackContext.success(obj); - } - }, rawArgs, callbackContext); - } - else if (action.equals("getFileMetadata")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws FileNotFoundException, JSONException, MalformedURLException { - String fname=args.getString(0); - JSONObject obj = getFileMetadata(fname); - callbackContext.success(obj); - } - }, rawArgs, callbackContext); - } - else if (action.equals("getParent")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws JSONException, IOException { - String fname=args.getString(0); - JSONObject obj = getParent(fname); - callbackContext.success(obj); - } - }, rawArgs, callbackContext); - } - else if (action.equals("getDirectory")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws FileExistsException, IOException, TypeMismatchException, EncodingException, JSONException { - String dirname=args.getString(0); - String path=args.getString(1); - JSONObject obj = getFile(dirname, path, args.optJSONObject(2), true); - callbackContext.success(obj); - } - }, rawArgs, callbackContext); - } - else if (action.equals("getFile")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws FileExistsException, IOException, TypeMismatchException, EncodingException, JSONException { - String dirname=args.getString(0); - String path=args.getString(1); - JSONObject obj = getFile(dirname, path, args.optJSONObject(2), false); - callbackContext.success(obj); - } - }, rawArgs, callbackContext); - } - else if (action.equals("remove")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws JSONException, NoModificationAllowedException, InvalidModificationException, MalformedURLException { - String fname=args.getString(0); - boolean success = remove(fname); - if (success) { - callbackContext.success(); - } else { - callbackContext.error(FileUtils.NO_MODIFICATION_ALLOWED_ERR); - } - } - }, rawArgs, callbackContext); - } - else if (action.equals("removeRecursively")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws JSONException, FileExistsException, MalformedURLException, NoModificationAllowedException { - String fname=args.getString(0); - boolean success = removeRecursively(fname); - if (success) { - callbackContext.success(); - } else { - callbackContext.error(FileUtils.NO_MODIFICATION_ALLOWED_ERR); - } - } - }, rawArgs, callbackContext); - } - else if (action.equals("moveTo")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws JSONException, NoModificationAllowedException, IOException, InvalidModificationException, EncodingException, FileExistsException { - String fname=args.getString(0); - String newParent=args.getString(1); - String newName=args.getString(2); - JSONObject entry = transferTo(fname, newParent, newName, true); - callbackContext.success(entry); - } - }, rawArgs, callbackContext); - } - else if (action.equals("copyTo")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws JSONException, NoModificationAllowedException, IOException, InvalidModificationException, EncodingException, FileExistsException { - String fname=args.getString(0); - String newParent=args.getString(1); - String newName=args.getString(2); - JSONObject entry = transferTo(fname, newParent, newName, false); - callbackContext.success(entry); - } - }, rawArgs, callbackContext); - } - else if (action.equals("readEntries")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws FileNotFoundException, JSONException, MalformedURLException { - String fname=args.getString(0); - JSONArray entries = readEntries(fname); - callbackContext.success(entries); - } - }, rawArgs, callbackContext); - } - else if (action.equals("_getLocalFilesystemPath")) { - // Internal method for testing: Get the on-disk location of a local filesystem url. - // [Currently used for testing file-transfer] - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws FileNotFoundException, JSONException, MalformedURLException { - String localURLstr = args.getString(0); - String fname = filesystemPathForURL(localURLstr); - callbackContext.success(fname); - } - }, rawArgs, callbackContext); - } - else { - return false; - } - return true; - } - - public LocalFilesystemURL resolveNativeUri(Uri nativeUri) { - LocalFilesystemURL localURL = null; - - // Try all installed filesystems. Return the best matching URL - // (determined by the shortest resulting URL) - for (Filesystem fs : filesystems) { - LocalFilesystemURL url = fs.toLocalUri(nativeUri); - if (url != null) { - // A shorter fullPath implies that the filesystem is a better - // match for the local path than the previous best. - if (localURL == null || (url.uri.toString().length() < localURL.toString().length())) { - localURL = url; - } - } - } - return localURL; - } - - /* - * These two native-only methods can be used by other plugins to translate between - * device file system paths and URLs. By design, there is no direct JavaScript - * interface to these methods. - */ - - public String filesystemPathForURL(String localURLstr) throws MalformedURLException { - try { - LocalFilesystemURL inputURL = LocalFilesystemURL.parse(localURLstr); - Filesystem fs = this.filesystemForURL(inputURL); - if (fs == null) { - throw new MalformedURLException("No installed handlers for this URL"); - } - return fs.filesystemPathForURL(inputURL); - } catch (IllegalArgumentException e) { - throw new MalformedURLException("Unrecognized filesystem URL"); - } - } - - public LocalFilesystemURL filesystemURLforLocalPath(String localPath) { - LocalFilesystemURL localURL = null; - int shortestFullPath = 0; - - // Try all installed filesystems. Return the best matching URL - // (determined by the shortest resulting URL) - for (Filesystem fs: filesystems) { - LocalFilesystemURL url = fs.URLforFilesystemPath(localPath); - if (url != null) { - // A shorter fullPath implies that the filesystem is a better - // match for the local path than the previous best. - if (localURL == null || (url.path.length() < shortestFullPath)) { - localURL = url; - shortestFullPath = url.path.length(); - } - } - } - return localURL; - } - - - /* helper to execute functions async and handle the result codes - * - */ - private void threadhelper(final FileOp f, final String rawArgs, final CallbackContext callbackContext){ - cordova.getThreadPool().execute(new Runnable() { - public void run() { - try { - JSONArray args = new JSONArray(rawArgs); - f.run(args); - } catch ( Exception e) { - if( e instanceof EncodingException){ - callbackContext.error(FileUtils.ENCODING_ERR); - } else if(e instanceof FileNotFoundException) { - callbackContext.error(FileUtils.NOT_FOUND_ERR); - } else if(e instanceof FileExistsException) { - callbackContext.error(FileUtils.PATH_EXISTS_ERR); - } else if(e instanceof NoModificationAllowedException ) { - callbackContext.error(FileUtils.NO_MODIFICATION_ALLOWED_ERR); - } else if(e instanceof InvalidModificationException ) { - callbackContext.error(FileUtils.INVALID_MODIFICATION_ERR); - } else if(e instanceof MalformedURLException ) { - callbackContext.error(FileUtils.ENCODING_ERR); - } else if(e instanceof IOException ) { - callbackContext.error(FileUtils.INVALID_MODIFICATION_ERR); - } else if(e instanceof EncodingException ) { - callbackContext.error(FileUtils.ENCODING_ERR); - } else if(e instanceof TypeMismatchException ) { - callbackContext.error(FileUtils.TYPE_MISMATCH_ERR); - } else if(e instanceof JSONException ) { - callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION)); - } else { - e.printStackTrace(); - callbackContext.error(FileUtils.UNKNOWN_ERR); - } - } - } - }); - } - - /** - * Allows the user to look up the Entry for a file or directory referred to by a local URI. - * - * @param uriString of the file/directory to look up - * @return a JSONObject representing a Entry from the filesystem - * @throws MalformedURLException if the url is not valid - * @throws FileNotFoundException if the file does not exist - * @throws IOException if the user can't read the file - * @throws JSONException - */ - private JSONObject resolveLocalFileSystemURI(String uriString) throws IOException, JSONException { - if (uriString == null) { - throw new MalformedURLException("Unrecognized filesystem URL"); - } - Uri uri = Uri.parse(uriString); - - LocalFilesystemURL inputURL = LocalFilesystemURL.parse(uri); - if (inputURL == null) { - /* Check for file://, content:// urls */ - inputURL = resolveNativeUri(uri); - } - - try { - Filesystem fs = this.filesystemForURL(inputURL); - if (fs == null) { - throw new MalformedURLException("No installed handlers for this URL"); - } - if (fs.exists(inputURL)) { - return fs.getEntryForLocalURL(inputURL); - } - } catch (IllegalArgumentException e) { - throw new MalformedURLException("Unrecognized filesystem URL"); - } - throw new FileNotFoundException(); - } - - /** - * Read the list of files from this directory. - * - * @return a JSONArray containing JSONObjects that represent Entry objects. - * @throws FileNotFoundException if the directory is not found. - * @throws JSONException - * @throws MalformedURLException - */ - private JSONArray readEntries(String baseURLstr) throws FileNotFoundException, JSONException, MalformedURLException { - try { - LocalFilesystemURL inputURL = LocalFilesystemURL.parse(baseURLstr); - Filesystem fs = this.filesystemForURL(inputURL); - if (fs == null) { - throw new MalformedURLException("No installed handlers for this URL"); - } - return fs.readEntriesAtLocalURL(inputURL); - - } catch (IllegalArgumentException e) { - throw new MalformedURLException("Unrecognized filesystem URL"); - } - } - - /** - * A setup method that handles the move/copy of files/directories - * - * @param newName for the file directory to be called, if null use existing file name - * @param move if false do a copy, if true do a move - * @return a Entry object - * @throws NoModificationAllowedException - * @throws IOException - * @throws InvalidModificationException - * @throws EncodingException - * @throws JSONException - * @throws FileExistsException - */ - private JSONObject transferTo(String srcURLstr, String destURLstr, String newName, boolean move) throws JSONException, NoModificationAllowedException, IOException, InvalidModificationException, EncodingException, FileExistsException { - if (srcURLstr == null || destURLstr == null) { - // either no source or no destination provided - throw new FileNotFoundException(); - } - - LocalFilesystemURL srcURL = LocalFilesystemURL.parse(srcURLstr); - LocalFilesystemURL destURL = LocalFilesystemURL.parse(destURLstr); - - Filesystem srcFs = this.filesystemForURL(srcURL); - Filesystem destFs = this.filesystemForURL(destURL); - - // Check for invalid file name - if (newName != null && newName.contains(":")) { - throw new EncodingException("Bad file name"); - } - - return destFs.copyFileToURL(destURL, newName, srcFs, srcURL, move); - } - - /** - * Deletes a directory and all of its contents, if any. In the event of an error - * [e.g. trying to delete a directory that contains a file that cannot be removed], - * some of the contents of the directory may be deleted. - * It is an error to attempt to delete the root directory of a filesystem. - * - * @return a boolean representing success of failure - * @throws FileExistsException - * @throws NoModificationAllowedException - * @throws MalformedURLException - */ - private boolean removeRecursively(String baseURLstr) throws FileExistsException, NoModificationAllowedException, MalformedURLException { - try { - LocalFilesystemURL inputURL = LocalFilesystemURL.parse(baseURLstr); - // You can't delete the root directory. - if ("".equals(inputURL.path) || "/".equals(inputURL.path)) { - throw new NoModificationAllowedException("You can't delete the root directory"); - } - - Filesystem fs = this.filesystemForURL(inputURL); - if (fs == null) { - throw new MalformedURLException("No installed handlers for this URL"); - } - return fs.recursiveRemoveFileAtLocalURL(inputURL); - - } catch (IllegalArgumentException e) { - throw new MalformedURLException("Unrecognized filesystem URL"); - } - } - - - /** - * Deletes a file or directory. It is an error to attempt to delete a directory that is not empty. - * It is an error to attempt to delete the root directory of a filesystem. - * - * @return a boolean representing success of failure - * @throws NoModificationAllowedException - * @throws InvalidModificationException - * @throws MalformedURLException - */ - private boolean remove(String baseURLstr) throws NoModificationAllowedException, InvalidModificationException, MalformedURLException { - try { - LocalFilesystemURL inputURL = LocalFilesystemURL.parse(baseURLstr); - // You can't delete the root directory. - if ("".equals(inputURL.path) || "/".equals(inputURL.path)) { - - throw new NoModificationAllowedException("You can't delete the root directory"); - } - - Filesystem fs = this.filesystemForURL(inputURL); - if (fs == null) { - throw new MalformedURLException("No installed handlers for this URL"); - } - return fs.removeFileAtLocalURL(inputURL); - - } catch (IllegalArgumentException e) { - throw new MalformedURLException("Unrecognized filesystem URL"); - } - } - - /** - * Creates or looks up a file. - * - * @param baseURLstr base directory - * @param path file/directory to lookup or create - * @param options specify whether to create or not - * @param directory if true look up directory, if false look up file - * @return a Entry object - * @throws FileExistsException - * @throws IOException - * @throws TypeMismatchException - * @throws EncodingException - * @throws JSONException - */ - private JSONObject getFile(String baseURLstr, String path, JSONObject options, boolean directory) throws FileExistsException, IOException, TypeMismatchException, EncodingException, JSONException { - try { - LocalFilesystemURL inputURL = LocalFilesystemURL.parse(baseURLstr); - Filesystem fs = this.filesystemForURL(inputURL); - if (fs == null) { - throw new MalformedURLException("No installed handlers for this URL"); - } - return fs.getFileForLocalURL(inputURL, path, options, directory); - - } catch (IllegalArgumentException e) { - throw new MalformedURLException("Unrecognized filesystem URL"); - } - - } - - /** - * Look up the parent DirectoryEntry containing this Entry. - * If this Entry is the root of its filesystem, its parent is itself. - */ - private JSONObject getParent(String baseURLstr) throws JSONException, IOException { - try { - LocalFilesystemURL inputURL = LocalFilesystemURL.parse(baseURLstr); - Filesystem fs = this.filesystemForURL(inputURL); - if (fs == null) { - throw new MalformedURLException("No installed handlers for this URL"); - } - return fs.getParentForLocalURL(inputURL); - - } catch (IllegalArgumentException e) { - throw new MalformedURLException("Unrecognized filesystem URL"); - } - } - - /** - * Returns a File that represents the current state of the file that this FileEntry represents. - * - * @return returns a JSONObject represent a W3C File object - */ - private JSONObject getFileMetadata(String baseURLstr) throws FileNotFoundException, JSONException, MalformedURLException { - try { - LocalFilesystemURL inputURL = LocalFilesystemURL.parse(baseURLstr); - Filesystem fs = this.filesystemForURL(inputURL); - if (fs == null) { - throw new MalformedURLException("No installed handlers for this URL"); - } - return fs.getFileMetadataForLocalURL(inputURL); - - } catch (IllegalArgumentException e) { - throw new MalformedURLException("Unrecognized filesystem URL"); - } - } - - /** - * Requests a filesystem in which to store application data. - * - * @param type of file system requested - * @return a JSONObject representing the file system - * @throws IOException - * @throws JSONException - */ - private JSONObject requestFileSystem(int type) throws IOException, JSONException { - JSONObject fs = new JSONObject(); - Filesystem rootFs = null; - try { - rootFs = this.filesystems.get(type); - } catch (ArrayIndexOutOfBoundsException e) { - // Pass null through - } - if (rootFs == null) { - throw new IOException("No filesystem of type requested"); - } - fs.put("name", rootFs.name); - fs.put("root", rootFs.getRootEntry()); - return fs; - } - - - /** - * Requests a filesystem in which to store application data. - * - * @return a JSONObject representing the file system - */ - private JSONArray requestAllFileSystems() throws IOException, JSONException { - JSONArray ret = new JSONArray(); - for (Filesystem fs : filesystems) { - ret.put(fs.getRootEntry()); - } - return ret; - } - - private static String toDirUrl(File f) { - return Uri.fromFile(f).toString() + '/'; - } - - private JSONObject requestAllPaths() throws JSONException { - Context context = cordova.getActivity(); - JSONObject ret = new JSONObject(); - ret.put("applicationDirectory", "file:///android_asset/"); - ret.put("applicationStorageDirectory", toDirUrl(context.getFilesDir().getParentFile())); - ret.put("dataDirectory", toDirUrl(context.getFilesDir())); - ret.put("cacheDirectory", toDirUrl(context.getCacheDir())); - if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - try { - ret.put("externalApplicationStorageDirectory", toDirUrl(context.getExternalFilesDir(null).getParentFile())); - ret.put("externalDataDirectory", toDirUrl(context.getExternalFilesDir(null))); - ret.put("externalCacheDirectory", toDirUrl(context.getExternalCacheDir())); - ret.put("externalRootDirectory", toDirUrl(Environment.getExternalStorageDirectory())); - } - catch(NullPointerException e) { - /* If external storage is unavailable, context.getExternal* returns null */ - Log.d(LOG_TAG, "Unable to access these paths, most liklely due to USB storage"); - } - } - return ret; - } - - /** - * Returns a JSON object representing the given File. Internal APIs should be modified - * to use URLs instead of raw FS paths wherever possible, when interfacing with this plugin. - * - * @param file the File to convert - * @return a JSON representation of the given File - * @throws JSONException - */ - public JSONObject getEntryForFile(File file) throws JSONException { - JSONObject entry; - - for (Filesystem fs : filesystems) { - entry = fs.makeEntryForFile(file); - if (entry != null) { - return entry; - } - } - return null; - } - - /** - * Returns a JSON object representing the given File. Deprecated, as this is only used by - * FileTransfer, and because it is a static method that should really be an instance method, - * since it depends on the actual filesystem roots in use. Internal APIs should be modified - * to use URLs instead of raw FS paths wherever possible, when interfacing with this plugin. - * - * @param file the File to convert - * @return a JSON representation of the given File - * @throws JSONException - */ - @Deprecated - public static JSONObject getEntry(File file) throws JSONException { - if (getFilePlugin() != null) { - return getFilePlugin().getEntryForFile(file); - } - return null; - } - - /** - * Read the contents of a file. - * This is done in a background thread; the result is sent to the callback. - * - * @param start Start position in the file. - * @param end End position to stop at (exclusive). - * @param callbackContext The context through which to send the result. - * @param encoding The encoding to return contents as. Typical value is UTF-8. (see http://www.iana.org/assignments/character-sets) - * @param resultType The desired type of data to send to the callback. - * @return Contents of file. - */ - public void readFileAs(final String srcURLstr, final int start, final int end, final CallbackContext callbackContext, final String encoding, final int resultType) throws MalformedURLException { - try { - LocalFilesystemURL inputURL = LocalFilesystemURL.parse(srcURLstr); - Filesystem fs = this.filesystemForURL(inputURL); - if (fs == null) { - throw new MalformedURLException("No installed handlers for this URL"); - } - - fs.readFileAtURL(inputURL, start, end, new Filesystem.ReadFileCallback() { - public void handleData(InputStream inputStream, String contentType) { - try { - ByteArrayOutputStream os = new ByteArrayOutputStream(); - final int BUFFER_SIZE = 8192; - byte[] buffer = new byte[BUFFER_SIZE]; - - for (;;) { - int bytesRead = inputStream.read(buffer, 0, BUFFER_SIZE); - - if (bytesRead <= 0) { - break; - } - os.write(buffer, 0, bytesRead); - } - - PluginResult result; - switch (resultType) { - case PluginResult.MESSAGE_TYPE_STRING: - result = new PluginResult(PluginResult.Status.OK, os.toString(encoding)); - break; - case PluginResult.MESSAGE_TYPE_ARRAYBUFFER: - result = new PluginResult(PluginResult.Status.OK, os.toByteArray()); - break; - case PluginResult.MESSAGE_TYPE_BINARYSTRING: - result = new PluginResult(PluginResult.Status.OK, os.toByteArray(), true); - break; - default: // Base64. - byte[] base64 = Base64.encode(os.toByteArray(), Base64.NO_WRAP); - String s = "data:" + contentType + ";base64," + new String(base64, "US-ASCII"); - result = new PluginResult(PluginResult.Status.OK, s); - } - - callbackContext.sendPluginResult(result); - } catch (IOException e) { - Log.d(LOG_TAG, e.getLocalizedMessage()); - callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, NOT_READABLE_ERR)); - } - } - }); - - - } catch (IllegalArgumentException e) { - throw new MalformedURLException("Unrecognized filesystem URL"); - } catch (FileNotFoundException e) { - callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, NOT_FOUND_ERR)); - } catch (IOException e) { - Log.d(LOG_TAG, e.getLocalizedMessage()); - callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, NOT_READABLE_ERR)); - } - } - - - /** - * Write contents of file. - * - * @param data The contents of the file. - * @param offset The position to begin writing the file. - * @param isBinary True if the file contents are base64-encoded binary data - */ - /**/ - public long write(String srcURLstr, String data, int offset, boolean isBinary) throws FileNotFoundException, IOException, NoModificationAllowedException { - try { - LocalFilesystemURL inputURL = LocalFilesystemURL.parse(srcURLstr); - Filesystem fs = this.filesystemForURL(inputURL); - if (fs == null) { - throw new MalformedURLException("No installed handlers for this URL"); - } - - long x = fs.writeToFileAtURL(inputURL, data, offset, isBinary); Log.d("TEST",srcURLstr + ": "+x); return x; - } catch (IllegalArgumentException e) { - throw new MalformedURLException("Unrecognized filesystem URL"); - } - - } - - /** - * Truncate the file to size - */ - private long truncateFile(String srcURLstr, long size) throws FileNotFoundException, IOException, NoModificationAllowedException { - try { - LocalFilesystemURL inputURL = LocalFilesystemURL.parse(srcURLstr); - Filesystem fs = this.filesystemForURL(inputURL); - if (fs == null) { - throw new MalformedURLException("No installed handlers for this URL"); - } - - return fs.truncateFileAtURL(inputURL, size); - } catch (IllegalArgumentException e) { - throw new MalformedURLException("Unrecognized filesystem URL"); - } - } -} diff --git a/plugins/cordova-plugin-file/src/android/Filesystem.java b/plugins/cordova-plugin-file/src/android/Filesystem.java deleted file mode 100644 index faf31d2a..00000000 --- a/plugins/cordova-plugin-file/src/android/Filesystem.java +++ /dev/null @@ -1,325 +0,0 @@ -/* - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you 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. - */ -package org.apache.cordova.file; - -import android.net.Uri; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FilterInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Arrays; - -import org.apache.cordova.CordovaResourceApi; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -public abstract class Filesystem { - - protected final Uri rootUri; - protected final CordovaResourceApi resourceApi; - public final String name; - private JSONObject rootEntry; - - public Filesystem(Uri rootUri, String name, CordovaResourceApi resourceApi) { - this.rootUri = rootUri; - this.name = name; - this.resourceApi = resourceApi; - } - - public interface ReadFileCallback { - public void handleData(InputStream inputStream, String contentType) throws IOException; - } - - public static JSONObject makeEntryForURL(LocalFilesystemURL inputURL, Uri nativeURL) { - try { - String path = inputURL.path; - int end = path.endsWith("/") ? 1 : 0; - String[] parts = path.substring(0, path.length() - end).split("/+"); - String fileName = parts[parts.length - 1]; - - JSONObject entry = new JSONObject(); - entry.put("isFile", !inputURL.isDirectory); - entry.put("isDirectory", inputURL.isDirectory); - entry.put("name", fileName); - entry.put("fullPath", path); - // The file system can't be specified, as it would lead to an infinite loop, - // but the filesystem name can be. - entry.put("filesystemName", inputURL.fsName); - // Backwards compatibility - entry.put("filesystem", "temporary".equals(inputURL.fsName) ? 0 : 1); - - String nativeUrlStr = nativeURL.toString(); - if (inputURL.isDirectory && !nativeUrlStr.endsWith("/")) { - nativeUrlStr += "/"; - } - entry.put("nativeURL", nativeUrlStr); - return entry; - } catch (JSONException e) { - e.printStackTrace(); - throw new RuntimeException(e); - } - } - - public JSONObject makeEntryForURL(LocalFilesystemURL inputURL) { - Uri nativeUri = toNativeUri(inputURL); - return nativeUri == null ? null : makeEntryForURL(inputURL, nativeUri); - } - - public JSONObject makeEntryForNativeUri(Uri nativeUri) { - LocalFilesystemURL inputUrl = toLocalUri(nativeUri); - return inputUrl == null ? null : makeEntryForURL(inputUrl, nativeUri); - } - - public JSONObject getEntryForLocalURL(LocalFilesystemURL inputURL) throws IOException { - return makeEntryForURL(inputURL); - } - - public JSONObject makeEntryForFile(File file) { - return makeEntryForNativeUri(Uri.fromFile(file)); - } - - abstract JSONObject getFileForLocalURL(LocalFilesystemURL inputURL, String path, - JSONObject options, boolean directory) throws FileExistsException, IOException, TypeMismatchException, EncodingException, JSONException; - - abstract boolean removeFileAtLocalURL(LocalFilesystemURL inputURL) throws InvalidModificationException, NoModificationAllowedException; - - abstract boolean recursiveRemoveFileAtLocalURL(LocalFilesystemURL inputURL) throws FileExistsException, NoModificationAllowedException; - - abstract LocalFilesystemURL[] listChildren(LocalFilesystemURL inputURL) throws FileNotFoundException; - - public final JSONArray readEntriesAtLocalURL(LocalFilesystemURL inputURL) throws FileNotFoundException { - LocalFilesystemURL[] children = listChildren(inputURL); - JSONArray entries = new JSONArray(); - if (children != null) { - for (LocalFilesystemURL url : children) { - entries.put(makeEntryForURL(url)); - } - } - return entries; - } - - abstract JSONObject getFileMetadataForLocalURL(LocalFilesystemURL inputURL) throws FileNotFoundException; - - public Uri getRootUri() { - return rootUri; - } - - public boolean exists(LocalFilesystemURL inputURL) { - try { - getFileMetadataForLocalURL(inputURL); - } catch (FileNotFoundException e) { - return false; - } - return true; - } - - public Uri nativeUriForFullPath(String fullPath) { - Uri ret = null; - if (fullPath != null) { - String encodedPath = Uri.fromFile(new File(fullPath)).getEncodedPath(); - if (encodedPath.startsWith("/")) { - encodedPath = encodedPath.substring(1); - } - ret = rootUri.buildUpon().appendEncodedPath(encodedPath).build(); - } - return ret; - } - - public LocalFilesystemURL localUrlforFullPath(String fullPath) { - Uri nativeUri = nativeUriForFullPath(fullPath); - if (nativeUri != null) { - return toLocalUri(nativeUri); - } - return null; - } - - /** - * Removes multiple repeated //s, and collapses processes ../s. - */ - protected static String normalizePath(String rawPath) { - // If this is an absolute path, trim the leading "/" and replace it later - boolean isAbsolutePath = rawPath.startsWith("/"); - if (isAbsolutePath) { - rawPath = rawPath.replaceFirst("/+", ""); - } - ArrayList<String> components = new ArrayList<String>(Arrays.asList(rawPath.split("/+"))); - for (int index = 0; index < components.size(); ++index) { - if (components.get(index).equals("..")) { - components.remove(index); - if (index > 0) { - components.remove(index-1); - --index; - } - } - } - StringBuilder normalizedPath = new StringBuilder(); - for(String component: components) { - normalizedPath.append("/"); - normalizedPath.append(component); - } - if (isAbsolutePath) { - return normalizedPath.toString(); - } else { - return normalizedPath.toString().substring(1); - } - } - - - - public abstract Uri toNativeUri(LocalFilesystemURL inputURL); - public abstract LocalFilesystemURL toLocalUri(Uri inputURL); - - public JSONObject getRootEntry() { - if (rootEntry == null) { - rootEntry = makeEntryForNativeUri(rootUri); - } - return rootEntry; - } - - public JSONObject getParentForLocalURL(LocalFilesystemURL inputURL) throws IOException { - Uri parentUri = inputURL.uri; - String parentPath = new File(inputURL.uri.getPath()).getParent(); - if (!"/".equals(parentPath)) { - parentUri = inputURL.uri.buildUpon().path(parentPath + '/').build(); - } - return getEntryForLocalURL(LocalFilesystemURL.parse(parentUri)); - } - - protected LocalFilesystemURL makeDestinationURL(String newName, LocalFilesystemURL srcURL, LocalFilesystemURL destURL, boolean isDirectory) { - // I know this looks weird but it is to work around a JSON bug. - if ("null".equals(newName) || "".equals(newName)) { - newName = srcURL.uri.getLastPathSegment();; - } - - String newDest = destURL.uri.toString(); - if (newDest.endsWith("/")) { - newDest = newDest + newName; - } else { - newDest = newDest + "/" + newName; - } - if (isDirectory) { - newDest += '/'; - } - return LocalFilesystemURL.parse(newDest); - } - - /* Read a source URL (possibly from a different filesystem, srcFs,) and copy it to - * the destination URL on this filesystem, optionally with a new filename. - * If move is true, then this method should either perform an atomic move operation - * or remove the source file when finished. - */ - public JSONObject copyFileToURL(LocalFilesystemURL destURL, String newName, - Filesystem srcFs, LocalFilesystemURL srcURL, boolean move) throws IOException, InvalidModificationException, JSONException, NoModificationAllowedException, FileExistsException { - // First, check to see that we can do it - if (move && !srcFs.canRemoveFileAtLocalURL(srcURL)) { - throw new NoModificationAllowedException("Cannot move file at source URL"); - } - final LocalFilesystemURL destination = makeDestinationURL(newName, srcURL, destURL, srcURL.isDirectory); - - Uri srcNativeUri = srcFs.toNativeUri(srcURL); - - CordovaResourceApi.OpenForReadResult ofrr = resourceApi.openForRead(srcNativeUri); - OutputStream os = null; - try { - os = getOutputStreamForURL(destination); - } catch (IOException e) { - ofrr.inputStream.close(); - throw e; - } - // Closes streams. - resourceApi.copyResource(ofrr, os); - - if (move) { - srcFs.removeFileAtLocalURL(srcURL); - } - return getEntryForLocalURL(destination); - } - - public OutputStream getOutputStreamForURL(LocalFilesystemURL inputURL) throws IOException { - return resourceApi.openOutputStream(toNativeUri(inputURL)); - } - - public void readFileAtURL(LocalFilesystemURL inputURL, long start, long end, - ReadFileCallback readFileCallback) throws IOException { - CordovaResourceApi.OpenForReadResult ofrr = resourceApi.openForRead(toNativeUri(inputURL)); - if (end < 0) { - end = ofrr.length; - } - long numBytesToRead = end - start; - try { - if (start > 0) { - ofrr.inputStream.skip(start); - } - InputStream inputStream = ofrr.inputStream; - if (end < ofrr.length) { - inputStream = new LimitedInputStream(inputStream, numBytesToRead); - } - readFileCallback.handleData(inputStream, ofrr.mimeType); - } finally { - ofrr.inputStream.close(); - } - } - - abstract long writeToFileAtURL(LocalFilesystemURL inputURL, String data, int offset, - boolean isBinary) throws NoModificationAllowedException, IOException; - - abstract long truncateFileAtURL(LocalFilesystemURL inputURL, long size) - throws IOException, NoModificationAllowedException; - - // This method should return null if filesystem urls cannot be mapped to paths - abstract String filesystemPathForURL(LocalFilesystemURL url); - - abstract LocalFilesystemURL URLforFilesystemPath(String path); - - abstract boolean canRemoveFileAtLocalURL(LocalFilesystemURL inputURL); - - protected class LimitedInputStream extends FilterInputStream { - long numBytesToRead; - public LimitedInputStream(InputStream in, long numBytesToRead) { - super(in); - this.numBytesToRead = numBytesToRead; - } - @Override - public int read() throws IOException { - if (numBytesToRead <= 0) { - return -1; - } - numBytesToRead--; - return in.read(); - } - @Override - public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException { - if (numBytesToRead <= 0) { - return -1; - } - int bytesToRead = byteCount; - if (byteCount > numBytesToRead) { - bytesToRead = (int)numBytesToRead; // Cast okay; long is less than int here. - } - int numBytesRead = in.read(buffer, byteOffset, bytesToRead); - numBytesToRead -= numBytesRead; - return numBytesRead; - } - } -} diff --git a/plugins/cordova-plugin-file/src/android/InvalidModificationException.java b/plugins/cordova-plugin-file/src/android/InvalidModificationException.java deleted file mode 100644 index 8f6bec59..00000000 --- a/plugins/cordova-plugin-file/src/android/InvalidModificationException.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you 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. -*/ - - -package org.apache.cordova.file; - -@SuppressWarnings("serial") -public class InvalidModificationException extends Exception { - - public InvalidModificationException(String message) { - super(message); - } - -} diff --git a/plugins/cordova-plugin-file/src/android/LocalFilesystem.java b/plugins/cordova-plugin-file/src/android/LocalFilesystem.java deleted file mode 100644 index 3b1ecca8..00000000 --- a/plugins/cordova-plugin-file/src/android/LocalFilesystem.java +++ /dev/null @@ -1,505 +0,0 @@ -/* - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you 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. - */ -package org.apache.cordova.file; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.RandomAccessFile; -import java.nio.channels.FileChannel; -import org.apache.cordova.CordovaResourceApi; -import org.json.JSONException; -import org.json.JSONObject; - -import android.os.Build; -import android.os.Environment; -import android.util.Base64; -import android.net.Uri; -import android.content.Context; -import android.content.Intent; - -public class LocalFilesystem extends Filesystem { - private final Context context; - - public LocalFilesystem(String name, Context context, CordovaResourceApi resourceApi, File fsRoot) { - super(Uri.fromFile(fsRoot).buildUpon().appendEncodedPath("").build(), name, resourceApi); - this.context = context; - } - - public String filesystemPathForFullPath(String fullPath) { - return new File(rootUri.getPath(), fullPath).toString(); - } - - @Override - public String filesystemPathForURL(LocalFilesystemURL url) { - return filesystemPathForFullPath(url.path); - } - - private String fullPathForFilesystemPath(String absolutePath) { - if (absolutePath != null && absolutePath.startsWith(rootUri.getPath())) { - return absolutePath.substring(rootUri.getPath().length() - 1); - } - return null; - } - - @Override - public Uri toNativeUri(LocalFilesystemURL inputURL) { - return nativeUriForFullPath(inputURL.path); - } - - @Override - public LocalFilesystemURL toLocalUri(Uri inputURL) { - if (!"file".equals(inputURL.getScheme())) { - return null; - } - File f = new File(inputURL.getPath()); - // Removes and duplicate /s (e.g. file:///a//b/c) - Uri resolvedUri = Uri.fromFile(f); - String rootUriNoTrailingSlash = rootUri.getEncodedPath(); - rootUriNoTrailingSlash = rootUriNoTrailingSlash.substring(0, rootUriNoTrailingSlash.length() - 1); - if (!resolvedUri.getEncodedPath().startsWith(rootUriNoTrailingSlash)) { - return null; - } - String subPath = resolvedUri.getEncodedPath().substring(rootUriNoTrailingSlash.length()); - // Strip leading slash - if (!subPath.isEmpty()) { - subPath = subPath.substring(1); - } - Uri.Builder b = new Uri.Builder() - .scheme(LocalFilesystemURL.FILESYSTEM_PROTOCOL) - .authority("localhost") - .path(name); - if (!subPath.isEmpty()) { - b.appendEncodedPath(subPath); - } - if (f.isDirectory() || inputURL.getPath().endsWith("/")) { - // Add trailing / for directories. - b.appendEncodedPath(""); - } - return LocalFilesystemURL.parse(b.build()); - } - - @Override - public LocalFilesystemURL URLforFilesystemPath(String path) { - return localUrlforFullPath(fullPathForFilesystemPath(path)); - } - - @Override - public JSONObject getFileForLocalURL(LocalFilesystemURL inputURL, - String path, JSONObject options, boolean directory) throws FileExistsException, IOException, TypeMismatchException, EncodingException, JSONException { - boolean create = false; - boolean exclusive = false; - - if (options != null) { - create = options.optBoolean("create"); - if (create) { - exclusive = options.optBoolean("exclusive"); - } - } - - // Check for a ":" character in the file to line up with BB and iOS - if (path.contains(":")) { - throw new EncodingException("This path has an invalid \":\" in it."); - } - - LocalFilesystemURL requestedURL; - - // Check whether the supplied path is absolute or relative - if (directory && !path.endsWith("/")) { - path += "/"; - } - if (path.startsWith("/")) { - requestedURL = localUrlforFullPath(normalizePath(path)); - } else { - requestedURL = localUrlforFullPath(normalizePath(inputURL.path + "/" + path)); - } - - File fp = new File(this.filesystemPathForURL(requestedURL)); - - if (create) { - if (exclusive && fp.exists()) { - throw new FileExistsException("create/exclusive fails"); - } - if (directory) { - fp.mkdir(); - } else { - fp.createNewFile(); - } - if (!fp.exists()) { - throw new FileExistsException("create fails"); - } - } - else { - if (!fp.exists()) { - throw new FileNotFoundException("path does not exist"); - } - if (directory) { - if (fp.isFile()) { - throw new TypeMismatchException("path doesn't exist or is file"); - } - } else { - if (fp.isDirectory()) { - throw new TypeMismatchException("path doesn't exist or is directory"); - } - } - } - - // Return the directory - return makeEntryForURL(requestedURL); - } - - @Override - public boolean removeFileAtLocalURL(LocalFilesystemURL inputURL) throws InvalidModificationException { - - File fp = new File(filesystemPathForURL(inputURL)); - - // You can't delete a directory that is not empty - if (fp.isDirectory() && fp.list().length > 0) { - throw new InvalidModificationException("You can't delete a directory that is not empty."); - } - - return fp.delete(); - } - - @Override - public boolean exists(LocalFilesystemURL inputURL) { - File fp = new File(filesystemPathForURL(inputURL)); - return fp.exists(); - } - - @Override - public boolean recursiveRemoveFileAtLocalURL(LocalFilesystemURL inputURL) throws FileExistsException { - File directory = new File(filesystemPathForURL(inputURL)); - return removeDirRecursively(directory); - } - - protected boolean removeDirRecursively(File directory) throws FileExistsException { - if (directory.isDirectory()) { - for (File file : directory.listFiles()) { - removeDirRecursively(file); - } - } - - if (!directory.delete()) { - throw new FileExistsException("could not delete: " + directory.getName()); - } else { - return true; - } - } - - @Override - public LocalFilesystemURL[] listChildren(LocalFilesystemURL inputURL) throws FileNotFoundException { - File fp = new File(filesystemPathForURL(inputURL)); - - if (!fp.exists()) { - // The directory we are listing doesn't exist so we should fail. - throw new FileNotFoundException(); - } - - File[] files = fp.listFiles(); - if (files == null) { - // inputURL is a directory - return null; - } - LocalFilesystemURL[] entries = new LocalFilesystemURL[files.length]; - for (int i = 0; i < files.length; i++) { - entries[i] = URLforFilesystemPath(files[i].getPath()); - } - - return entries; - } - - @Override - public JSONObject getFileMetadataForLocalURL(LocalFilesystemURL inputURL) throws FileNotFoundException { - File file = new File(filesystemPathForURL(inputURL)); - - if (!file.exists()) { - throw new FileNotFoundException("File at " + inputURL.uri + " does not exist."); - } - - JSONObject metadata = new JSONObject(); - try { - // Ensure that directories report a size of 0 - metadata.put("size", file.isDirectory() ? 0 : file.length()); - metadata.put("type", resourceApi.getMimeType(Uri.fromFile(file))); - metadata.put("name", file.getName()); - metadata.put("fullPath", inputURL.path); - metadata.put("lastModifiedDate", file.lastModified()); - } catch (JSONException e) { - return null; - } - return metadata; - } - - private void copyFile(Filesystem srcFs, LocalFilesystemURL srcURL, File destFile, boolean move) throws IOException, InvalidModificationException, NoModificationAllowedException { - if (move) { - String realSrcPath = srcFs.filesystemPathForURL(srcURL); - if (realSrcPath != null) { - File srcFile = new File(realSrcPath); - if (srcFile.renameTo(destFile)) { - return; - } - // Trying to rename the file failed. Possibly because we moved across file system on the device. - } - } - - CordovaResourceApi.OpenForReadResult offr = resourceApi.openForRead(srcFs.toNativeUri(srcURL)); - copyResource(offr, new FileOutputStream(destFile)); - - if (move) { - srcFs.removeFileAtLocalURL(srcURL); - } - } - - private void copyDirectory(Filesystem srcFs, LocalFilesystemURL srcURL, File dstDir, boolean move) throws IOException, NoModificationAllowedException, InvalidModificationException, FileExistsException { - if (move) { - String realSrcPath = srcFs.filesystemPathForURL(srcURL); - if (realSrcPath != null) { - File srcDir = new File(realSrcPath); - // If the destination directory already exists and is empty then delete it. This is according to spec. - if (dstDir.exists()) { - if (dstDir.list().length > 0) { - throw new InvalidModificationException("directory is not empty"); - } - dstDir.delete(); - } - // Try to rename the directory - if (srcDir.renameTo(dstDir)) { - return; - } - // Trying to rename the file failed. Possibly because we moved across file system on the device. - } - } - - if (dstDir.exists()) { - if (dstDir.list().length > 0) { - throw new InvalidModificationException("directory is not empty"); - } - } else { - if (!dstDir.mkdir()) { - // If we can't create the directory then fail - throw new NoModificationAllowedException("Couldn't create the destination directory"); - } - } - - LocalFilesystemURL[] children = srcFs.listChildren(srcURL); - for (LocalFilesystemURL childLocalUrl : children) { - File target = new File(dstDir, new File(childLocalUrl.path).getName()); - if (childLocalUrl.isDirectory) { - copyDirectory(srcFs, childLocalUrl, target, false); - } else { - copyFile(srcFs, childLocalUrl, target, false); - } - } - - if (move) { - srcFs.recursiveRemoveFileAtLocalURL(srcURL); - } - } - - @Override - public JSONObject copyFileToURL(LocalFilesystemURL destURL, String newName, - Filesystem srcFs, LocalFilesystemURL srcURL, boolean move) throws IOException, InvalidModificationException, JSONException, NoModificationAllowedException, FileExistsException { - - // Check to see if the destination directory exists - String newParent = this.filesystemPathForURL(destURL); - File destinationDir = new File(newParent); - if (!destinationDir.exists()) { - // The destination does not exist so we should fail. - throw new FileNotFoundException("The source does not exist"); - } - - // Figure out where we should be copying to - final LocalFilesystemURL destinationURL = makeDestinationURL(newName, srcURL, destURL, srcURL.isDirectory); - - Uri dstNativeUri = toNativeUri(destinationURL); - Uri srcNativeUri = srcFs.toNativeUri(srcURL); - // Check to see if source and destination are the same file - if (dstNativeUri.equals(srcNativeUri)) { - throw new InvalidModificationException("Can't copy onto itself"); - } - - if (move && !srcFs.canRemoveFileAtLocalURL(srcURL)) { - throw new InvalidModificationException("Source URL is read-only (cannot move)"); - } - - File destFile = new File(dstNativeUri.getPath()); - if (destFile.exists()) { - if (!srcURL.isDirectory && destFile.isDirectory()) { - throw new InvalidModificationException("Can't copy/move a file to an existing directory"); - } else if (srcURL.isDirectory && destFile.isFile()) { - throw new InvalidModificationException("Can't copy/move a directory to an existing file"); - } - } - - if (srcURL.isDirectory) { - // E.g. Copy /sdcard/myDir to /sdcard/myDir/backup - if (dstNativeUri.toString().startsWith(srcNativeUri.toString() + '/')) { - throw new InvalidModificationException("Can't copy directory into itself"); - } - copyDirectory(srcFs, srcURL, destFile, move); - } else { - copyFile(srcFs, srcURL, destFile, move); - } - return makeEntryForURL(destinationURL); - } - - @Override - public long writeToFileAtURL(LocalFilesystemURL inputURL, String data, - int offset, boolean isBinary) throws IOException, NoModificationAllowedException { - - boolean append = false; - if (offset > 0) { - this.truncateFileAtURL(inputURL, offset); - append = true; - } - - byte[] rawData; - if (isBinary) { - rawData = Base64.decode(data, Base64.DEFAULT); - } else { - rawData = data.getBytes(); - } - ByteArrayInputStream in = new ByteArrayInputStream(rawData); - try - { - byte buff[] = new byte[rawData.length]; - String absolutePath = filesystemPathForURL(inputURL); - FileOutputStream out = new FileOutputStream(absolutePath, append); - try { - in.read(buff, 0, buff.length); - out.write(buff, 0, rawData.length); - out.flush(); - } finally { - // Always close the output - out.close(); - } - if (isPublicDirectory(absolutePath)) { - broadcastNewFile(Uri.fromFile(new File(absolutePath))); - } - } - catch (NullPointerException e) - { - // This is a bug in the Android implementation of the Java Stack - NoModificationAllowedException realException = new NoModificationAllowedException(inputURL.toString()); - throw realException; - } - - return rawData.length; - } - - private boolean isPublicDirectory(String absolutePath) { - // TODO: should expose a way to scan app's private files (maybe via a flag). - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - // Lollipop has a bug where SD cards are null. - for (File f : context.getExternalMediaDirs()) { - if(f != null && absolutePath.startsWith(f.getAbsolutePath())) { - return true; - } - } - } - - String extPath = Environment.getExternalStorageDirectory().getAbsolutePath(); - return absolutePath.startsWith(extPath); - } - - /** - * Send broadcast of new file so files appear over MTP - */ - private void broadcastNewFile(Uri nativeUri) { - Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, nativeUri); - context.sendBroadcast(intent); - } - - @Override - public long truncateFileAtURL(LocalFilesystemURL inputURL, long size) throws IOException { - File file = new File(filesystemPathForURL(inputURL)); - - if (!file.exists()) { - throw new FileNotFoundException("File at " + inputURL.uri + " does not exist."); - } - - RandomAccessFile raf = new RandomAccessFile(filesystemPathForURL(inputURL), "rw"); - try { - if (raf.length() >= size) { - FileChannel channel = raf.getChannel(); - channel.truncate(size); - return size; - } - - return raf.length(); - } finally { - raf.close(); - } - - - } - - @Override - public boolean canRemoveFileAtLocalURL(LocalFilesystemURL inputURL) { - String path = filesystemPathForURL(inputURL); - File file = new File(path); - return file.exists(); - } - - // This is a copy & paste from CordovaResource API that is required since CordovaResourceApi - // has a bug pre-4.0.0. - // TODO: Once cordova-android@4.0.0 is released, delete this copy and make the plugin depend on - // 4.0.0 with an engine tag. - private static void copyResource(CordovaResourceApi.OpenForReadResult input, OutputStream outputStream) throws IOException { - try { - InputStream inputStream = input.inputStream; - if (inputStream instanceof FileInputStream && outputStream instanceof FileOutputStream) { - FileChannel inChannel = ((FileInputStream)input.inputStream).getChannel(); - FileChannel outChannel = ((FileOutputStream)outputStream).getChannel(); - long offset = 0; - long length = input.length; - if (input.assetFd != null) { - offset = input.assetFd.getStartOffset(); - } - // transferFrom()'s 2nd arg is a relative position. Need to set the absolute - // position first. - inChannel.position(offset); - outChannel.transferFrom(inChannel, 0, length); - } else { - final int BUFFER_SIZE = 8192; - byte[] buffer = new byte[BUFFER_SIZE]; - - for (;;) { - int bytesRead = inputStream.read(buffer, 0, BUFFER_SIZE); - - if (bytesRead <= 0) { - break; - } - outputStream.write(buffer, 0, bytesRead); - } - } - } finally { - input.inputStream.close(); - if (outputStream != null) { - outputStream.close(); - } - } - } -} diff --git a/plugins/cordova-plugin-file/src/android/LocalFilesystemURL.java b/plugins/cordova-plugin-file/src/android/LocalFilesystemURL.java deleted file mode 100644 index 74f43db6..00000000 --- a/plugins/cordova-plugin-file/src/android/LocalFilesystemURL.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you 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. - */ -package org.apache.cordova.file; - -import android.net.Uri; - -public class LocalFilesystemURL { - - public static final String FILESYSTEM_PROTOCOL = "cdvfile"; - - public final Uri uri; - public final String fsName; - public final String path; - public final boolean isDirectory; - - private LocalFilesystemURL(Uri uri, String fsName, String fsPath, boolean isDirectory) { - this.uri = uri; - this.fsName = fsName; - this.path = fsPath; - this.isDirectory = isDirectory; - } - - public static LocalFilesystemURL parse(Uri uri) { - if (!FILESYSTEM_PROTOCOL.equals(uri.getScheme())) { - return null; - } - String path = uri.getPath(); - if (path.length() < 1) { - return null; - } - int firstSlashIdx = path.indexOf('/', 1); - if (firstSlashIdx < 0) { - return null; - } - String fsName = path.substring(1, firstSlashIdx); - path = path.substring(firstSlashIdx); - boolean isDirectory = path.charAt(path.length() - 1) == '/'; - return new LocalFilesystemURL(uri, fsName, path, isDirectory); - } - - public static LocalFilesystemURL parse(String uri) { - return parse(Uri.parse(uri)); - } - - public String toString() { - return uri.toString(); - } -} diff --git a/plugins/cordova-plugin-file/src/android/NoModificationAllowedException.java b/plugins/cordova-plugin-file/src/android/NoModificationAllowedException.java deleted file mode 100644 index 627eafb5..00000000 --- a/plugins/cordova-plugin-file/src/android/NoModificationAllowedException.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you 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. -*/ - -package org.apache.cordova.file; - -@SuppressWarnings("serial") -public class NoModificationAllowedException extends Exception { - - public NoModificationAllowedException(String message) { - super(message); - } - -} diff --git a/plugins/cordova-plugin-file/src/android/TypeMismatchException.java b/plugins/cordova-plugin-file/src/android/TypeMismatchException.java deleted file mode 100644 index 1315f9a9..00000000 --- a/plugins/cordova-plugin-file/src/android/TypeMismatchException.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you 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. -*/ - - -package org.apache.cordova.file; - -@SuppressWarnings("serial") -public class TypeMismatchException extends Exception { - - public TypeMismatchException(String message) { - super(message); - } - -} diff --git a/plugins/cordova-plugin-file/src/android/build-extras.gradle b/plugins/cordova-plugin-file/src/android/build-extras.gradle deleted file mode 100644 index a0a7844a..00000000 --- a/plugins/cordova-plugin-file/src/android/build-extras.gradle +++ /dev/null @@ -1,47 +0,0 @@ -/* - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you 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. - */ -ext.postBuildExtras = { - def inAssetsDir = file("assets") - def outAssetsDir = inAssetsDir - def outFile = new File(outAssetsDir, "cdvasset.manifest") - - def newTask = task("cdvCreateAssetManifest") << { - def contents = new HashMap() - def sizes = new HashMap() - contents[""] = inAssetsDir.list() - def tree = fileTree(dir: inAssetsDir) - tree.visit { fileDetails -> - if (fileDetails.isDirectory()) { - contents[fileDetails.relativePath.toString()] = fileDetails.file.list() - } else { - sizes[fileDetails.relativePath.toString()] = fileDetails.file.length() - } - } - - outAssetsDir.mkdirs() - outFile.withObjectOutputStream { oos -> - oos.writeObject(contents) - oos.writeObject(sizes) - } - } - newTask.inputs.dir inAssetsDir - newTask.outputs.file outFile - def preBuildTask = tasks["preBuild"] - preBuildTask.dependsOn(newTask) -} diff --git a/plugins/cordova-plugin-file/src/blackberry10/index.js b/plugins/cordova-plugin-file/src/blackberry10/index.js deleted file mode 100644 index 913ab30a..00000000 --- a/plugins/cordova-plugin-file/src/blackberry10/index.js +++ /dev/null @@ -1,44 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - * -*/ -module.exports = { - setSandbox : function (success, fail, args, env) { - require("lib/webview").setSandbox(JSON.parse(decodeURIComponent(args[0]))); - new PluginResult(args, env).ok(); - }, - - getHomePath: function (success, fail, args, env) { - var homeDir = window.qnx.webplatform.getApplication().getEnv("HOME"); - new PluginResult(args, env).ok(homeDir); - }, - - requestAllPaths: function (success, fail, args, env) { - var homeDir = 'file://' + window.qnx.webplatform.getApplication().getEnv("HOME").replace('/data', ''), - paths = { - applicationDirectory: homeDir + '/app/native/', - applicationStorageDirectory: homeDir + '/', - dataDirectory: homeDir + '/data/webviews/webfs/persistent/local__0/', - cacheDirectory: homeDir + '/data/webviews/webfs/temporary/local__0/', - externalRootDirectory: 'file:///accounts/1000/removable/sdcard/', - sharedDirectory: homeDir + '/shared/' - }; - success(paths); - } -}; diff --git a/plugins/cordova-plugin-file/src/browser/FileProxy.js b/plugins/cordova-plugin-file/src/browser/FileProxy.js deleted file mode 100644 index c853db8d..00000000 --- a/plugins/cordova-plugin-file/src/browser/FileProxy.js +++ /dev/null @@ -1,964 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - * - */ - -/*global require, exports, module*/ -/*global FILESYSTEM_PREFIX*/ -/*global IDBKeyRange*/ - -/* Heavily based on https://github.com/ebidel/idb.filesystem.js */ - -// window.webkitRequestFileSystem and window.webkitResolveLocalFileSystemURL -// are available only in Chrome and possible a good flag to indicate -// that we're running in Chrome -var isChrome = window.webkitRequestFileSystem && window.webkitResolveLocalFileSystemURL; - -// For chrome we don't need to implement proxy methods -// All functionality can be accessed natively. -if (isChrome) { - var pathsPrefix = { - // Read-only directory where the application is installed. - applicationDirectory: location.origin + "/", - // Where to put app-specific data files. - dataDirectory: 'filesystem:file:///persistent/', - // Cached files that should survive app restarts. - // Apps should not rely on the OS to delete files in here. - cacheDirectory: 'filesystem:file:///temporary/', - }; - - exports.requestAllPaths = function(successCallback) { - successCallback(pathsPrefix); - }; - - require("cordova/exec/proxy").add("File", module.exports); - return; -} - -var LocalFileSystem = require('./LocalFileSystem'), - FileSystem = require('./FileSystem'), - FileEntry = require('./FileEntry'), - FileError = require('./FileError'), - DirectoryEntry = require('./DirectoryEntry'), - File = require('./File'); - -(function(exports, global) { - var indexedDB = global.indexedDB || global.mozIndexedDB; - if (!indexedDB) { - throw "Firefox OS File plugin: indexedDB not supported"; - } - - var fs_ = null; - - var idb_ = {}; - idb_.db = null; - var FILE_STORE_ = 'entries'; - - var DIR_SEPARATOR = '/'; - - var pathsPrefix = { - // Read-only directory where the application is installed. - applicationDirectory: location.origin + "/", - // Where to put app-specific data files. - dataDirectory: 'file:///persistent/', - // Cached files that should survive app restarts. - // Apps should not rely on the OS to delete files in here. - cacheDirectory: 'file:///temporary/', - }; - - var unicodeLastChar = 65535; - -/*** Exported functionality ***/ - - exports.requestFileSystem = function(successCallback, errorCallback, args) { - var type = args[0]; - // Size is ignored since IDB filesystem size depends - // on browser implementation and can't be set up by user - var size = args[1]; // jshint ignore: line - - if (type !== LocalFileSystem.TEMPORARY && type !== LocalFileSystem.PERSISTENT) { - errorCallback && errorCallback(FileError.INVALID_MODIFICATION_ERR); - return; - } - - var name = type === LocalFileSystem.TEMPORARY ? 'temporary' : 'persistent'; - var storageName = (location.protocol + location.host).replace(/:/g, '_'); - - var root = new DirectoryEntry('', DIR_SEPARATOR); - fs_ = new FileSystem(name, root); - - idb_.open(storageName, function() { - successCallback(fs_); - }, errorCallback); - }; - - // Overridden by Android, BlackBerry 10 and iOS to populate fsMap - require('./fileSystems').getFs = function(name, callback) { - callback(new FileSystem(name, fs_.root)); - }; - - // list a directory's contents (files and folders). - exports.readEntries = function(successCallback, errorCallback, args) { - var fullPath = args[0]; - - if (typeof successCallback !== 'function') { - throw Error('Expected successCallback argument.'); - } - - var path = resolveToFullPath_(fullPath); - - exports.getDirectory(function() { - idb_.getAllEntries(path.fullPath + DIR_SEPARATOR, path.storagePath, function(entries) { - successCallback(entries); - }, errorCallback); - }, function() { - if (errorCallback) { - errorCallback(FileError.NOT_FOUND_ERR); - } - }, [path.storagePath, path.fullPath, {create: false}]); - }; - - exports.getFile = function(successCallback, errorCallback, args) { - var fullPath = args[0]; - var path = args[1]; - var options = args[2] || {}; - - // Create an absolute path if we were handed a relative one. - path = resolveToFullPath_(fullPath, path); - - idb_.get(path.storagePath, function(fileEntry) { - if (options.create === true && options.exclusive === true && fileEntry) { - // If create and exclusive are both true, and the path already exists, - // getFile must fail. - - if (errorCallback) { - errorCallback(FileError.PATH_EXISTS_ERR); - } - } else if (options.create === true && !fileEntry) { - // If create is true, the path doesn't exist, and no other error occurs, - // getFile must create it as a zero-length file and return a corresponding - // FileEntry. - var newFileEntry = new FileEntry(path.fileName, path.fullPath, new FileSystem(path.fsName, fs_.root)); - - newFileEntry.file_ = new MyFile({ - size: 0, - name: newFileEntry.name, - lastModifiedDate: new Date(), - storagePath: path.storagePath - }); - - idb_.put(newFileEntry, path.storagePath, successCallback, errorCallback); - } else if (options.create === true && fileEntry) { - if (fileEntry.isFile) { - // Overwrite file, delete then create new. - idb_['delete'](path.storagePath, function() { - var newFileEntry = new FileEntry(path.fileName, path.fullPath, new FileSystem(path.fsName, fs_.root)); - - newFileEntry.file_ = new MyFile({ - size: 0, - name: newFileEntry.name, - lastModifiedDate: new Date(), - storagePath: path.storagePath - }); - - idb_.put(newFileEntry, path.storagePath, successCallback, errorCallback); - }, errorCallback); - } else { - if (errorCallback) { - errorCallback(FileError.INVALID_MODIFICATION_ERR); - } - } - } else if ((!options.create || options.create === false) && !fileEntry) { - // If create is not true and the path doesn't exist, getFile must fail. - if (errorCallback) { - errorCallback(FileError.NOT_FOUND_ERR); - } - } else if ((!options.create || options.create === false) && fileEntry && - fileEntry.isDirectory) { - // If create is not true and the path exists, but is a directory, getFile - // must fail. - if (errorCallback) { - errorCallback(FileError.TYPE_MISMATCH_ERR); - } - } else { - // Otherwise, if no other error occurs, getFile must return a FileEntry - // corresponding to path. - - successCallback(fileEntryFromIdbEntry(fileEntry)); - } - }, errorCallback); - }; - - exports.getFileMetadata = function(successCallback, errorCallback, args) { - var fullPath = args[0]; - - exports.getFile(function(fileEntry) { - successCallback(new File(fileEntry.file_.name, fileEntry.fullPath, '', fileEntry.file_.lastModifiedDate, - fileEntry.file_.size)); - }, errorCallback, [fullPath, null]); - }; - - exports.getMetadata = function(successCallback, errorCallback, args) { - exports.getFile(function (fileEntry) { - successCallback( - { - modificationTime: fileEntry.file_.lastModifiedDate, - size: fileEntry.file_.lastModifiedDate - }); - }, errorCallback, args); - }; - - exports.setMetadata = function(successCallback, errorCallback, args) { - var fullPath = args[0]; - var metadataObject = args[1]; - - exports.getFile(function (fileEntry) { - fileEntry.file_.lastModifiedDate = metadataObject.modificationTime; - idb_.put(fileEntry, fileEntry.file_.storagePath, successCallback, errorCallback); - }, errorCallback, [fullPath, null]); - }; - - exports.write = function(successCallback, errorCallback, args) { - var fileName = args[0], - data = args[1], - position = args[2], - isBinary = args[3]; // jshint ignore: line - - if (!data) { - errorCallback && errorCallback(FileError.INVALID_MODIFICATION_ERR); - return; - } - - if (typeof data === 'string' || data instanceof String) { - data = new Blob([data]); - } - - exports.getFile(function(fileEntry) { - var blob_ = fileEntry.file_.blob_; - - if (!blob_) { - blob_ = new Blob([data], {type: data.type}); - } else { - // Calc the head and tail fragments - var head = blob_.slice(0, position); - var tail = blob_.slice(position + (data.size || data.byteLength)); - - // Calc the padding - var padding = position - head.size; - if (padding < 0) { - padding = 0; - } - - // Do the "write". In fact, a full overwrite of the Blob. - blob_ = new Blob([head, new Uint8Array(padding), data, tail], - {type: data.type}); - } - - // Set the blob we're writing on this file entry so we can recall it later. - fileEntry.file_.blob_ = blob_; - fileEntry.file_.lastModifiedDate = new Date() || null; - fileEntry.file_.size = blob_.size; - fileEntry.file_.name = blob_.name; - fileEntry.file_.type = blob_.type; - - idb_.put(fileEntry, fileEntry.file_.storagePath, function() { - successCallback(data.size || data.byteLength); - }, errorCallback); - }, errorCallback, [fileName, null]); - }; - - exports.readAsText = function(successCallback, errorCallback, args) { - var fileName = args[0], - enc = args[1], - startPos = args[2], - endPos = args[3]; - - readAs('text', fileName, enc, startPos, endPos, successCallback, errorCallback); - }; - - exports.readAsDataURL = function(successCallback, errorCallback, args) { - var fileName = args[0], - startPos = args[1], - endPos = args[2]; - - readAs('dataURL', fileName, null, startPos, endPos, successCallback, errorCallback); - }; - - exports.readAsBinaryString = function(successCallback, errorCallback, args) { - var fileName = args[0], - startPos = args[1], - endPos = args[2]; - - readAs('binaryString', fileName, null, startPos, endPos, successCallback, errorCallback); - }; - - exports.readAsArrayBuffer = function(successCallback, errorCallback, args) { - var fileName = args[0], - startPos = args[1], - endPos = args[2]; - - readAs('arrayBuffer', fileName, null, startPos, endPos, successCallback, errorCallback); - }; - - exports.removeRecursively = exports.remove = function(successCallback, errorCallback, args) { - if (typeof successCallback !== 'function') { - throw Error('Expected successCallback argument.'); - } - - var fullPath = resolveToFullPath_(args[0]).storagePath; - if (fullPath === pathsPrefix.cacheDirectory || fullPath === pathsPrefix.dataDirectory) { - errorCallback(FileError.NO_MODIFICATION_ALLOWED_ERR); - return; - } - - function deleteEntry(isDirectory) { - // TODO: This doesn't protect against directories that have content in it. - // Should throw an error instead if the dirEntry is not empty. - idb_['delete'](fullPath, function() { - successCallback(); - }, function() { - if (errorCallback) { errorCallback(); } - }, isDirectory); - } - - // We need to to understand what we are deleting: - exports.getDirectory(function(entry) { - deleteEntry(entry.isDirectory); - }, function(){ - //DirectoryEntry was already deleted or entry is FileEntry - deleteEntry(false); - }, [fullPath, null, {create: false}]); - }; - - exports.getDirectory = function(successCallback, errorCallback, args) { - var fullPath = args[0]; - var path = args[1]; - var options = args[2]; - - // Create an absolute path if we were handed a relative one. - path = resolveToFullPath_(fullPath, path); - - idb_.get(path.storagePath, function(folderEntry) { - if (!options) { - options = {}; - } - - if (options.create === true && options.exclusive === true && folderEntry) { - // If create and exclusive are both true, and the path already exists, - // getDirectory must fail. - if (errorCallback) { - errorCallback(FileError.PATH_EXISTS_ERR); - } - // There is a strange bug in mobilespec + FF, which results in coming to multiple else-if's - // so we are shielding from it with returns. - return; - } - - if (options.create === true && !folderEntry) { - // If create is true, the path doesn't exist, and no other error occurs, - // getDirectory must create it as a zero-length file and return a corresponding - // MyDirectoryEntry. - var dirEntry = new DirectoryEntry(path.fileName, path.fullPath, new FileSystem(path.fsName, fs_.root)); - - idb_.put(dirEntry, path.storagePath, successCallback, errorCallback); - return; - } - - if (options.create === true && folderEntry) { - - if (folderEntry.isDirectory) { - // IDB won't save methods, so we need re-create the MyDirectoryEntry. - successCallback(new DirectoryEntry(folderEntry.name, folderEntry.fullPath, folderEntry.filesystem)); - } else { - if (errorCallback) { - errorCallback(FileError.INVALID_MODIFICATION_ERR); - } - } - return; - } - - if ((!options.create || options.create === false) && !folderEntry) { - // Handle root special. It should always exist. - if (path.fullPath === DIR_SEPARATOR) { - successCallback(fs_.root); - return; - } - - // If create is not true and the path doesn't exist, getDirectory must fail. - if (errorCallback) { - errorCallback(FileError.NOT_FOUND_ERR); - } - - return; - } - if ((!options.create || options.create === false) && folderEntry && folderEntry.isFile) { - // If create is not true and the path exists, but is a file, getDirectory - // must fail. - if (errorCallback) { - errorCallback(FileError.TYPE_MISMATCH_ERR); - } - return; - } - - // Otherwise, if no other error occurs, getDirectory must return a - // MyDirectoryEntry corresponding to path. - - // IDB won't' save methods, so we need re-create MyDirectoryEntry. - successCallback(new DirectoryEntry(folderEntry.name, folderEntry.fullPath, folderEntry.filesystem)); - }, errorCallback); - }; - - exports.getParent = function(successCallback, errorCallback, args) { - if (typeof successCallback !== 'function') { - throw Error('Expected successCallback argument.'); - } - - var fullPath = args[0]; - //fullPath is like this: - //file:///persistent/path/to/file or - //file:///persistent/path/to/directory/ - - if (fullPath === DIR_SEPARATOR || fullPath === pathsPrefix.cacheDirectory || - fullPath === pathsPrefix.dataDirectory) { - successCallback(fs_.root); - return; - } - - //To delete all slashes at the end - while (fullPath[fullPath.length - 1] === '/') { - fullPath = fullPath.substr(0, fullPath.length - 1); - } - - var pathArr = fullPath.split(DIR_SEPARATOR); - pathArr.pop(); - var parentName = pathArr.pop(); - var path = pathArr.join(DIR_SEPARATOR) + DIR_SEPARATOR; - - //To get parent of root files - var joined = path + parentName + DIR_SEPARATOR;//is like this: file:///persistent/ - if (joined === pathsPrefix.cacheDirectory || joined === pathsPrefix.dataDirectory) { - exports.getDirectory(successCallback, errorCallback, [joined, DIR_SEPARATOR, {create: false}]); - return; - } - - exports.getDirectory(successCallback, errorCallback, [path, parentName, {create: false}]); - }; - - exports.copyTo = function(successCallback, errorCallback, args) { - var srcPath = args[0]; - var parentFullPath = args[1]; - var name = args[2]; - - if (name.indexOf('/') !== -1 || srcPath === parentFullPath + name) { - if (errorCallback) { - errorCallback(FileError.INVALID_MODIFICATION_ERR); - } - - return; - } - - // Read src file - exports.getFile(function(srcFileEntry) { - - var path = resolveToFullPath_(parentFullPath); - //Check directory - exports.getDirectory(function() { - - // Create dest file - exports.getFile(function(dstFileEntry) { - - exports.write(function() { - successCallback(dstFileEntry); - }, errorCallback, [dstFileEntry.file_.storagePath, srcFileEntry.file_.blob_, 0]); - - }, errorCallback, [parentFullPath, name, {create: true}]); - - }, function() { if (errorCallback) { errorCallback(FileError.NOT_FOUND_ERR); }}, - [path.storagePath, null, {create:false}]); - - }, errorCallback, [srcPath, null]); - }; - - exports.moveTo = function(successCallback, errorCallback, args) { - var srcPath = args[0]; - // parentFullPath and name parameters is ignored because - // args is being passed downstream to exports.copyTo method - var parentFullPath = args[1]; // jshint ignore: line - var name = args[2]; // jshint ignore: line - - exports.copyTo(function (fileEntry) { - - exports.remove(function () { - successCallback(fileEntry); - }, errorCallback, [srcPath]); - - }, errorCallback, args); - }; - - exports.resolveLocalFileSystemURI = function(successCallback, errorCallback, args) { - var path = args[0]; - - // Ignore parameters - if (path.indexOf('?') !== -1) { - path = String(path).split("?")[0]; - } - - // support for encodeURI - if (/\%5/g.test(path) || /\%20/g.test(path)) { - path = decodeURI(path); - } - - if (path.trim()[0] === '/') { - errorCallback && errorCallback(FileError.ENCODING_ERR); - return; - } - - //support for cdvfile - if (path.trim().substr(0,7) === "cdvfile") { - if (path.indexOf("cdvfile://localhost") === -1) { - errorCallback && errorCallback(FileError.ENCODING_ERR); - return; - } - - var indexPersistent = path.indexOf("persistent"); - var indexTemporary = path.indexOf("temporary"); - - //cdvfile://localhost/persistent/path/to/file - if (indexPersistent !== -1) { - path = "file:///persistent" + path.substr(indexPersistent + 10); - } else if (indexTemporary !== -1) { - path = "file:///temporary" + path.substr(indexTemporary + 9); - } else { - errorCallback && errorCallback(FileError.ENCODING_ERR); - return; - } - } - - // to avoid path form of '///path/to/file' - function handlePathSlashes(path) { - var cutIndex = 0; - for (var i = 0; i < path.length - 1; i++) { - if (path[i] === DIR_SEPARATOR && path[i + 1] === DIR_SEPARATOR) { - cutIndex = i + 1; - } else break; - } - - return path.substr(cutIndex); - } - - // Handle localhost containing paths (see specs ) - if (path.indexOf('file://localhost/') === 0) { - path = path.replace('file://localhost/', 'file:///'); - } - - if (path.indexOf(pathsPrefix.dataDirectory) === 0) { - path = path.substring(pathsPrefix.dataDirectory.length - 1); - path = handlePathSlashes(path); - - exports.requestFileSystem(function() { - exports.getFile(successCallback, function() { - exports.getDirectory(successCallback, errorCallback, [pathsPrefix.dataDirectory, path, - {create: false}]); - }, [pathsPrefix.dataDirectory, path, {create: false}]); - }, errorCallback, [LocalFileSystem.PERSISTENT]); - } else if (path.indexOf(pathsPrefix.cacheDirectory) === 0) { - path = path.substring(pathsPrefix.cacheDirectory.length - 1); - path = handlePathSlashes(path); - - exports.requestFileSystem(function() { - exports.getFile(successCallback, function() { - exports.getDirectory(successCallback, errorCallback, [pathsPrefix.cacheDirectory, path, - {create: false}]); - }, [pathsPrefix.cacheDirectory, path, {create: false}]); - }, errorCallback, [LocalFileSystem.TEMPORARY]); - } else if (path.indexOf(pathsPrefix.applicationDirectory) === 0) { - path = path.substring(pathsPrefix.applicationDirectory.length); - //TODO: need to cut out redundant slashes? - - var xhr = new XMLHttpRequest(); - xhr.open("GET", path, true); - xhr.onreadystatechange = function () { - if (xhr.status === 200 && xhr.readyState === 4) { - exports.requestFileSystem(function(fs) { - fs.name = location.hostname; - - //TODO: need to call exports.getFile(...) to handle errors correct - fs.root.getFile(path, {create: true}, writeFile, errorCallback); - }, errorCallback, [LocalFileSystem.PERSISTENT]); - } - }; - - xhr.onerror = function () { - errorCallback && errorCallback(FileError.NOT_READABLE_ERR); - }; - - xhr.send(); - } else { - errorCallback && errorCallback(FileError.NOT_FOUND_ERR); - } - - function writeFile(entry) { - entry.createWriter(function (fileWriter) { - fileWriter.onwriteend = function (evt) { - if (!evt.target.error) { - entry.filesystemName = location.hostname; - successCallback(entry); - } - }; - fileWriter.onerror = function () { - errorCallback && errorCallback(FileError.NOT_READABLE_ERR); - }; - fileWriter.write(new Blob([xhr.response])); - }, errorCallback); - } - }; - - exports.requestAllPaths = function(successCallback) { - successCallback(pathsPrefix); - }; - -/*** Helpers ***/ - - /** - * Interface to wrap the native File interface. - * - * This interface is necessary for creating zero-length (empty) files, - * something the Filesystem API allows you to do. Unfortunately, File's - * constructor cannot be called directly, making it impossible to instantiate - * an empty File in JS. - * - * @param {Object} opts Initial values. - * @constructor - */ - function MyFile(opts) { - var blob_ = new Blob(); - - this.size = opts.size || 0; - this.name = opts.name || ''; - this.type = opts.type || ''; - this.lastModifiedDate = opts.lastModifiedDate || null; - this.storagePath = opts.storagePath || ''; - - // Need some black magic to correct the object's size/name/type based on the - // blob that is saved. - Object.defineProperty(this, 'blob_', { - enumerable: true, - get: function() { - return blob_; - }, - set: function(val) { - blob_ = val; - this.size = blob_.size; - this.name = blob_.name; - this.type = blob_.type; - this.lastModifiedDate = blob_.lastModifiedDate; - }.bind(this) - }); - } - - MyFile.prototype.constructor = MyFile; - - // When saving an entry, the fullPath should always lead with a slash and never - // end with one (e.g. a directory). Also, resolve '.' and '..' to an absolute - // one. This method ensures path is legit! - function resolveToFullPath_(cwdFullPath, path) { - path = path || ''; - var fullPath = path; - var prefix = ''; - - cwdFullPath = cwdFullPath || DIR_SEPARATOR; - if (cwdFullPath.indexOf(FILESYSTEM_PREFIX) === 0) { - prefix = cwdFullPath.substring(0, cwdFullPath.indexOf(DIR_SEPARATOR, FILESYSTEM_PREFIX.length)); - cwdFullPath = cwdFullPath.substring(cwdFullPath.indexOf(DIR_SEPARATOR, FILESYSTEM_PREFIX.length)); - } - - var relativePath = path[0] !== DIR_SEPARATOR; - if (relativePath) { - fullPath = cwdFullPath; - if (cwdFullPath !== DIR_SEPARATOR) { - fullPath += DIR_SEPARATOR + path; - } else { - fullPath += path; - } - } - - // Remove doubled separator substrings - var re = new RegExp(DIR_SEPARATOR + DIR_SEPARATOR, 'g'); - fullPath = fullPath.replace(re, DIR_SEPARATOR); - - // Adjust '..'s by removing parent directories when '..' flows in path. - var parts = fullPath.split(DIR_SEPARATOR); - for (var i = 0; i < parts.length; ++i) { - var part = parts[i]; - if (part === '..') { - parts[i - 1] = ''; - parts[i] = ''; - } - } - fullPath = parts.filter(function(el) { - return el; - }).join(DIR_SEPARATOR); - - // Add back in leading slash. - if (fullPath[0] !== DIR_SEPARATOR) { - fullPath = DIR_SEPARATOR + fullPath; - } - - // Replace './' by current dir. ('./one/./two' -> one/two) - fullPath = fullPath.replace(/\.\//g, DIR_SEPARATOR); - - // Replace '//' with '/'. - fullPath = fullPath.replace(/\/\//g, DIR_SEPARATOR); - - // Replace '/.' with '/'. - fullPath = fullPath.replace(/\/\./g, DIR_SEPARATOR); - - // Remove '/' if it appears on the end. - if (fullPath[fullPath.length - 1] === DIR_SEPARATOR && - fullPath !== DIR_SEPARATOR) { - fullPath = fullPath.substring(0, fullPath.length - 1); - } - - var storagePath = prefix + fullPath; - storagePath = decodeURI(storagePath); - fullPath = decodeURI(fullPath); - - return { - storagePath: storagePath, - fullPath: fullPath, - fileName: fullPath.split(DIR_SEPARATOR).pop(), - fsName: prefix.split(DIR_SEPARATOR).pop() - }; - } - - function fileEntryFromIdbEntry(fileEntry) { - // IDB won't save methods, so we need re-create the FileEntry. - var clonedFileEntry = new FileEntry(fileEntry.name, fileEntry.fullPath, fileEntry.filesystem); - clonedFileEntry.file_ = fileEntry.file_; - - return clonedFileEntry; - } - - function readAs(what, fullPath, encoding, startPos, endPos, successCallback, errorCallback) { - exports.getFile(function(fileEntry) { - var fileReader = new FileReader(), - blob = fileEntry.file_.blob_.slice(startPos, endPos); - - fileReader.onload = function(e) { - successCallback(e.target.result); - }; - - fileReader.onerror = errorCallback; - - switch (what) { - case 'text': - fileReader.readAsText(blob, encoding); - break; - case 'dataURL': - fileReader.readAsDataURL(blob); - break; - case 'arrayBuffer': - fileReader.readAsArrayBuffer(blob); - break; - case 'binaryString': - fileReader.readAsBinaryString(blob); - break; - } - - }, errorCallback, [fullPath, null]); - } - -/*** Core logic to handle IDB operations ***/ - - idb_.open = function(dbName, successCallback, errorCallback) { - var self = this; - - // TODO: FF 12.0a1 isn't liking a db name with : in it. - var request = indexedDB.open(dbName.replace(':', '_')/*, 1 /*version*/); - - request.onerror = errorCallback || onError; - - request.onupgradeneeded = function(e) { - // First open was called or higher db version was used. - - // console.log('onupgradeneeded: oldVersion:' + e.oldVersion, - // 'newVersion:' + e.newVersion); - - self.db = e.target.result; - self.db.onerror = onError; - - if (!self.db.objectStoreNames.contains(FILE_STORE_)) { - self.db.createObjectStore(FILE_STORE_/*,{keyPath: 'id', autoIncrement: true}*/); - } - }; - - request.onsuccess = function(e) { - self.db = e.target.result; - self.db.onerror = onError; - successCallback(e); - }; - - request.onblocked = errorCallback || onError; - }; - - idb_.close = function() { - this.db.close(); - this.db = null; - }; - - idb_.get = function(fullPath, successCallback, errorCallback) { - if (!this.db) { - errorCallback && errorCallback(FileError.INVALID_MODIFICATION_ERR); - return; - } - - var tx = this.db.transaction([FILE_STORE_], 'readonly'); - - var request = tx.objectStore(FILE_STORE_).get(fullPath); - - tx.onabort = errorCallback || onError; - tx.oncomplete = function() { - successCallback(request.result); - }; - }; - - idb_.getAllEntries = function(fullPath, storagePath, successCallback, errorCallback) { - if (!this.db) { - errorCallback && errorCallback(FileError.INVALID_MODIFICATION_ERR); - return; - } - - var results = []; - - if (storagePath[storagePath.length - 1] === DIR_SEPARATOR) { - storagePath = storagePath.substring(0, storagePath.length - 1); - } - - var range = IDBKeyRange.bound(storagePath + DIR_SEPARATOR + ' ', - storagePath + DIR_SEPARATOR + String.fromCharCode(unicodeLastChar)); - - var tx = this.db.transaction([FILE_STORE_], 'readonly'); - tx.onabort = errorCallback || onError; - tx.oncomplete = function() { - results = results.filter(function(val) { - var pathWithoutSlash = val.fullPath; - - if (val.fullPath[val.fullPath.length - 1] === DIR_SEPARATOR) { - pathWithoutSlash = pathWithoutSlash.substr(0, pathWithoutSlash.length - 1); - } - - var valPartsLen = pathWithoutSlash.split(DIR_SEPARATOR).length; - var fullPathPartsLen = fullPath.split(DIR_SEPARATOR).length; - - /* Input fullPath parameter equals '//' for root folder */ - /* Entries in root folder has valPartsLen equals 2 (see below) */ - if (fullPath[fullPath.length -1] === DIR_SEPARATOR && fullPath.trim().length === 2) { - fullPathPartsLen = 1; - } else if (fullPath[fullPath.length -1] === DIR_SEPARATOR) { - fullPathPartsLen = fullPath.substr(0, fullPath.length - 1).split(DIR_SEPARATOR).length; - } else { - fullPathPartsLen = fullPath.split(DIR_SEPARATOR).length; - } - - if (valPartsLen === fullPathPartsLen + 1) { - // If this a subfolder and entry is a direct child, include it in - // the results. Otherwise, it's not an entry of this folder. - return val; - } else return false; - }); - - successCallback(results); - }; - - var request = tx.objectStore(FILE_STORE_).openCursor(range); - - request.onsuccess = function(e) { - var cursor = e.target.result; - if (cursor) { - var val = cursor.value; - - results.push(val.isFile ? fileEntryFromIdbEntry(val) : new DirectoryEntry(val.name, val.fullPath, val.filesystem)); - cursor['continue'](); - } - }; - }; - - idb_['delete'] = function(fullPath, successCallback, errorCallback, isDirectory) { - if (!idb_.db) { - errorCallback && errorCallback(FileError.INVALID_MODIFICATION_ERR); - return; - } - - var tx = this.db.transaction([FILE_STORE_], 'readwrite'); - tx.oncomplete = successCallback; - tx.onabort = errorCallback || onError; - tx.oncomplete = function() { - if (isDirectory) { - //We delete nested files and folders after deleting parent folder - //We use ranges: https://developer.mozilla.org/en-US/docs/Web/API/IDBKeyRange - fullPath = fullPath + DIR_SEPARATOR; - - //Range contains all entries in the form fullPath<symbol> where - //symbol in the range from ' ' to symbol which has code `unicodeLastChar` - var range = IDBKeyRange.bound(fullPath + ' ', fullPath + String.fromCharCode(unicodeLastChar)); - - var newTx = this.db.transaction([FILE_STORE_], 'readwrite'); - newTx.oncomplete = successCallback; - newTx.onabort = errorCallback || onError; - newTx.objectStore(FILE_STORE_)['delete'](range); - } else { - successCallback(); - } - }; - tx.objectStore(FILE_STORE_)['delete'](fullPath); - }; - - idb_.put = function(entry, storagePath, successCallback, errorCallback) { - if (!this.db) { - errorCallback && errorCallback(FileError.INVALID_MODIFICATION_ERR); - return; - } - - var tx = this.db.transaction([FILE_STORE_], 'readwrite'); - tx.onabort = errorCallback || onError; - tx.oncomplete = function() { - // TODO: Error is thrown if we pass the request event back instead. - successCallback(entry); - }; - - tx.objectStore(FILE_STORE_).put(entry, storagePath); - }; - - // Global error handler. Errors bubble from request, to transaction, to db. - function onError(e) { - switch (e.target.errorCode) { - case 12: - console.log('Error - Attempt to open db with a lower version than the ' + - 'current one.'); - break; - default: - console.log('errorCode: ' + e.target.errorCode); - } - - console.log(e, e.code, e.message); - } - -})(module.exports, window); - -require("cordova/exec/proxy").add("File", module.exports); diff --git a/plugins/cordova-plugin-file/src/firefoxos/FileProxy.js b/plugins/cordova-plugin-file/src/firefoxos/FileProxy.js deleted file mode 100644 index 340ae4bc..00000000 --- a/plugins/cordova-plugin-file/src/firefoxos/FileProxy.js +++ /dev/null @@ -1,785 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - * - */ - -var LocalFileSystem = require('./LocalFileSystem'), - FileSystem = require('./FileSystem'), - FileEntry = require('./FileEntry'), - FileError = require('./FileError'), - DirectoryEntry = require('./DirectoryEntry'), - File = require('./File'); - -/* -QUIRKS: - Does not fail when removing non-empty directories - Does not support metadata for directories - Does not support requestAllFileSystems - Does not support resolveLocalFileSystemURI - Methods copyTo and moveTo do not support directories - - Heavily based on https://github.com/ebidel/idb.filesystem.js - */ - - -(function(exports, global) { - var indexedDB = global.indexedDB || global.mozIndexedDB; - if (!indexedDB) { - throw "Firefox OS File plugin: indexedDB not supported"; - } - - var fs_ = null; - - var idb_ = {}; - idb_.db = null; - var FILE_STORE_ = 'entries'; - - var DIR_SEPARATOR = '/'; - var DIR_OPEN_BOUND = String.fromCharCode(DIR_SEPARATOR.charCodeAt(0) + 1); - - var pathsPrefix = { - // Read-only directory where the application is installed. - applicationDirectory: location.origin + "/", - // Where to put app-specific data files. - dataDirectory: 'file:///persistent/', - // Cached files that should survive app restarts. - // Apps should not rely on the OS to delete files in here. - cacheDirectory: 'file:///temporary/', - }; - -/*** Exported functionality ***/ - - exports.requestFileSystem = function(successCallback, errorCallback, args) { - var type = args[0]; - var size = args[1]; - - if (type !== LocalFileSystem.TEMPORARY && type !== LocalFileSystem.PERSISTENT) { - errorCallback && errorCallback(FileError.INVALID_MODIFICATION_ERR); - return; - } - - var name = type === LocalFileSystem.TEMPORARY ? 'temporary' : 'persistent'; - var storageName = (location.protocol + location.host).replace(/:/g, '_'); - - var root = new DirectoryEntry('', DIR_SEPARATOR); - fs_ = new FileSystem(name, root); - - idb_.open(storageName, function() { - successCallback(fs_); - }, errorCallback); - }; - - require('./fileSystems').getFs = function(name, callback) { - callback(new FileSystem(name, fs_.root)); - }; - - // list a directory's contents (files and folders). - exports.readEntries = function(successCallback, errorCallback, args) { - var fullPath = args[0]; - - if (!successCallback) { - throw Error('Expected successCallback argument.'); - } - - var path = resolveToFullPath_(fullPath); - - idb_.getAllEntries(path.fullPath, path.storagePath, function(entries) { - successCallback(entries); - }, errorCallback); - }; - - exports.getFile = function(successCallback, errorCallback, args) { - var fullPath = args[0]; - var path = args[1]; - var options = args[2] || {}; - - // Create an absolute path if we were handed a relative one. - path = resolveToFullPath_(fullPath, path); - - idb_.get(path.storagePath, function(fileEntry) { - if (options.create === true && options.exclusive === true && fileEntry) { - // If create and exclusive are both true, and the path already exists, - // getFile must fail. - - if (errorCallback) { - errorCallback(FileError.PATH_EXISTS_ERR); - } - } else if (options.create === true && !fileEntry) { - // If create is true, the path doesn't exist, and no other error occurs, - // getFile must create it as a zero-length file and return a corresponding - // FileEntry. - var newFileEntry = new FileEntry(path.fileName, path.fullPath, new FileSystem(path.fsName, fs_.root)); - - newFileEntry.file_ = new MyFile({ - size: 0, - name: newFileEntry.name, - lastModifiedDate: new Date(), - storagePath: path.storagePath - }); - - idb_.put(newFileEntry, path.storagePath, successCallback, errorCallback); - } else if (options.create === true && fileEntry) { - if (fileEntry.isFile) { - // Overwrite file, delete then create new. - idb_['delete'](path.storagePath, function() { - var newFileEntry = new FileEntry(path.fileName, path.fullPath, new FileSystem(path.fsName, fs_.root)); - - newFileEntry.file_ = new MyFile({ - size: 0, - name: newFileEntry.name, - lastModifiedDate: new Date(), - storagePath: path.storagePath - }); - - idb_.put(newFileEntry, path.storagePath, successCallback, errorCallback); - }, errorCallback); - } else { - if (errorCallback) { - errorCallback(FileError.INVALID_MODIFICATION_ERR); - } - } - } else if ((!options.create || options.create === false) && !fileEntry) { - // If create is not true and the path doesn't exist, getFile must fail. - if (errorCallback) { - errorCallback(FileError.NOT_FOUND_ERR); - } - } else if ((!options.create || options.create === false) && fileEntry && - fileEntry.isDirectory) { - // If create is not true and the path exists, but is a directory, getFile - // must fail. - if (errorCallback) { - errorCallback(FileError.INVALID_MODIFICATION_ERR); - } - } else { - // Otherwise, if no other error occurs, getFile must return a FileEntry - // corresponding to path. - - successCallback(fileEntryFromIdbEntry(fileEntry)); - } - }, errorCallback); - }; - - exports.getFileMetadata = function(successCallback, errorCallback, args) { - var fullPath = args[0]; - - exports.getFile(function(fileEntry) { - successCallback(new File(fileEntry.file_.name, fileEntry.fullPath, '', fileEntry.file_.lastModifiedDate, - fileEntry.file_.size)); - }, errorCallback, [fullPath, null]); - }; - - exports.getMetadata = function(successCallback, errorCallback, args) { - exports.getFile(function (fileEntry) { - successCallback( - { - modificationTime: fileEntry.file_.lastModifiedDate, - size: fileEntry.file_.lastModifiedDate - }); - }, errorCallback, args); - }; - - exports.setMetadata = function(successCallback, errorCallback, args) { - var fullPath = args[0]; - var metadataObject = args[1]; - - exports.getFile(function (fileEntry) { - fileEntry.file_.lastModifiedDate = metadataObject.modificationTime; - }, errorCallback, [fullPath, null]); - }; - - exports.write = function(successCallback, errorCallback, args) { - var fileName = args[0], - data = args[1], - position = args[2], - isBinary = args[3]; - - if (!data) { - errorCallback && errorCallback(FileError.INVALID_MODIFICATION_ERR); - return; - } - - exports.getFile(function(fileEntry) { - var blob_ = fileEntry.file_.blob_; - - if (!blob_) { - blob_ = new Blob([data], {type: data.type}); - } else { - // Calc the head and tail fragments - var head = blob_.slice(0, position); - var tail = blob_.slice(position + data.byteLength); - - // Calc the padding - var padding = position - head.size; - if (padding < 0) { - padding = 0; - } - - // Do the "write". In fact, a full overwrite of the Blob. - blob_ = new Blob([head, new Uint8Array(padding), data, tail], - {type: data.type}); - } - - // Set the blob we're writing on this file entry so we can recall it later. - fileEntry.file_.blob_ = blob_; - fileEntry.file_.lastModifiedDate = data.lastModifiedDate || null; - fileEntry.file_.size = blob_.size; - fileEntry.file_.name = blob_.name; - fileEntry.file_.type = blob_.type; - - idb_.put(fileEntry, fileEntry.file_.storagePath, function() { - successCallback(data.byteLength); - }, errorCallback); - }, errorCallback, [fileName, null]); - }; - - exports.readAsText = function(successCallback, errorCallback, args) { - var fileName = args[0], - enc = args[1], - startPos = args[2], - endPos = args[3]; - - readAs('text', fileName, enc, startPos, endPos, successCallback, errorCallback); - }; - - exports.readAsDataURL = function(successCallback, errorCallback, args) { - var fileName = args[0], - startPos = args[1], - endPos = args[2]; - - readAs('dataURL', fileName, null, startPos, endPos, successCallback, errorCallback); - }; - - exports.readAsBinaryString = function(successCallback, errorCallback, args) { - var fileName = args[0], - startPos = args[1], - endPos = args[2]; - - readAs('binaryString', fileName, null, startPos, endPos, successCallback, errorCallback); - }; - - exports.readAsArrayBuffer = function(successCallback, errorCallback, args) { - var fileName = args[0], - startPos = args[1], - endPos = args[2]; - - readAs('arrayBuffer', fileName, null, startPos, endPos, successCallback, errorCallback); - }; - - exports.removeRecursively = exports.remove = function(successCallback, errorCallback, args) { - var fullPath = args[0]; - - // TODO: This doesn't protect against directories that have content in it. - // Should throw an error instead if the dirEntry is not empty. - idb_['delete'](fullPath, function() { - successCallback(); - }, errorCallback); - }; - - exports.getDirectory = function(successCallback, errorCallback, args) { - var fullPath = args[0]; - var path = args[1]; - var options = args[2]; - - // Create an absolute path if we were handed a relative one. - path = resolveToFullPath_(fullPath, path); - - idb_.get(path.storagePath, function(folderEntry) { - if (!options) { - options = {}; - } - - if (options.create === true && options.exclusive === true && folderEntry) { - // If create and exclusive are both true, and the path already exists, - // getDirectory must fail. - if (errorCallback) { - errorCallback(FileError.INVALID_MODIFICATION_ERR); - } - } else if (options.create === true && !folderEntry) { - // If create is true, the path doesn't exist, and no other error occurs, - // getDirectory must create it as a zero-length file and return a corresponding - // MyDirectoryEntry. - var dirEntry = new DirectoryEntry(path.fileName, path.fullPath, new FileSystem(path.fsName, fs_.root)); - - idb_.put(dirEntry, path.storagePath, successCallback, errorCallback); - } else if (options.create === true && folderEntry) { - - if (folderEntry.isDirectory) { - // IDB won't save methods, so we need re-create the MyDirectoryEntry. - successCallback(new DirectoryEntry(folderEntry.name, folderEntry.fullPath, folderEntry.fileSystem)); - } else { - if (errorCallback) { - errorCallback(FileError.INVALID_MODIFICATION_ERR); - } - } - } else if ((!options.create || options.create === false) && !folderEntry) { - // Handle root special. It should always exist. - if (path.fullPath === DIR_SEPARATOR) { - successCallback(fs_.root); - return; - } - - // If create is not true and the path doesn't exist, getDirectory must fail. - if (errorCallback) { - errorCallback(FileError.NOT_FOUND_ERR); - } - } else if ((!options.create || options.create === false) && folderEntry && - folderEntry.isFile) { - // If create is not true and the path exists, but is a file, getDirectory - // must fail. - if (errorCallback) { - errorCallback(FileError.INVALID_MODIFICATION_ERR); - } - } else { - // Otherwise, if no other error occurs, getDirectory must return a - // MyDirectoryEntry corresponding to path. - - // IDB won't' save methods, so we need re-create MyDirectoryEntry. - successCallback(new DirectoryEntry(folderEntry.name, folderEntry.fullPath, folderEntry.fileSystem)); - } - }, errorCallback); - }; - - exports.getParent = function(successCallback, errorCallback, args) { - var fullPath = args[0]; - - if (fullPath === DIR_SEPARATOR) { - successCallback(fs_.root); - return; - } - - var pathArr = fullPath.split(DIR_SEPARATOR); - pathArr.pop(); - var namesa = pathArr.pop(); - var path = pathArr.join(DIR_SEPARATOR); - - exports.getDirectory(successCallback, errorCallback, [path, namesa, {create: false}]); - }; - - exports.copyTo = function(successCallback, errorCallback, args) { - var srcPath = args[0]; - var parentFullPath = args[1]; - var name = args[2]; - - // Read src file - exports.getFile(function(srcFileEntry) { - - // Create dest file - exports.getFile(function(dstFileEntry) { - - exports.write(function() { - successCallback(dstFileEntry); - }, errorCallback, [dstFileEntry.file_.storagePath, srcFileEntry.file_.blob_, 0]); - - }, errorCallback, [parentFullPath, name, {create: true}]); - - }, errorCallback, [srcPath, null]); - }; - - exports.moveTo = function(successCallback, errorCallback, args) { - var srcPath = args[0]; - var parentFullPath = args[1]; - var name = args[2]; - - exports.copyTo(function (fileEntry) { - - exports.remove(function () { - successCallback(fileEntry); - }, errorCallback, [srcPath]); - - }, errorCallback, args); - }; - - exports.resolveLocalFileSystemURI = function(successCallback, errorCallback, args) { - var path = args[0]; - - // Ignore parameters - if (path.indexOf('?') !== -1) { - path = String(path).split("?")[0]; - } - - // support for encodeURI - if (/\%5/g.test(path)) { - path = decodeURI(path); - } - - if (path.indexOf(pathsPrefix.dataDirectory) === 0) { - path = path.substring(pathsPrefix.dataDirectory.length - 1); - - exports.requestFileSystem(function(fs) { - fs.root.getFile(path, {create: false}, successCallback, function() { - fs.root.getDirectory(path, {create: false}, successCallback, errorCallback); - }); - }, errorCallback, [LocalFileSystem.PERSISTENT]); - } else if (path.indexOf(pathsPrefix.cacheDirectory) === 0) { - path = path.substring(pathsPrefix.cacheDirectory.length - 1); - - exports.requestFileSystem(function(fs) { - fs.root.getFile(path, {create: false}, successCallback, function() { - fs.root.getDirectory(path, {create: false}, successCallback, errorCallback); - }); - }, errorCallback, [LocalFileSystem.TEMPORARY]); - } else if (path.indexOf(pathsPrefix.applicationDirectory) === 0) { - path = path.substring(pathsPrefix.applicationDirectory.length); - - var xhr = new XMLHttpRequest(); - xhr.open("GET", path, true); - xhr.onreadystatechange = function () { - if (xhr.status === 200 && xhr.readyState === 4) { - exports.requestFileSystem(function(fs) { - fs.name = location.hostname; - fs.root.getFile(path, {create: true}, writeFile, errorCallback); - }, errorCallback, [LocalFileSystem.PERSISTENT]); - } - }; - - xhr.onerror = function () { - errorCallback && errorCallback(FileError.NOT_READABLE_ERR); - }; - - xhr.send(); - } else { - errorCallback && errorCallback(FileError.NOT_FOUND_ERR); - } - - function writeFile(entry) { - entry.createWriter(function (fileWriter) { - fileWriter.onwriteend = function (evt) { - if (!evt.target.error) { - entry.filesystemName = location.hostname; - successCallback(entry); - } - }; - fileWriter.onerror = function () { - errorCallback && errorCallback(FileError.NOT_READABLE_ERR); - }; - fileWriter.write(new Blob([xhr.response])); - }, errorCallback); - } - }; - - exports.requestAllPaths = function(successCallback) { - successCallback(pathsPrefix); - }; - -/*** Helpers ***/ - - /** - * Interface to wrap the native File interface. - * - * This interface is necessary for creating zero-length (empty) files, - * something the Filesystem API allows you to do. Unfortunately, File's - * constructor cannot be called directly, making it impossible to instantiate - * an empty File in JS. - * - * @param {Object} opts Initial values. - * @constructor - */ - function MyFile(opts) { - var blob_ = new Blob(); - - this.size = opts.size || 0; - this.name = opts.name || ''; - this.type = opts.type || ''; - this.lastModifiedDate = opts.lastModifiedDate || null; - this.storagePath = opts.storagePath || ''; - - // Need some black magic to correct the object's size/name/type based on the - // blob that is saved. - Object.defineProperty(this, 'blob_', { - enumerable: true, - get: function() { - return blob_; - }, - set: function(val) { - blob_ = val; - this.size = blob_.size; - this.name = blob_.name; - this.type = blob_.type; - this.lastModifiedDate = blob_.lastModifiedDate; - }.bind(this) - }); - } - - MyFile.prototype.constructor = MyFile; - - // When saving an entry, the fullPath should always lead with a slash and never - // end with one (e.g. a directory). Also, resolve '.' and '..' to an absolute - // one. This method ensures path is legit! - function resolveToFullPath_(cwdFullPath, path) { - path = path || ''; - var fullPath = path; - var prefix = ''; - - cwdFullPath = cwdFullPath || DIR_SEPARATOR; - if (cwdFullPath.indexOf(FILESYSTEM_PREFIX) === 0) { - prefix = cwdFullPath.substring(0, cwdFullPath.indexOf(DIR_SEPARATOR, FILESYSTEM_PREFIX.length)); - cwdFullPath = cwdFullPath.substring(cwdFullPath.indexOf(DIR_SEPARATOR, FILESYSTEM_PREFIX.length)); - } - - var relativePath = path[0] !== DIR_SEPARATOR; - if (relativePath) { - fullPath = cwdFullPath; - if (cwdFullPath != DIR_SEPARATOR) { - fullPath += DIR_SEPARATOR + path; - } else { - fullPath += path; - } - } - - // Adjust '..'s by removing parent directories when '..' flows in path. - var parts = fullPath.split(DIR_SEPARATOR); - for (var i = 0; i < parts.length; ++i) { - var part = parts[i]; - if (part == '..') { - parts[i - 1] = ''; - parts[i] = ''; - } - } - fullPath = parts.filter(function(el) { - return el; - }).join(DIR_SEPARATOR); - - // Add back in leading slash. - if (fullPath[0] !== DIR_SEPARATOR) { - fullPath = DIR_SEPARATOR + fullPath; - } - - // Replace './' by current dir. ('./one/./two' -> one/two) - fullPath = fullPath.replace(/\.\//g, DIR_SEPARATOR); - - // Replace '//' with '/'. - fullPath = fullPath.replace(/\/\//g, DIR_SEPARATOR); - - // Replace '/.' with '/'. - fullPath = fullPath.replace(/\/\./g, DIR_SEPARATOR); - - // Remove '/' if it appears on the end. - if (fullPath[fullPath.length - 1] == DIR_SEPARATOR && - fullPath != DIR_SEPARATOR) { - fullPath = fullPath.substring(0, fullPath.length - 1); - } - - return { - storagePath: prefix + fullPath, - fullPath: fullPath, - fileName: fullPath.split(DIR_SEPARATOR).pop(), - fsName: prefix.split(DIR_SEPARATOR).pop() - }; - } - - function fileEntryFromIdbEntry(fileEntry) { - // IDB won't save methods, so we need re-create the FileEntry. - var clonedFileEntry = new FileEntry(fileEntry.name, fileEntry.fullPath, fileEntry.fileSystem); - clonedFileEntry.file_ = fileEntry.file_; - - return clonedFileEntry; - } - - function readAs(what, fullPath, encoding, startPos, endPos, successCallback, errorCallback) { - exports.getFile(function(fileEntry) { - var fileReader = new FileReader(), - blob = fileEntry.file_.blob_.slice(startPos, endPos); - - fileReader.onload = function(e) { - successCallback(e.target.result); - }; - - fileReader.onerror = errorCallback; - - switch (what) { - case 'text': - fileReader.readAsText(blob, encoding); - break; - case 'dataURL': - fileReader.readAsDataURL(blob); - break; - case 'arrayBuffer': - fileReader.readAsArrayBuffer(blob); - break; - case 'binaryString': - fileReader.readAsBinaryString(blob); - break; - } - - }, errorCallback, [fullPath, null]); - } - -/*** Core logic to handle IDB operations ***/ - - idb_.open = function(dbName, successCallback, errorCallback) { - var self = this; - - // TODO: FF 12.0a1 isn't liking a db name with : in it. - var request = indexedDB.open(dbName.replace(':', '_')/*, 1 /*version*/); - - request.onerror = errorCallback || onError; - - request.onupgradeneeded = function(e) { - // First open was called or higher db version was used. - - // console.log('onupgradeneeded: oldVersion:' + e.oldVersion, - // 'newVersion:' + e.newVersion); - - self.db = e.target.result; - self.db.onerror = onError; - - if (!self.db.objectStoreNames.contains(FILE_STORE_)) { - var store = self.db.createObjectStore(FILE_STORE_/*,{keyPath: 'id', autoIncrement: true}*/); - } - }; - - request.onsuccess = function(e) { - self.db = e.target.result; - self.db.onerror = onError; - successCallback(e); - }; - - request.onblocked = errorCallback || onError; - }; - - idb_.close = function() { - this.db.close(); - this.db = null; - }; - - idb_.get = function(fullPath, successCallback, errorCallback) { - if (!this.db) { - errorCallback && errorCallback(FileError.INVALID_MODIFICATION_ERR); - return; - } - - var tx = this.db.transaction([FILE_STORE_], 'readonly'); - - //var request = tx.objectStore(FILE_STORE_).get(fullPath); - var range = IDBKeyRange.bound(fullPath, fullPath + DIR_OPEN_BOUND, - false, true); - var request = tx.objectStore(FILE_STORE_).get(range); - - tx.onabort = errorCallback || onError; - tx.oncomplete = function(e) { - successCallback(request.result); - }; - }; - - idb_.getAllEntries = function(fullPath, storagePath, successCallback, errorCallback) { - if (!this.db) { - errorCallback && errorCallback(FileError.INVALID_MODIFICATION_ERR); - return; - } - - var results = []; - - if (storagePath[storagePath.length - 1] === DIR_SEPARATOR) { - storagePath = storagePath.substring(0, storagePath.length - 1); - } - - range = IDBKeyRange.bound( - storagePath + DIR_SEPARATOR, storagePath + DIR_OPEN_BOUND, false, true); - - var tx = this.db.transaction([FILE_STORE_], 'readonly'); - tx.onabort = errorCallback || onError; - tx.oncomplete = function(e) { - results = results.filter(function(val) { - var valPartsLen = val.fullPath.split(DIR_SEPARATOR).length; - var fullPathPartsLen = fullPath.split(DIR_SEPARATOR).length; - - if (fullPath === DIR_SEPARATOR && valPartsLen < fullPathPartsLen + 1) { - // Hack to filter out entries in the root folder. This is inefficient - // because reading the entires of fs.root (e.g. '/') returns ALL - // results in the database, then filters out the entries not in '/'. - return val; - } else if (fullPath !== DIR_SEPARATOR && - valPartsLen === fullPathPartsLen + 1) { - // If this a subfolder and entry is a direct child, include it in - // the results. Otherwise, it's not an entry of this folder. - return val; - } - }); - - successCallback(results); - }; - - var request = tx.objectStore(FILE_STORE_).openCursor(range); - - request.onsuccess = function(e) { - var cursor = e.target.result; - if (cursor) { - var val = cursor.value; - - results.push(val.isFile ? fileEntryFromIdbEntry(val) : new DirectoryEntry(val.name, val.fullPath, val.fileSystem)); - cursor['continue'](); - } - }; - }; - - idb_['delete'] = function(fullPath, successCallback, errorCallback) { - if (!this.db) { - errorCallback && errorCallback(FileError.INVALID_MODIFICATION_ERR); - return; - } - - var tx = this.db.transaction([FILE_STORE_], 'readwrite'); - tx.oncomplete = successCallback; - tx.onabort = errorCallback || onError; - - //var request = tx.objectStore(FILE_STORE_).delete(fullPath); - var range = IDBKeyRange.bound( - fullPath, fullPath + DIR_OPEN_BOUND, false, true); - tx.objectStore(FILE_STORE_)['delete'](range); - }; - - idb_.put = function(entry, storagePath, successCallback, errorCallback) { - if (!this.db) { - errorCallback && errorCallback(FileError.INVALID_MODIFICATION_ERR); - return; - } - - var tx = this.db.transaction([FILE_STORE_], 'readwrite'); - tx.onabort = errorCallback || onError; - tx.oncomplete = function(e) { - // TODO: Error is thrown if we pass the request event back instead. - successCallback(entry); - }; - - tx.objectStore(FILE_STORE_).put(entry, storagePath); - }; - - // Global error handler. Errors bubble from request, to transaction, to db. - function onError(e) { - switch (e.target.errorCode) { - case 12: - console.log('Error - Attempt to open db with a lower version than the ' + - 'current one.'); - break; - default: - console.log('errorCode: ' + e.target.errorCode); - } - - console.log(e, e.code, e.message); - } - -// Clean up. -// TODO: Is there a place for this? -// global.addEventListener('beforeunload', function(e) { -// idb_.db && idb_.db.close(); -// }, false); - -})(module.exports, window); - -require("cordova/exec/proxy").add("File", module.exports); diff --git a/plugins/cordova-plugin-file/src/ios/CDVAssetLibraryFilesystem.h b/plugins/cordova-plugin-file/src/ios/CDVAssetLibraryFilesystem.h deleted file mode 100644 index e09e2250..00000000 --- a/plugins/cordova-plugin-file/src/ios/CDVAssetLibraryFilesystem.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you 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. - */ - -#import "CDVFile.h" - -extern NSString* const kCDVAssetsLibraryPrefix; -extern NSString* const kCDVAssetsLibraryScheme; - -@interface CDVAssetLibraryFilesystem : NSObject<CDVFileSystem> { -} - -- (id) initWithName:(NSString *)name; - -@end diff --git a/plugins/cordova-plugin-file/src/ios/CDVAssetLibraryFilesystem.m b/plugins/cordova-plugin-file/src/ios/CDVAssetLibraryFilesystem.m deleted file mode 100644 index 0b95fac3..00000000 --- a/plugins/cordova-plugin-file/src/ios/CDVAssetLibraryFilesystem.m +++ /dev/null @@ -1,253 +0,0 @@ -/* - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you 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. - */ - -#import "CDVFile.h" -#import "CDVAssetLibraryFilesystem.h" -#import <Cordova/CDV.h> -#import <AssetsLibrary/ALAsset.h> -#import <AssetsLibrary/ALAssetRepresentation.h> -#import <AssetsLibrary/ALAssetsLibrary.h> -#import <MobileCoreServices/MobileCoreServices.h> - -NSString* const kCDVAssetsLibraryPrefix = @"assets-library://"; -NSString* const kCDVAssetsLibraryScheme = @"assets-library"; - -@implementation CDVAssetLibraryFilesystem -@synthesize name=_name, urlTransformer; - - -/* - The CDVAssetLibraryFilesystem works with resources which are identified - by iOS as - asset-library://<path> - and represents them internally as URLs of the form - cdvfile://localhost/assets-library/<path> - */ - -- (NSURL *)assetLibraryURLForLocalURL:(CDVFilesystemURL *)url -{ - if ([url.url.scheme isEqualToString:kCDVFilesystemURLPrefix]) { - NSString *path = [[url.url absoluteString] substringFromIndex:[@"cdvfile://localhost/assets-library" length]]; - return [NSURL URLWithString:[NSString stringWithFormat:@"assets-library:/%@", path]]; - } - return url.url; -} - -- (CDVPluginResult *)entryForLocalURI:(CDVFilesystemURL *)url -{ - NSDictionary* entry = [self makeEntryForLocalURL:url]; - return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:entry]; -} - -- (NSDictionary *)makeEntryForLocalURL:(CDVFilesystemURL *)url { - return [self makeEntryForPath:url.fullPath isDirectory:NO]; -} - -- (NSDictionary*)makeEntryForPath:(NSString*)fullPath isDirectory:(BOOL)isDir -{ - NSMutableDictionary* dirEntry = [NSMutableDictionary dictionaryWithCapacity:5]; - NSString* lastPart = [fullPath lastPathComponent]; - if (isDir && ![fullPath hasSuffix:@"/"]) { - fullPath = [fullPath stringByAppendingString:@"/"]; - } - [dirEntry setObject:[NSNumber numberWithBool:!isDir] forKey:@"isFile"]; - [dirEntry setObject:[NSNumber numberWithBool:isDir] forKey:@"isDirectory"]; - [dirEntry setObject:fullPath forKey:@"fullPath"]; - [dirEntry setObject:lastPart forKey:@"name"]; - [dirEntry setObject:self.name forKey: @"filesystemName"]; - - NSURL* nativeURL = [NSURL URLWithString:[NSString stringWithFormat:@"assets-library:/%@",fullPath]]; - if (self.urlTransformer) { - nativeURL = self.urlTransformer(nativeURL); - } - dirEntry[@"nativeURL"] = [nativeURL absoluteString]; - - return dirEntry; -} - -/* helper function to get the mimeType from the file extension - * IN: - * NSString* fullPath - filename (may include path) - * OUT: - * NSString* the mime type as type/subtype. nil if not able to determine - */ -+ (NSString*)getMimeTypeFromPath:(NSString*)fullPath -{ - NSString* mimeType = nil; - - if (fullPath) { - CFStringRef typeId = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)[fullPath pathExtension], NULL); - if (typeId) { - mimeType = (__bridge_transfer NSString*)UTTypeCopyPreferredTagWithClass(typeId, kUTTagClassMIMEType); - if (!mimeType) { - // special case for m4a - if ([(__bridge NSString*)typeId rangeOfString : @"m4a-audio"].location != NSNotFound) { - mimeType = @"audio/mp4"; - } else if ([[fullPath pathExtension] rangeOfString:@"wav"].location != NSNotFound) { - mimeType = @"audio/wav"; - } else if ([[fullPath pathExtension] rangeOfString:@"css"].location != NSNotFound) { - mimeType = @"text/css"; - } - } - CFRelease(typeId); - } - } - return mimeType; -} - -- (id)initWithName:(NSString *)name -{ - if (self) { - _name = name; - } - return self; -} - -- (CDVPluginResult *)getFileForURL:(CDVFilesystemURL *)baseURI requestedPath:(NSString *)requestedPath options:(NSDictionary *)options -{ - // return unsupported result for assets-library URLs - return [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"getFile not supported for assets-library URLs."]; -} - -- (CDVPluginResult*)getParentForURL:(CDVFilesystemURL *)localURI -{ - // we don't (yet?) support getting the parent of an asset - return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_READABLE_ERR]; -} - -- (CDVPluginResult*)setMetadataForURL:(CDVFilesystemURL *)localURI withObject:(NSDictionary *)options -{ - // setMetadata doesn't make sense for asset library files - return [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR]; -} - -- (CDVPluginResult *)removeFileAtURL:(CDVFilesystemURL *)localURI -{ - // return error for assets-library URLs - return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:INVALID_MODIFICATION_ERR]; -} - -- (CDVPluginResult *)recursiveRemoveFileAtURL:(CDVFilesystemURL *)localURI -{ - // return error for assets-library URLs - return [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"removeRecursively not supported for assets-library URLs."]; -} - -- (CDVPluginResult *)readEntriesAtURL:(CDVFilesystemURL *)localURI -{ - // return unsupported result for assets-library URLs - return [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"readEntries not supported for assets-library URLs."]; -} - -- (CDVPluginResult *)truncateFileAtURL:(CDVFilesystemURL *)localURI atPosition:(unsigned long long)pos -{ - // assets-library files can't be truncated - return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NO_MODIFICATION_ALLOWED_ERR]; -} - -- (CDVPluginResult *)writeToFileAtURL:(CDVFilesystemURL *)localURL withData:(NSData*)encData append:(BOOL)shouldAppend -{ - // text can't be written into assets-library files - return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NO_MODIFICATION_ALLOWED_ERR]; -} - -- (void)copyFileToURL:(CDVFilesystemURL *)destURL withName:(NSString *)newName fromFileSystem:(NSObject<CDVFileSystem> *)srcFs atURL:(CDVFilesystemURL *)srcURL copy:(BOOL)bCopy callback:(void (^)(CDVPluginResult *))callback -{ - // Copying to an assets library file is not doable, since we can't write it. - CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:INVALID_MODIFICATION_ERR]; - callback(result); -} - -- (NSString *)filesystemPathForURL:(CDVFilesystemURL *)url -{ - NSString *path = nil; - if ([[url.url scheme] isEqualToString:kCDVAssetsLibraryScheme]) { - path = [url.url path]; - } else { - path = url.fullPath; - } - if ([path hasSuffix:@"/"]) { - path = [path substringToIndex:([path length]-1)]; - } - return path; -} - -- (void)readFileAtURL:(CDVFilesystemURL *)localURL start:(NSInteger)start end:(NSInteger)end callback:(void (^)(NSData*, NSString* mimeType, CDVFileError))callback -{ - ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset* asset) { - if (asset) { - // We have the asset! Get the data and send it off. - ALAssetRepresentation* assetRepresentation = [asset defaultRepresentation]; - NSUInteger size = (end > start) ? (end - start) : [assetRepresentation size]; - Byte* buffer = (Byte*)malloc(size); - NSUInteger bufferSize = [assetRepresentation getBytes:buffer fromOffset:start length:size error:nil]; - NSData* data = [NSData dataWithBytesNoCopy:buffer length:bufferSize freeWhenDone:YES]; - NSString* MIMEType = (__bridge_transfer NSString*)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)[assetRepresentation UTI], kUTTagClassMIMEType); - - callback(data, MIMEType, NO_ERROR); - } else { - callback(nil, nil, NOT_FOUND_ERR); - } - }; - - ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError* error) { - // Retrieving the asset failed for some reason. Send the appropriate error. - NSLog(@"Error: %@", error); - callback(nil, nil, SECURITY_ERR); - }; - - ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init]; - [assetsLibrary assetForURL:[self assetLibraryURLForLocalURL:localURL] resultBlock:resultBlock failureBlock:failureBlock]; -} - -- (void)getFileMetadataForURL:(CDVFilesystemURL *)localURL callback:(void (^)(CDVPluginResult *))callback -{ - // In this case, we need to use an asynchronous method to retrieve the file. - // Because of this, we can't just assign to `result` and send it at the end of the method. - // Instead, we return after calling the asynchronous method and send `result` in each of the blocks. - ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset* asset) { - if (asset) { - // We have the asset! Populate the dictionary and send it off. - NSMutableDictionary* fileInfo = [NSMutableDictionary dictionaryWithCapacity:5]; - ALAssetRepresentation* assetRepresentation = [asset defaultRepresentation]; - [fileInfo setObject:[NSNumber numberWithUnsignedLongLong:[assetRepresentation size]] forKey:@"size"]; - [fileInfo setObject:localURL.fullPath forKey:@"fullPath"]; - NSString* filename = [assetRepresentation filename]; - [fileInfo setObject:filename forKey:@"name"]; - [fileInfo setObject:[CDVAssetLibraryFilesystem getMimeTypeFromPath:filename] forKey:@"type"]; - NSDate* creationDate = [asset valueForProperty:ALAssetPropertyDate]; - NSNumber* msDate = [NSNumber numberWithDouble:[creationDate timeIntervalSince1970] * 1000]; - [fileInfo setObject:msDate forKey:@"lastModifiedDate"]; - - callback([CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileInfo]); - } else { - // We couldn't find the asset. Send the appropriate error. - callback([CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR]); - } - }; - ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError* error) { - // Retrieving the asset failed for some reason. Send the appropriate error. - callback([CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[error localizedDescription]]); - }; - - ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init]; - [assetsLibrary assetForURL:[self assetLibraryURLForLocalURL:localURL] resultBlock:resultBlock failureBlock:failureBlock]; - return; -} -@end diff --git a/plugins/cordova-plugin-file/src/ios/CDVFile.h b/plugins/cordova-plugin-file/src/ios/CDVFile.h deleted file mode 100644 index 33630c03..00000000 --- a/plugins/cordova-plugin-file/src/ios/CDVFile.h +++ /dev/null @@ -1,157 +0,0 @@ -/* - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you 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. - */ - -#import <Foundation/Foundation.h> -#import <Cordova/CDVPlugin.h> - -NSString* const kCDVAssetsLibraryPrefix; -NSString* const kCDVFilesystemURLPrefix; - -enum CDVFileError { - NO_ERROR = 0, - NOT_FOUND_ERR = 1, - SECURITY_ERR = 2, - ABORT_ERR = 3, - NOT_READABLE_ERR = 4, - ENCODING_ERR = 5, - NO_MODIFICATION_ALLOWED_ERR = 6, - INVALID_STATE_ERR = 7, - SYNTAX_ERR = 8, - INVALID_MODIFICATION_ERR = 9, - QUOTA_EXCEEDED_ERR = 10, - TYPE_MISMATCH_ERR = 11, - PATH_EXISTS_ERR = 12 -}; -typedef int CDVFileError; - -@interface CDVFilesystemURL : NSObject { - NSURL *_url; - NSString *_fileSystemName; - NSString *_fullPath; -} - -- (id) initWithString:(NSString*)strURL; -- (id) initWithURL:(NSURL*)URL; -+ (CDVFilesystemURL *)fileSystemURLWithString:(NSString *)strURL; -+ (CDVFilesystemURL *)fileSystemURLWithURL:(NSURL *)URL; - -- (NSString *)absoluteURL; - -@property (atomic) NSURL *url; -@property (atomic) NSString *fileSystemName; -@property (atomic) NSString *fullPath; - -@end - -@interface CDVFilesystemURLProtocol : NSURLProtocol -@end - -@protocol CDVFileSystem -- (CDVPluginResult *)entryForLocalURI:(CDVFilesystemURL *)url; -- (CDVPluginResult *)getFileForURL:(CDVFilesystemURL *)baseURI requestedPath:(NSString *)requestedPath options:(NSDictionary *)options; -- (CDVPluginResult *)getParentForURL:(CDVFilesystemURL *)localURI; -- (CDVPluginResult *)setMetadataForURL:(CDVFilesystemURL *)localURI withObject:(NSDictionary *)options; -- (CDVPluginResult *)removeFileAtURL:(CDVFilesystemURL *)localURI; -- (CDVPluginResult *)recursiveRemoveFileAtURL:(CDVFilesystemURL *)localURI; -- (CDVPluginResult *)readEntriesAtURL:(CDVFilesystemURL *)localURI; -- (CDVPluginResult *)truncateFileAtURL:(CDVFilesystemURL *)localURI atPosition:(unsigned long long)pos; -- (CDVPluginResult *)writeToFileAtURL:(CDVFilesystemURL *)localURL withData:(NSData*)encData append:(BOOL)shouldAppend; -- (void)copyFileToURL:(CDVFilesystemURL *)destURL withName:(NSString *)newName fromFileSystem:(NSObject<CDVFileSystem> *)srcFs atURL:(CDVFilesystemURL *)srcURL copy:(BOOL)bCopy callback:(void (^)(CDVPluginResult *))callback; -- (void)readFileAtURL:(CDVFilesystemURL *)localURL start:(NSInteger)start end:(NSInteger)end callback:(void (^)(NSData*, NSString* mimeType, CDVFileError))callback; -- (void)getFileMetadataForURL:(CDVFilesystemURL *)localURL callback:(void (^)(CDVPluginResult *))callback; - -- (NSDictionary *)makeEntryForLocalURL:(CDVFilesystemURL *)url; -- (NSDictionary*)makeEntryForPath:(NSString*)fullPath isDirectory:(BOOL)isDir; - -@property (nonatomic,strong) NSString *name; -@property (nonatomic, copy) NSURL*(^urlTransformer)(NSURL*); - -@optional -- (NSString *)filesystemPathForURL:(CDVFilesystemURL *)localURI; -- (CDVFilesystemURL *)URLforFilesystemPath:(NSString *)path; - -@end - -@interface CDVFile : CDVPlugin { - NSString* rootDocsPath; - NSString* appDocsPath; - NSString* appLibraryPath; - NSString* appTempPath; - - NSMutableArray* fileSystems_; - BOOL userHasAllowed; -} - -- (NSNumber*)checkFreeDiskSpace:(NSString*)appPath; -- (NSDictionary*)makeEntryForPath:(NSString*)fullPath fileSystemName:(NSString *)fsName isDirectory:(BOOL)isDir; -- (NSDictionary *)makeEntryForURL:(NSURL *)URL; -- (CDVFilesystemURL *)fileSystemURLforLocalPath:(NSString *)localPath; - -- (NSObject<CDVFileSystem> *)filesystemForURL:(CDVFilesystemURL *)localURL; - -/* Native Registration API */ -- (void)registerFilesystem:(NSObject<CDVFileSystem> *)fs; -- (NSObject<CDVFileSystem> *)fileSystemByName:(NSString *)fsName; - -/* Exec API */ -- (void)requestFileSystem:(CDVInvokedUrlCommand*)command; -- (void)resolveLocalFileSystemURI:(CDVInvokedUrlCommand*)command; -- (void)getDirectory:(CDVInvokedUrlCommand*)command; -- (void)getFile:(CDVInvokedUrlCommand*)command; -- (void)getParent:(CDVInvokedUrlCommand*)command; -- (void)removeRecursively:(CDVInvokedUrlCommand*)command; -- (void)remove:(CDVInvokedUrlCommand*)command; -- (void)copyTo:(CDVInvokedUrlCommand*)command; -- (void)moveTo:(CDVInvokedUrlCommand*)command; -- (void)getFileMetadata:(CDVInvokedUrlCommand*)command; -- (void)readEntries:(CDVInvokedUrlCommand*)command; -- (void)readAsText:(CDVInvokedUrlCommand*)command; -- (void)readAsDataURL:(CDVInvokedUrlCommand*)command; -- (void)readAsArrayBuffer:(CDVInvokedUrlCommand*)command; -- (void)write:(CDVInvokedUrlCommand*)command; -- (void)testFileExists:(CDVInvokedUrlCommand*)command; -- (void)testDirectoryExists:(CDVInvokedUrlCommand*)command; -- (void)getFreeDiskSpace:(CDVInvokedUrlCommand*)command; -- (void)truncate:(CDVInvokedUrlCommand*)command; -- (void)doCopyMove:(CDVInvokedUrlCommand*)command isCopy:(BOOL)bCopy; - -/* Compatibilty with older File API */ -- (NSString*)getMimeTypeFromPath:(NSString*)fullPath; -- (NSDictionary *)getDirectoryEntry:(NSString *)target isDirectory:(BOOL)bDirRequest; - -/* Conversion between filesystem paths and URLs */ -- (NSString *)filesystemPathForURL:(CDVFilesystemURL *)URL; - -/* Internal methods for testing */ -- (void)_getLocalFilesystemPath:(CDVInvokedUrlCommand*)command; - -@property (nonatomic, strong) NSString* rootDocsPath; -@property (nonatomic, strong) NSString* appDocsPath; -@property (nonatomic, strong) NSString* appLibraryPath; -@property (nonatomic, strong) NSString* appTempPath; -@property (nonatomic, strong) NSString* persistentPath; -@property (nonatomic, strong) NSString* temporaryPath; -@property (nonatomic, strong) NSMutableArray* fileSystems; - -@property BOOL userHasAllowed; - -@end - -#define kW3FileTemporary @"temporary" -#define kW3FilePersistent @"persistent" diff --git a/plugins/cordova-plugin-file/src/ios/CDVFile.m b/plugins/cordova-plugin-file/src/ios/CDVFile.m deleted file mode 100644 index eec8978e..00000000 --- a/plugins/cordova-plugin-file/src/ios/CDVFile.m +++ /dev/null @@ -1,1092 +0,0 @@ -/* - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you 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. - */ - -#import <Cordova/CDV.h> -#import "CDVFile.h" -#import "CDVLocalFilesystem.h" -#import "CDVAssetLibraryFilesystem.h" -#import <objc/message.h> - -CDVFile *filePlugin = nil; - -extern NSString * const NSURLIsExcludedFromBackupKey __attribute__((weak_import)); - -#ifndef __IPHONE_5_1 - NSString* const NSURLIsExcludedFromBackupKey = @"NSURLIsExcludedFromBackupKey"; -#endif - -NSString* const kCDVFilesystemURLPrefix = @"cdvfile"; - -@implementation CDVFilesystemURL -@synthesize url=_url; -@synthesize fileSystemName=_fileSystemName; -@synthesize fullPath=_fullPath; - -- (id) initWithString:(NSString *)strURL -{ - if ( self = [super init] ) { - NSURL *decodedURL = [NSURL URLWithString:strURL]; - return [self initWithURL:decodedURL]; - } - return nil; -} - --(id) initWithURL:(NSURL *)URL -{ - if ( self = [super init] ) { - _url = URL; - _fileSystemName = [self filesystemNameForLocalURI:URL]; - _fullPath = [self fullPathForLocalURI:URL]; - } - return self; -} - -/* - * IN - * NSString localURI - * OUT - * NSString FileSystem Name for this URI, or nil if it is not recognized. - */ -- (NSString *)filesystemNameForLocalURI:(NSURL *)uri -{ - if ([[uri scheme] isEqualToString:kCDVFilesystemURLPrefix] && [[uri host] isEqualToString:@"localhost"]) { - NSArray *pathComponents = [uri pathComponents]; - if (pathComponents != nil && pathComponents.count > 1) { - return [pathComponents objectAtIndex:1]; - } - } else if ([[uri scheme] isEqualToString:kCDVAssetsLibraryScheme]) { - return @"assets-library"; - } - return nil; -} - -/* - * IN - * NSString localURI - * OUT - * NSString fullPath component suitable for an Entry object. - * The incoming URI should be properly escaped. The returned fullPath is unescaped. - */ -- (NSString *)fullPathForLocalURI:(NSURL *)uri -{ - if ([[uri scheme] isEqualToString:kCDVFilesystemURLPrefix] && [[uri host] isEqualToString:@"localhost"]) { - NSString *path = [uri path]; - if ([uri query]) { - path = [NSString stringWithFormat:@"%@?%@", path, [uri query]]; - } - NSRange slashRange = [path rangeOfString:@"/" options:0 range:NSMakeRange(1, path.length-1)]; - if (slashRange.location == NSNotFound) { - return @""; - } - return [path substringFromIndex:slashRange.location]; - } else if ([[uri scheme] isEqualToString:kCDVAssetsLibraryScheme]) { - return [[uri absoluteString] substringFromIndex:[kCDVAssetsLibraryScheme length]+2]; - } - return nil; -} - -+ (CDVFilesystemURL *)fileSystemURLWithString:(NSString *)strURL -{ - return [[CDVFilesystemURL alloc] initWithString:strURL]; -} - -+ (CDVFilesystemURL *)fileSystemURLWithURL:(NSURL *)URL -{ - return [[CDVFilesystemURL alloc] initWithURL:URL]; -} - -- (NSString *)absoluteURL -{ - return [NSString stringWithFormat:@"cdvfile://localhost/%@%@", self.fileSystemName, self.fullPath]; -} - -@end - -@implementation CDVFilesystemURLProtocol - -+ (BOOL)canInitWithRequest:(NSURLRequest*)request -{ - NSURL* url = [request URL]; - return [[url scheme] isEqualToString:kCDVFilesystemURLPrefix]; -} - -+ (NSURLRequest*)canonicalRequestForRequest:(NSURLRequest*)request -{ - return request; -} - -+ (BOOL)requestIsCacheEquivalent:(NSURLRequest*)requestA toRequest:(NSURLRequest*)requestB -{ - return [[[requestA URL] resourceSpecifier] isEqualToString:[[requestB URL] resourceSpecifier]]; -} - -- (void)startLoading -{ - CDVFilesystemURL* url = [CDVFilesystemURL fileSystemURLWithURL:[[self request] URL]]; - NSObject<CDVFileSystem> *fs = [filePlugin filesystemForURL:url]; - [fs readFileAtURL:url start:0 end:-1 callback:^void(NSData *data, NSString *mimetype, CDVFileError error) { - NSMutableDictionary* responseHeaders = [[NSMutableDictionary alloc] init]; - responseHeaders[@"Cache-Control"] = @"no-cache"; - - if (!error) { - responseHeaders[@"Content-Type"] = mimetype; - NSURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:url.url statusCode:200 HTTPVersion:@"HTTP/1.1"headerFields:responseHeaders]; - [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; - [[self client] URLProtocol:self didLoadData:data]; - [[self client] URLProtocolDidFinishLoading:self]; - } else { - NSURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:url.url statusCode:404 HTTPVersion:@"HTTP/1.1"headerFields:responseHeaders]; - [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; - [[self client] URLProtocolDidFinishLoading:self]; - } - }]; -} - -- (void)stopLoading -{} - -- (NSCachedURLResponse *)connection:(NSURLConnection *)connection - willCacheResponse:(NSCachedURLResponse*)cachedResponse { - return nil; -} - -@end - - -@implementation CDVFile - -@synthesize rootDocsPath, appDocsPath, appLibraryPath, appTempPath, userHasAllowed, fileSystems=fileSystems_; - -- (void)registerFilesystem:(NSObject<CDVFileSystem> *)fs { - __weak CDVFile* weakSelf = self; - SEL sel = NSSelectorFromString(@"urlTransformer"); - // for backwards compatibility - we check if this property is there - // we create a wrapper block because the urlTransformer property - // on the commandDelegate might be set dynamically at a future time - // (and not dependent on plugin loading order) - if ([self.commandDelegate respondsToSelector:sel]) { - fs.urlTransformer = ^NSURL*(NSURL* urlToTransform) { - // grab the block from the commandDelegate - NSURL* (^urlTransformer)(NSURL*) = ((id(*)(id, SEL))objc_msgSend)(weakSelf.commandDelegate, sel); - // if block is not null, we call it - if (urlTransformer) { - return urlTransformer(urlToTransform); - } else { // else we return the same url - return urlToTransform; - } - }; - } - [fileSystems_ addObject:fs]; -} - -- (NSObject<CDVFileSystem> *)fileSystemByName:(NSString *)fsName -{ - if (self.fileSystems != nil) { - for (NSObject<CDVFileSystem> *fs in self.fileSystems) { - if ([fs.name isEqualToString:fsName]) { - return fs; - } - } - } - return nil; - -} - -- (NSObject<CDVFileSystem> *)filesystemForURL:(CDVFilesystemURL *)localURL { - if (localURL.fileSystemName == nil) return nil; - @try { - return [self fileSystemByName:localURL.fileSystemName]; - } - @catch (NSException *e) { - return nil; - } -} - -- (NSArray *)getExtraFileSystemsPreference:(UIViewController *)vc -{ - NSString *filesystemsStr = nil; - if([self.viewController isKindOfClass:[CDVViewController class]]) { - CDVViewController *vc = (CDVViewController *)self.viewController; - NSDictionary *settings = [vc settings]; - filesystemsStr = [settings[@"iosextrafilesystems"] lowercaseString]; - } - if (!filesystemsStr) { - filesystemsStr = @"library,library-nosync,documents,documents-nosync,cache,bundle,root"; - } - return [filesystemsStr componentsSeparatedByString:@","]; -} - -- (void)makeNonSyncable:(NSString*)path { - [[NSFileManager defaultManager] createDirectoryAtPath:path - withIntermediateDirectories:YES - attributes:nil - error:nil]; - NSURL* url = [NSURL fileURLWithPath:path]; - [url setResourceValue: [NSNumber numberWithBool: YES] - forKey: NSURLIsExcludedFromBackupKey error:nil]; - -} - -- (void)registerExtraFileSystems:(NSArray *)filesystems fromAvailableSet:(NSDictionary *)availableFileSystems -{ - NSMutableSet *installedFilesystems = [[NSMutableSet alloc] initWithCapacity:7]; - - /* Build non-syncable directories as necessary */ - for (NSString *nonSyncFS in @[@"library-nosync", @"documents-nosync"]) { - if ([filesystems containsObject:nonSyncFS]) { - [self makeNonSyncable:availableFileSystems[nonSyncFS]]; - } - } - - /* Register filesystems in order */ - for (NSString *fsName in filesystems) { - if (![installedFilesystems containsObject:fsName]) { - NSString *fsRoot = availableFileSystems[fsName]; - if (fsRoot) { - [filePlugin registerFilesystem:[[CDVLocalFilesystem alloc] initWithName:fsName root:fsRoot]]; - [installedFilesystems addObject:fsName]; - } else { - NSLog(@"Unrecognized extra filesystem identifier: %@", fsName); - } - } - } -} - -- (NSDictionary *)getAvailableFileSystems -{ - NSString *libPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0]; - NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; - return @{ - @"library": libPath, - @"library-nosync": [libPath stringByAppendingPathComponent:@"NoCloud"], - @"documents": docPath, - @"documents-nosync": [docPath stringByAppendingPathComponent:@"NoCloud"], - @"cache": [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0], - @"bundle": [[NSBundle mainBundle] bundlePath], - @"root": @"/" - }; -} - -- (void)pluginInitialize -{ - filePlugin = self; - [NSURLProtocol registerClass:[CDVFilesystemURLProtocol class]]; - - fileSystems_ = [[NSMutableArray alloc] initWithCapacity:3]; - - // Get the Library directory path - NSArray* paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES); - self.appLibraryPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"files"]; - - // Get the Temporary directory path - self.appTempPath = [NSTemporaryDirectory()stringByStandardizingPath]; // remove trailing slash from NSTemporaryDirectory() - - // Get the Documents directory path - paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); - self.rootDocsPath = [paths objectAtIndex:0]; - self.appDocsPath = [self.rootDocsPath stringByAppendingPathComponent:@"files"]; - - - NSString *location = nil; - if([self.viewController isKindOfClass:[CDVViewController class]]) { - CDVViewController *vc = (CDVViewController *)self.viewController; - NSMutableDictionary *settings = vc.settings; - location = [[settings objectForKey:@"iospersistentfilelocation"] lowercaseString]; - } - if (location == nil) { - // Compatibilty by default (if the config preference is not set, or - // if we're not embedded in a CDVViewController somehow.) - location = @"compatibility"; - } - - NSError *error; - if ([[NSFileManager defaultManager] createDirectoryAtPath:self.appTempPath - withIntermediateDirectories:YES - attributes:nil - error:&error]) { - [self registerFilesystem:[[CDVLocalFilesystem alloc] initWithName:@"temporary" root:self.appTempPath]]; - } else { - NSLog(@"Unable to create temporary directory: %@", error); - } - if ([location isEqualToString:@"library"]) { - if ([[NSFileManager defaultManager] createDirectoryAtPath:self.appLibraryPath - withIntermediateDirectories:YES - attributes:nil - error:&error]) { - [self registerFilesystem:[[CDVLocalFilesystem alloc] initWithName:@"persistent" root:self.appLibraryPath]]; - } else { - NSLog(@"Unable to create library directory: %@", error); - } - } else if ([location isEqualToString:@"compatibility"]) { - /* - * Fall-back to compatibility mode -- this is the logic implemented in - * earlier versions of this plugin, and should be maintained here so - * that apps which were originally deployed with older versions of the - * plugin can continue to provide access to files stored under those - * versions. - */ - [self registerFilesystem:[[CDVLocalFilesystem alloc] initWithName:@"persistent" root:self.rootDocsPath]]; - } else { - NSAssert(false, - @"File plugin configuration error: Please set iosPersistentFileLocation in config.xml to one of \"library\" (for new applications) or \"compatibility\" (for compatibility with previous versions)"); - } - [self registerFilesystem:[[CDVAssetLibraryFilesystem alloc] initWithName:@"assets-library"]]; - - [self registerExtraFileSystems:[self getExtraFileSystemsPreference:self.viewController] - fromAvailableSet:[self getAvailableFileSystems]]; - -} - -- (CDVFilesystemURL *)fileSystemURLforArg:(NSString *)urlArg -{ - CDVFilesystemURL* ret = nil; - if ([urlArg hasPrefix:@"file://"]) { - /* This looks like a file url. Get the path, and see if any handlers recognize it. */ - NSURL *fileURL = [NSURL URLWithString:urlArg]; - NSURL *resolvedFileURL = [fileURL URLByResolvingSymlinksInPath]; - NSString *path = [resolvedFileURL path]; - ret = [self fileSystemURLforLocalPath:path]; - } else { - ret = [CDVFilesystemURL fileSystemURLWithString:urlArg]; - } - return ret; -} - -- (CDVFilesystemURL *)fileSystemURLforLocalPath:(NSString *)localPath -{ - CDVFilesystemURL *localURL = nil; - NSUInteger shortestFullPath = 0; - - // Try all installed filesystems, in order. Return the most match url. - for (id object in self.fileSystems) { - if ([object respondsToSelector:@selector(URLforFilesystemPath:)]) { - CDVFilesystemURL *url = [object URLforFilesystemPath:localPath]; - if (url){ - // A shorter fullPath would imply that the filesystem is a better match for the local path - if (!localURL || ([[url fullPath] length] < shortestFullPath)) { - localURL = url; - shortestFullPath = [[url fullPath] length]; - } - } - } - } - return localURL; -} - -- (NSNumber*)checkFreeDiskSpace:(NSString*)appPath -{ - NSFileManager* fMgr = [[NSFileManager alloc] init]; - - NSError* __autoreleasing pError = nil; - - NSDictionary* pDict = [fMgr attributesOfFileSystemForPath:appPath error:&pError]; - NSNumber* pNumAvail = (NSNumber*)[pDict objectForKey:NSFileSystemFreeSize]; - - return pNumAvail; -} - -/* Request the File System info - * - * IN: - * arguments[0] - type (number as string) - * TEMPORARY = 0, PERSISTENT = 1; - * arguments[1] - size - * - * OUT: - * Dictionary representing FileSystem object - * name - the human readable directory name - * root = DirectoryEntry object - * bool isDirectory - * bool isFile - * string name - * string fullPath - * fileSystem = FileSystem object - !! ignored because creates circular reference !! - */ - -- (void)requestFileSystem:(CDVInvokedUrlCommand*)command -{ - // arguments - NSString* strType = [command argumentAtIndex:0]; - unsigned long long size = [[command argumentAtIndex:1] longLongValue]; - - int type = [strType intValue]; - CDVPluginResult* result = nil; - - if (type > self.fileSystems.count) { - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:NOT_FOUND_ERR]; - NSLog(@"No filesystem of type requested"); - } else { - NSString* fullPath = @"/"; - // check for avail space for size request - NSNumber* pNumAvail = [self checkFreeDiskSpace:self.rootDocsPath]; - // NSLog(@"Free space: %@", [NSString stringWithFormat:@"%qu", [ pNumAvail unsignedLongLongValue ]]); - if (pNumAvail && ([pNumAvail unsignedLongLongValue] < size)) { - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:QUOTA_EXCEEDED_ERR]; - } else { - NSObject<CDVFileSystem> *rootFs = [self.fileSystems objectAtIndex:type]; - if (rootFs == nil) { - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:NOT_FOUND_ERR]; - NSLog(@"No filesystem of type requested"); - } else { - NSMutableDictionary* fileSystem = [NSMutableDictionary dictionaryWithCapacity:2]; - [fileSystem setObject:rootFs.name forKey:@"name"]; - NSDictionary* dirEntry = [self makeEntryForPath:fullPath fileSystemName:rootFs.name isDirectory:YES]; - [fileSystem setObject:dirEntry forKey:@"root"]; - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileSystem]; - } - } - } - [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; -} - - -- (void)requestAllFileSystems:(CDVInvokedUrlCommand*)command -{ - NSMutableArray* ret = [[NSMutableArray alloc] init]; - for (NSObject<CDVFileSystem>* root in fileSystems_) { - [ret addObject:[self makeEntryForPath:@"/" fileSystemName:root.name isDirectory:YES]]; - } - CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:ret]; - [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; -} - -- (void)requestAllPaths:(CDVInvokedUrlCommand*)command -{ - NSString* libPath = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES)[0]; - NSString* libPathSync = [libPath stringByAppendingPathComponent:@"Cloud"]; - NSString* libPathNoSync = [libPath stringByAppendingPathComponent:@"NoCloud"]; - NSString* docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0]; - NSString* storagePath = [libPath stringByDeletingLastPathComponent]; - NSString* cachePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]; - - // Create the directories if necessary. - [[NSFileManager defaultManager] createDirectoryAtPath:libPathSync withIntermediateDirectories:YES attributes:nil error:nil]; - [[NSFileManager defaultManager] createDirectoryAtPath:libPathNoSync withIntermediateDirectories:YES attributes:nil error:nil]; - // Mark NoSync as non-iCloud. - [[NSURL fileURLWithPath:libPathNoSync] setResourceValue: [NSNumber numberWithBool: YES] - forKey: NSURLIsExcludedFromBackupKey error:nil]; - - NSDictionary* ret = @{ - @"applicationDirectory": [[NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]] absoluteString], - @"applicationStorageDirectory": [[NSURL fileURLWithPath:storagePath] absoluteString], - @"dataDirectory": [[NSURL fileURLWithPath:libPathNoSync] absoluteString], - @"syncedDataDirectory": [[NSURL fileURLWithPath:libPathSync] absoluteString], - @"documentsDirectory": [[NSURL fileURLWithPath:docPath] absoluteString], - @"cacheDirectory": [[NSURL fileURLWithPath:cachePath] absoluteString], - @"tempDirectory": [[NSURL fileURLWithPath:NSTemporaryDirectory()] absoluteString] - }; - - CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:ret]; - [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; -} - -/* Creates and returns a dictionary representing an Entry Object - * - * IN: - * NSString* fullPath of the entry - * int fsType - FileSystem type - * BOOL isDirectory - YES if this is a directory, NO if is a file - * OUT: - * NSDictionary* Entry object - * bool as NSNumber isDirectory - * bool as NSNumber isFile - * NSString* name - last part of path - * NSString* fullPath - * NSString* filesystemName - FileSystem name -- actual filesystem will be created on the JS side if necessary, to avoid - * creating circular reference (FileSystem contains DirectoryEntry which contains FileSystem.....!!) - */ -- (NSDictionary*)makeEntryForPath:(NSString*)fullPath fileSystemName:(NSString *)fsName isDirectory:(BOOL)isDir -{ - NSObject<CDVFileSystem> *fs = [self fileSystemByName:fsName]; - return [fs makeEntryForPath:fullPath isDirectory:isDir]; -} - -- (NSDictionary *)makeEntryForLocalURL:(CDVFilesystemURL *)localURL -{ - NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURL]; - return [fs makeEntryForLocalURL:localURL]; -} - -- (NSDictionary *)makeEntryForURL:(NSURL *)URL -{ - CDVFilesystemURL* fsURL = [self fileSystemURLforArg:[URL absoluteString]]; - return [self makeEntryForLocalURL:fsURL]; -} - -/* - * Given a URI determine the File System information associated with it and return an appropriate W3C entry object - * IN - * NSString* localURI: Should be an escaped local filesystem URI - * OUT - * Entry object - * bool isDirectory - * bool isFile - * string name - * string fullPath - * fileSystem = FileSystem object - !! ignored because creates circular reference FileSystem contains DirectoryEntry which contains FileSystem.....!! - */ -- (void)resolveLocalFileSystemURI:(CDVInvokedUrlCommand*)command -{ - // arguments - NSString* localURIstr = [command argumentAtIndex:0]; - CDVPluginResult* result; - - localURIstr = [self encodePath:localURIstr]; //encode path before resolving - CDVFilesystemURL* inputURI = [self fileSystemURLforArg:localURIstr]; - - if (inputURI == nil || inputURI.fileSystemName == nil) { - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:ENCODING_ERR]; - } else { - NSObject<CDVFileSystem> *fs = [self filesystemForURL:inputURI]; - if (fs == nil) { - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:ENCODING_ERR]; - } else { - result = [fs entryForLocalURI:inputURI]; - } - } - [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; -} - -//encode path with percent escapes --(NSString *)encodePath:(NSString *)path -{ - NSString *decodedPath = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; //decode incase it's already encoded to avoid encoding twice - return [decodedPath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; -} - - -/* Part of DirectoryEntry interface, creates or returns the specified directory - * IN: - * NSString* localURI - local filesystem URI for this directory - * NSString* path - directory to be created/returned; may be full path or relative path - * NSDictionary* - Flags object - * boolean as NSNumber create - - * if create is true and directory does not exist, create dir and return directory entry - * if create is true and exclusive is true and directory does exist, return error - * if create is false and directory does not exist, return error - * if create is false and the path represents a file, return error - * boolean as NSNumber exclusive - used in conjunction with create - * if exclusive is true and create is true - specifies failure if directory already exists - * - * - */ -- (void)getDirectory:(CDVInvokedUrlCommand*)command -{ - NSMutableArray* arguments = [NSMutableArray arrayWithArray:command.arguments]; - NSMutableDictionary* options = nil; - - if ([arguments count] >= 3) { - options = [command argumentAtIndex:2 withDefault:nil]; - } - // add getDir to options and call getFile() - if (options != nil) { - options = [NSMutableDictionary dictionaryWithDictionary:options]; - } else { - options = [NSMutableDictionary dictionaryWithCapacity:1]; - } - [options setObject:[NSNumber numberWithInt:1] forKey:@"getDir"]; - if ([arguments count] >= 3) { - [arguments replaceObjectAtIndex:2 withObject:options]; - } else { - [arguments addObject:options]; - } - CDVInvokedUrlCommand* subCommand = - [[CDVInvokedUrlCommand alloc] initWithArguments:arguments - callbackId:command.callbackId - className:command.className - methodName:command.methodName]; - - [self getFile:subCommand]; -} - -/* Part of DirectoryEntry interface, creates or returns the specified file - * IN: - * NSString* baseURI - local filesytem URI for the base directory to search - * NSString* requestedPath - file to be created/returned; may be absolute path or relative path - * NSDictionary* options - Flags object - * boolean as NSNumber create - - * if create is true and file does not exist, create file and return File entry - * if create is true and exclusive is true and file does exist, return error - * if create is false and file does not exist, return error - * if create is false and the path represents a directory, return error - * boolean as NSNumber exclusive - used in conjunction with create - * if exclusive is true and create is true - specifies failure if file already exists - */ -- (void)getFile:(CDVInvokedUrlCommand*)command -{ - NSString* baseURIstr = [command argumentAtIndex:0]; - CDVFilesystemURL* baseURI = [self fileSystemURLforArg:baseURIstr]; - NSString* requestedPath = [command argumentAtIndex:1]; - NSDictionary* options = [command argumentAtIndex:2 withDefault:nil]; - - NSObject<CDVFileSystem> *fs = [self filesystemForURL:baseURI]; - CDVPluginResult* result = [fs getFileForURL:baseURI requestedPath:requestedPath options:options]; - - - [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; -} - -/* - * Look up the parent Entry containing this Entry. - * If this Entry is the root of its filesystem, its parent is itself. - * IN: - * NSArray* arguments - * 0 - NSString* localURI - * NSMutableDictionary* options - * empty - */ -- (void)getParent:(CDVInvokedUrlCommand*)command -{ - // arguments are URL encoded - CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]]; - - NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI]; - CDVPluginResult* result = [fs getParentForURL:localURI]; - - [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; -} - -/* - * set MetaData of entry - * Currently we only support "com.apple.MobileBackup" (boolean) - */ -- (void)setMetadata:(CDVInvokedUrlCommand*)command -{ - // arguments - CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]]; - NSDictionary* options = [command argumentAtIndex:1 withDefault:nil]; - NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI]; - CDVPluginResult* result = [fs setMetadataForURL:localURI withObject:options]; - - [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; -} - -/* removes the directory or file entry - * IN: - * NSArray* arguments - * 0 - NSString* localURI - * - * returns NO_MODIFICATION_ALLOWED_ERR if is top level directory or no permission to delete dir - * returns INVALID_MODIFICATION_ERR if is non-empty dir or asset library file - * returns NOT_FOUND_ERR if file or dir is not found -*/ -- (void)remove:(CDVInvokedUrlCommand*)command -{ - // arguments - CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]]; - CDVPluginResult* result = nil; - - if ([localURI.fullPath isEqualToString:@""]) { - // error if try to remove top level (documents or tmp) dir - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NO_MODIFICATION_ALLOWED_ERR]; - } else { - NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI]; - result = [fs removeFileAtURL:localURI]; - } - [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; -} - -/* recursively removes the directory - * IN: - * NSArray* arguments - * 0 - NSString* localURI - * - * returns NO_MODIFICATION_ALLOWED_ERR if is top level directory or no permission to delete dir - * returns NOT_FOUND_ERR if file or dir is not found - */ -- (void)removeRecursively:(CDVInvokedUrlCommand*)command -{ - // arguments - CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]]; - CDVPluginResult* result = nil; - - if ([localURI.fullPath isEqualToString:@""]) { - // error if try to remove top level (documents or tmp) dir - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NO_MODIFICATION_ALLOWED_ERR]; - } else { - NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI]; - result = [fs recursiveRemoveFileAtURL:localURI]; - } - [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; -} - -- (void)copyTo:(CDVInvokedUrlCommand*)command -{ - [self doCopyMove:command isCopy:YES]; -} - -- (void)moveTo:(CDVInvokedUrlCommand*)command -{ - [self doCopyMove:command isCopy:NO]; -} - -/* Copy/move a file or directory to a new location - * IN: - * NSArray* arguments - * 0 - NSString* URL of entry to copy - * 1 - NSString* URL of the directory into which to copy/move the entry - * 2 - Optionally, the new name of the entry, defaults to the current name - * BOOL - bCopy YES if copy, NO if move - */ -- (void)doCopyMove:(CDVInvokedUrlCommand*)command isCopy:(BOOL)bCopy -{ - NSArray* arguments = command.arguments; - - // arguments - NSString* srcURLstr = [command argumentAtIndex:0]; - NSString* destURLstr = [command argumentAtIndex:1]; - - CDVPluginResult *result; - - if (!srcURLstr || !destURLstr) { - // either no source or no destination provided - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR]; - [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; - return; - } - - CDVFilesystemURL* srcURL = [self fileSystemURLforArg:srcURLstr]; - CDVFilesystemURL* destURL = [self fileSystemURLforArg:destURLstr]; - - NSObject<CDVFileSystem> *srcFs = [self filesystemForURL:srcURL]; - NSObject<CDVFileSystem> *destFs = [self filesystemForURL:destURL]; - - // optional argument; use last component from srcFullPath if new name not provided - NSString* newName = ([arguments count] > 2) ? [command argumentAtIndex:2] : [srcURL.url lastPathComponent]; - if ([newName rangeOfString:@":"].location != NSNotFound) { - // invalid chars in new name - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:ENCODING_ERR]; - [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; - return; - } - - [destFs copyFileToURL:destURL withName:newName fromFileSystem:srcFs atURL:srcURL copy:bCopy callback:^(CDVPluginResult* result) { - [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; - }]; - -} - -- (void)getFileMetadata:(CDVInvokedUrlCommand*)command -{ - // arguments - CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]]; - NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI]; - - [fs getFileMetadataForURL:localURI callback:^(CDVPluginResult* result) { - [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; - }]; -} - -- (void)readEntries:(CDVInvokedUrlCommand*)command -{ - CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]]; - NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI]; - CDVPluginResult *result = [fs readEntriesAtURL:localURI]; - - [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; -} - -/* read and return file data - * IN: - * NSArray* arguments - * 0 - NSString* fullPath - * 1 - NSString* encoding - * 2 - NSString* start - * 3 - NSString* end - */ -- (void)readAsText:(CDVInvokedUrlCommand*)command -{ - // arguments - CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]]; - NSString* encoding = [command argumentAtIndex:1]; - NSInteger start = [[command argumentAtIndex:2] integerValue]; - NSInteger end = [[command argumentAtIndex:3] integerValue]; - - NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI]; - - if (fs == nil) { - CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR]; - [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; - return; - } - - // TODO: implement - if ([@"UTF-8" caseInsensitiveCompare : encoding] != NSOrderedSame) { - NSLog(@"Only UTF-8 encodings are currently supported by readAsText"); - CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:ENCODING_ERR]; - [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; - return; - } - - [self.commandDelegate runInBackground:^ { - [fs readFileAtURL:localURI start:start end:end callback:^(NSData* data, NSString* mimeType, CDVFileError errorCode) { - CDVPluginResult* result = nil; - if (data != nil) { - NSString* str = [[NSString alloc] initWithBytesNoCopy:(void*)[data bytes] length:[data length] encoding:NSUTF8StringEncoding freeWhenDone:NO]; - // Check that UTF8 conversion did not fail. - if (str != nil) { - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:str]; - result.associatedObject = data; - } else { - errorCode = ENCODING_ERR; - } - } - if (result == nil) { - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode]; - } - - [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; - }]; - }]; -} - -/* Read content of text file and return as base64 encoded data url. - * IN: - * NSArray* arguments - * 0 - NSString* fullPath - * 1 - NSString* start - * 2 - NSString* end - * - * Determines the mime type from the file extension, returns ENCODING_ERR if mimetype can not be determined. - */ - -- (void)readAsDataURL:(CDVInvokedUrlCommand*)command -{ - CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]]; - NSInteger start = [[command argumentAtIndex:1] integerValue]; - NSInteger end = [[command argumentAtIndex:2] integerValue]; - - NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI]; - - [self.commandDelegate runInBackground:^ { - [fs readFileAtURL:localURI start:start end:end callback:^(NSData* data, NSString* mimeType, CDVFileError errorCode) { - CDVPluginResult* result = nil; - if (data != nil) { - SEL selector = NSSelectorFromString(@"cdv_base64EncodedString"); - if (![data respondsToSelector:selector]) { - selector = NSSelectorFromString(@"base64EncodedString"); - } - id (*func)(id, SEL) = (void *)[data methodForSelector:selector]; - NSString* b64Str = func(data, selector); - NSString* output = [NSString stringWithFormat:@"data:%@;base64,%@", mimeType, b64Str]; - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:output]; - } else { - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode]; - } - - [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; - }]; - }]; -} - -/* Read content of text file and return as an arraybuffer - * IN: - * NSArray* arguments - * 0 - NSString* fullPath - * 1 - NSString* start - * 2 - NSString* end - */ - -- (void)readAsArrayBuffer:(CDVInvokedUrlCommand*)command -{ - CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]]; - NSInteger start = [[command argumentAtIndex:1] integerValue]; - NSInteger end = [[command argumentAtIndex:2] integerValue]; - - NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI]; - - [self.commandDelegate runInBackground:^ { - [fs readFileAtURL:localURI start:start end:end callback:^(NSData* data, NSString* mimeType, CDVFileError errorCode) { - CDVPluginResult* result = nil; - if (data != nil) { - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArrayBuffer:data]; - } else { - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode]; - } - - [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; - }]; - }]; -} - -- (void)readAsBinaryString:(CDVInvokedUrlCommand*)command -{ - CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]]; - NSInteger start = [[command argumentAtIndex:1] integerValue]; - NSInteger end = [[command argumentAtIndex:2] integerValue]; - - NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI]; - - [self.commandDelegate runInBackground:^ { - [fs readFileAtURL:localURI start:start end:end callback:^(NSData* data, NSString* mimeType, CDVFileError errorCode) { - CDVPluginResult* result = nil; - if (data != nil) { - NSString* payload = [[NSString alloc] initWithBytesNoCopy:(void*)[data bytes] length:[data length] encoding:NSASCIIStringEncoding freeWhenDone:NO]; - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:payload]; - result.associatedObject = data; - } else { - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode]; - } - - [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; - }]; - }]; -} - - -- (void)truncate:(CDVInvokedUrlCommand*)command -{ - // arguments - CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]]; - unsigned long long pos = (unsigned long long)[[command argumentAtIndex:1] longLongValue]; - - NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI]; - CDVPluginResult *result = [fs truncateFileAtURL:localURI atPosition:pos]; - - [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; -} - -/* write - * IN: - * NSArray* arguments - * 0 - NSString* localURI of file to write to - * 1 - NSString* or NSData* data to write - * 2 - NSNumber* position to begin writing - */ -- (void)write:(CDVInvokedUrlCommand*)command -{ - [self.commandDelegate runInBackground:^ { - NSString* callbackId = command.callbackId; - - // arguments - CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]]; - id argData = [command argumentAtIndex:1]; - unsigned long long pos = (unsigned long long)[[command argumentAtIndex:2] longLongValue]; - - NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI]; - - - [fs truncateFileAtURL:localURI atPosition:pos]; - CDVPluginResult *result; - if ([argData isKindOfClass:[NSString class]]) { - NSData *encData = [argData dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES]; - result = [fs writeToFileAtURL:localURI withData:encData append:YES]; - } else if ([argData isKindOfClass:[NSData class]]) { - result = [fs writeToFileAtURL:localURI withData:argData append:YES]; - } else { - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Invalid parameter type"]; - } - [self.commandDelegate sendPluginResult:result callbackId:callbackId]; - }]; -} - -#pragma mark Methods for converting between URLs and paths - -- (NSString *)filesystemPathForURL:(CDVFilesystemURL *)localURL -{ - for (NSObject<CDVFileSystem> *fs in self.fileSystems) { - if ([fs.name isEqualToString:localURL.fileSystemName]) { - if ([fs respondsToSelector:@selector(filesystemPathForURL:)]) { - return [fs filesystemPathForURL:localURL]; - } - } - } - return nil; -} - -#pragma mark Undocumented Filesystem API - -- (void)testFileExists:(CDVInvokedUrlCommand*)command -{ - // arguments - NSString* argPath = [command argumentAtIndex:0]; - - // Get the file manager - NSFileManager* fMgr = [NSFileManager defaultManager]; - NSString* appFile = argPath; // [ self getFullPath: argPath]; - - BOOL bExists = [fMgr fileExistsAtPath:appFile]; - CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:(bExists ? 1 : 0)]; - - [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; -} - -- (void)testDirectoryExists:(CDVInvokedUrlCommand*)command -{ - // arguments - NSString* argPath = [command argumentAtIndex:0]; - - // Get the file manager - NSFileManager* fMgr = [[NSFileManager alloc] init]; - NSString* appFile = argPath; // [self getFullPath: argPath]; - BOOL bIsDir = NO; - BOOL bExists = [fMgr fileExistsAtPath:appFile isDirectory:&bIsDir]; - - CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:((bExists && bIsDir) ? 1 : 0)]; - - [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; -} - -// Returns number of bytes available via callback -- (void)getFreeDiskSpace:(CDVInvokedUrlCommand*)command -{ - // no arguments - - NSNumber* pNumAvail = [self checkFreeDiskSpace:self.appDocsPath]; - - NSString* strFreeSpace = [NSString stringWithFormat:@"%qu", [pNumAvail unsignedLongLongValue]]; - // NSLog(@"Free space is %@", strFreeSpace ); - - CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:strFreeSpace]; - - [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; -} - -#pragma mark Compatibility with older File API - -- (NSString*)getMimeTypeFromPath:(NSString*)fullPath -{ - return [CDVLocalFilesystem getMimeTypeFromPath:fullPath]; -} - -- (NSDictionary *)getDirectoryEntry:(NSString *)localPath isDirectory:(BOOL)bDirRequest -{ - CDVFilesystemURL *localURL = [self fileSystemURLforLocalPath:localPath]; - return [self makeEntryForPath:localURL.fullPath fileSystemName:localURL.fileSystemName isDirectory:bDirRequest]; -} - -#pragma mark Internal methods for testing -// Internal methods for testing: Get the on-disk location of a local filesystem url. -// [Currently used for testing file-transfer] - -- (void)_getLocalFilesystemPath:(CDVInvokedUrlCommand*)command -{ - CDVFilesystemURL* localURL = [self fileSystemURLforArg:command.arguments[0]]; - - NSString* fsPath = [self filesystemPathForURL:localURL]; - CDVPluginResult* result; - if (fsPath) { - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:fsPath]; - } else { - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Cannot resolve URL to a file"]; - } - [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; -} - -@end diff --git a/plugins/cordova-plugin-file/src/ios/CDVLocalFilesystem.h b/plugins/cordova-plugin-file/src/ios/CDVLocalFilesystem.h deleted file mode 100644 index a0186c85..00000000 --- a/plugins/cordova-plugin-file/src/ios/CDVLocalFilesystem.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you 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. - */ - -#import "CDVFile.h" - -@interface CDVLocalFilesystem : NSObject<CDVFileSystem> { - NSString *_name; - NSString *_fsRoot; -} - -- (id) initWithName:(NSString *)name root:(NSString *)fsRoot; -+ (NSString*)getMimeTypeFromPath:(NSString*)fullPath; - -@property (nonatomic,strong) NSString *fsRoot; - -@end diff --git a/plugins/cordova-plugin-file/src/ios/CDVLocalFilesystem.m b/plugins/cordova-plugin-file/src/ios/CDVLocalFilesystem.m deleted file mode 100644 index 72bc421e..00000000 --- a/plugins/cordova-plugin-file/src/ios/CDVLocalFilesystem.m +++ /dev/null @@ -1,734 +0,0 @@ -/* - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you 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. - */ - -#import "CDVFile.h" -#import "CDVLocalFilesystem.h" -#import <Cordova/CDV.h> -#import <MobileCoreServices/MobileCoreServices.h> -#import <sys/xattr.h> - -@implementation CDVLocalFilesystem -@synthesize name=_name, fsRoot=_fsRoot, urlTransformer; - -- (id) initWithName:(NSString *)name root:(NSString *)fsRoot -{ - if (self) { - _name = name; - _fsRoot = fsRoot; - } - return self; -} - -/* - * IN - * NSString localURI - * OUT - * CDVPluginResult result containing a file or directoryEntry for the localURI, or an error if the - * URI represents a non-existent path, or is unrecognized or otherwise malformed. - */ -- (CDVPluginResult *)entryForLocalURI:(CDVFilesystemURL *)url -{ - CDVPluginResult* result = nil; - NSDictionary* entry = [self makeEntryForLocalURL:url]; - if (entry) { - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:entry]; - } else { - // return NOT_FOUND_ERR - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR]; - } - return result; -} -- (NSDictionary *)makeEntryForLocalURL:(CDVFilesystemURL *)url { - NSString *path = [self filesystemPathForURL:url]; - NSFileManager* fileMgr = [[NSFileManager alloc] init]; - BOOL isDir = NO; - // see if exists and is file or dir - BOOL bExists = [fileMgr fileExistsAtPath:path isDirectory:&isDir]; - if (bExists) { - return [self makeEntryForPath:url.fullPath isDirectory:isDir]; - } else { - return nil; - } -} -- (NSDictionary*)makeEntryForPath:(NSString*)fullPath isDirectory:(BOOL)isDir -{ - NSMutableDictionary* dirEntry = [NSMutableDictionary dictionaryWithCapacity:5]; - NSString* lastPart = [[self stripQueryParametersFromPath:fullPath] lastPathComponent]; - if (isDir && ![fullPath hasSuffix:@"/"]) { - fullPath = [fullPath stringByAppendingString:@"/"]; - } - [dirEntry setObject:[NSNumber numberWithBool:!isDir] forKey:@"isFile"]; - [dirEntry setObject:[NSNumber numberWithBool:isDir] forKey:@"isDirectory"]; - [dirEntry setObject:fullPath forKey:@"fullPath"]; - [dirEntry setObject:lastPart forKey:@"name"]; - [dirEntry setObject:self.name forKey: @"filesystemName"]; - - NSURL* nativeURL = [NSURL fileURLWithPath:[self filesystemPathForFullPath:fullPath]]; - if (self.urlTransformer) { - nativeURL = self.urlTransformer(nativeURL); - } - - dirEntry[@"nativeURL"] = [nativeURL absoluteString]; - - return dirEntry; -} - -- (NSString *)stripQueryParametersFromPath:(NSString *)fullPath -{ - NSRange questionMark = [fullPath rangeOfString:@"?"]; - if (questionMark.location != NSNotFound) { - return [fullPath substringWithRange:NSMakeRange(0,questionMark.location)]; - } - return fullPath; -} - -- (NSString *)filesystemPathForFullPath:(NSString *)fullPath -{ - NSString *path = nil; - NSString *strippedFullPath = [self stripQueryParametersFromPath:fullPath]; - path = [NSString stringWithFormat:@"%@%@", self.fsRoot, strippedFullPath]; - if ([path length] > 1 && [path hasSuffix:@"/"]) { - path = [path substringToIndex:([path length]-1)]; - } - return path; -} -/* - * IN - * NSString localURI - * OUT - * NSString full local filesystem path for the represented file or directory, or nil if no such path is possible - * The file or directory does not necessarily have to exist. nil is returned if the filesystem type is not recognized, - * or if the URL is malformed. - * The incoming URI should be properly escaped (no raw spaces, etc. URI percent-encoding is expected). - */ -- (NSString *)filesystemPathForURL:(CDVFilesystemURL *)url -{ - return [self filesystemPathForFullPath:url.fullPath]; -} - -- (CDVFilesystemURL *)URLforFullPath:(NSString *)fullPath -{ - if (fullPath) { - NSString* escapedPath = [fullPath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; - if ([fullPath hasPrefix:@"/"]) { - return [CDVFilesystemURL fileSystemURLWithString:[NSString stringWithFormat:@"%@://localhost/%@%@", kCDVFilesystemURLPrefix, self.name, escapedPath]]; - } - return [CDVFilesystemURL fileSystemURLWithString:[NSString stringWithFormat:@"%@://localhost/%@/%@", kCDVFilesystemURLPrefix, self.name, escapedPath]]; - } - return nil; -} - -- (CDVFilesystemURL *)URLforFilesystemPath:(NSString *)path -{ - return [self URLforFullPath:[self fullPathForFileSystemPath:path]]; - -} - -- (NSString *)normalizePath:(NSString *)rawPath -{ - // If this is an absolute path, the first path component will be '/'. Skip it if that's the case - BOOL isAbsolutePath = [rawPath hasPrefix:@"/"]; - if (isAbsolutePath) { - rawPath = [rawPath substringFromIndex:1]; - } - NSMutableArray *components = [NSMutableArray arrayWithArray:[rawPath pathComponents]]; - for (int index = 0; index < [components count]; ++index) { - if ([[components objectAtIndex:index] isEqualToString:@".."]) { - [components removeObjectAtIndex:index]; - if (index > 0) { - [components removeObjectAtIndex:index-1]; - --index; - } - } - } - - if (isAbsolutePath) { - return [NSString stringWithFormat:@"/%@", [components componentsJoinedByString:@"/"]]; - } else { - return [components componentsJoinedByString:@"/"]; - } - - -} - -- (BOOL)valueForKeyIsNumber:(NSDictionary*)dict key:(NSString*)key -{ - BOOL bNumber = NO; - NSObject* value = dict[key]; - if (value) { - bNumber = [value isKindOfClass:[NSNumber class]]; - } - return bNumber; -} - -- (CDVPluginResult *)getFileForURL:(CDVFilesystemURL *)baseURI requestedPath:(NSString *)requestedPath options:(NSDictionary *)options -{ - CDVPluginResult* result = nil; - BOOL bDirRequest = NO; - BOOL create = NO; - BOOL exclusive = NO; - int errorCode = 0; // !!! risky - no error code currently defined for 0 - - if ([self valueForKeyIsNumber:options key:@"create"]) { - create = [(NSNumber*)[options valueForKey:@"create"] boolValue]; - } - if ([self valueForKeyIsNumber:options key:@"exclusive"]) { - exclusive = [(NSNumber*)[options valueForKey:@"exclusive"] boolValue]; - } - if ([self valueForKeyIsNumber:options key:@"getDir"]) { - // this will not exist for calls directly to getFile but will have been set by getDirectory before calling this method - bDirRequest = [(NSNumber*)[options valueForKey:@"getDir"] boolValue]; - } - // see if the requested path has invalid characters - should we be checking for more than just ":"? - if ([requestedPath rangeOfString:@":"].location != NSNotFound) { - errorCode = ENCODING_ERR; - } else { - // Build new fullPath for the requested resource. - // We concatenate the two paths together, and then scan the resulting string to remove - // parent ("..") references. Any parent references at the beginning of the string are - // silently removed. - NSString *combinedPath = [baseURI.fullPath stringByAppendingPathComponent:requestedPath]; - combinedPath = [self normalizePath:combinedPath]; - CDVFilesystemURL* requestedURL = [self URLforFullPath:combinedPath]; - - NSFileManager* fileMgr = [[NSFileManager alloc] init]; - BOOL bIsDir; - BOOL bExists = [fileMgr fileExistsAtPath:[self filesystemPathForURL:requestedURL] isDirectory:&bIsDir]; - if (bExists && (create == NO) && (bIsDir == !bDirRequest)) { - // path exists and is not of requested type - return TYPE_MISMATCH_ERR - errorCode = TYPE_MISMATCH_ERR; - } else if (!bExists && (create == NO)) { - // path does not exist and create is false - return NOT_FOUND_ERR - errorCode = NOT_FOUND_ERR; - } else if (bExists && (create == YES) && (exclusive == YES)) { - // file/dir already exists and exclusive and create are both true - return PATH_EXISTS_ERR - errorCode = PATH_EXISTS_ERR; - } else { - // if bExists and create == YES - just return data - // if bExists and create == NO - just return data - // if !bExists and create == YES - create and return data - BOOL bSuccess = YES; - NSError __autoreleasing* pError = nil; - if (!bExists && (create == YES)) { - if (bDirRequest) { - // create the dir - bSuccess = [fileMgr createDirectoryAtPath:[self filesystemPathForURL:requestedURL] withIntermediateDirectories:NO attributes:nil error:&pError]; - } else { - // create the empty file - bSuccess = [fileMgr createFileAtPath:[self filesystemPathForURL:requestedURL] contents:nil attributes:nil]; - } - } - if (!bSuccess) { - errorCode = ABORT_ERR; - if (pError) { - NSLog(@"error creating directory: %@", [pError localizedDescription]); - } - } else { - // NSLog(@"newly created file/dir (%@) exists: %d", reqFullPath, [fileMgr fileExistsAtPath:reqFullPath]); - // file existed or was created - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:[self makeEntryForPath:requestedURL.fullPath isDirectory:bDirRequest]]; - } - } // are all possible conditions met? - } - - if (errorCode > 0) { - // create error callback - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode]; - } - return result; - -} - -- (CDVPluginResult*)getParentForURL:(CDVFilesystemURL *)localURI -{ - CDVPluginResult* result = nil; - CDVFilesystemURL *newURI = nil; - if ([localURI.fullPath isEqualToString:@""]) { - // return self - newURI = localURI; - } else { - newURI = [CDVFilesystemURL fileSystemURLWithURL:[localURI.url URLByDeletingLastPathComponent]]; /* TODO: UGLY - FIX */ - } - NSFileManager* fileMgr = [[NSFileManager alloc] init]; - BOOL bIsDir; - BOOL bExists = [fileMgr fileExistsAtPath:[self filesystemPathForURL:newURI] isDirectory:&bIsDir]; - if (bExists) { - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:[self makeEntryForPath:newURI.fullPath isDirectory:bIsDir]]; - } else { - // invalid path or file does not exist - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR]; - } - return result; -} - -- (CDVPluginResult*)setMetadataForURL:(CDVFilesystemURL *)localURI withObject:(NSDictionary *)options -{ - BOOL ok = NO; - - NSString* filePath = [self filesystemPathForURL:localURI]; - // we only care about this iCloud key for now. - // set to 1/true to skip backup, set to 0/false to back it up (effectively removing the attribute) - NSString* iCloudBackupExtendedAttributeKey = @"com.apple.MobileBackup"; - id iCloudBackupExtendedAttributeValue = [options objectForKey:iCloudBackupExtendedAttributeKey]; - - if ((iCloudBackupExtendedAttributeValue != nil) && [iCloudBackupExtendedAttributeValue isKindOfClass:[NSNumber class]]) { - if (IsAtLeastiOSVersion(@"5.1")) { - NSURL* url = [NSURL fileURLWithPath:filePath]; - NSError* __autoreleasing error = nil; - - ok = [url setResourceValue:[NSNumber numberWithBool:[iCloudBackupExtendedAttributeValue boolValue]] forKey:NSURLIsExcludedFromBackupKey error:&error]; - } else { // below 5.1 (deprecated - only really supported in 5.01) - u_int8_t value = [iCloudBackupExtendedAttributeValue intValue]; - if (value == 0) { // remove the attribute (allow backup, the default) - ok = (removexattr([filePath fileSystemRepresentation], [iCloudBackupExtendedAttributeKey cStringUsingEncoding:NSUTF8StringEncoding], 0) == 0); - } else { // set the attribute (skip backup) - ok = (setxattr([filePath fileSystemRepresentation], [iCloudBackupExtendedAttributeKey cStringUsingEncoding:NSUTF8StringEncoding], &value, sizeof(value), 0, 0) == 0); - } - } - } - - if (ok) { - return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; - } else { - return [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR]; - } -} - -/* remove the file or directory (recursively) - * IN: - * NSString* fullPath - the full path to the file or directory to be removed - * NSString* callbackId - * called from remove and removeRecursively - check all pubic api specific error conditions (dir not empty, etc) before calling - */ - -- (CDVPluginResult*)doRemove:(NSString*)fullPath -{ - CDVPluginResult* result = nil; - BOOL bSuccess = NO; - NSError* __autoreleasing pError = nil; - NSFileManager* fileMgr = [[NSFileManager alloc] init]; - - @try { - bSuccess = [fileMgr removeItemAtPath:fullPath error:&pError]; - if (bSuccess) { - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; - } else { - // see if we can give a useful error - CDVFileError errorCode = ABORT_ERR; - NSLog(@"error removing filesystem entry at %@: %@", fullPath, [pError localizedDescription]); - if ([pError code] == NSFileNoSuchFileError) { - errorCode = NOT_FOUND_ERR; - } else if ([pError code] == NSFileWriteNoPermissionError) { - errorCode = NO_MODIFICATION_ALLOWED_ERR; - } - - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode]; - } - } @catch(NSException* e) { // NSInvalidArgumentException if path is . or .. - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:SYNTAX_ERR]; - } - - return result; -} - -- (CDVPluginResult *)removeFileAtURL:(CDVFilesystemURL *)localURI -{ - NSString *fileSystemPath = [self filesystemPathForURL:localURI]; - - NSFileManager* fileMgr = [[NSFileManager alloc] init]; - BOOL bIsDir = NO; - BOOL bExists = [fileMgr fileExistsAtPath:fileSystemPath isDirectory:&bIsDir]; - if (!bExists) { - return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR]; - } - if (bIsDir && ([[fileMgr contentsOfDirectoryAtPath:fileSystemPath error:nil] count] != 0)) { - // dir is not empty - return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:INVALID_MODIFICATION_ERR]; - } - return [self doRemove:fileSystemPath]; -} - -- (CDVPluginResult *)recursiveRemoveFileAtURL:(CDVFilesystemURL *)localURI -{ - NSString *fileSystemPath = [self filesystemPathForURL:localURI]; - return [self doRemove:fileSystemPath]; -} - -/* - * IN - * NSString localURI - * OUT - * NSString full local filesystem path for the represented file or directory, or nil if no such path is possible - * The file or directory does not necessarily have to exist. nil is returned if the filesystem type is not recognized, - * or if the URL is malformed. - * The incoming URI should be properly escaped (no raw spaces, etc. URI percent-encoding is expected). - */ -- (NSString *)fullPathForFileSystemPath:(NSString *)fsPath -{ - if ([fsPath hasPrefix:self.fsRoot]) { - return [fsPath substringFromIndex:[self.fsRoot length]]; - } - return nil; -} - - -- (CDVPluginResult *)readEntriesAtURL:(CDVFilesystemURL *)localURI -{ - NSFileManager* fileMgr = [[NSFileManager alloc] init]; - NSError* __autoreleasing error = nil; - NSString *fileSystemPath = [self filesystemPathForURL:localURI]; - - NSArray* contents = [fileMgr contentsOfDirectoryAtPath:fileSystemPath error:&error]; - - if (contents) { - NSMutableArray* entries = [NSMutableArray arrayWithCapacity:1]; - if ([contents count] > 0) { - // create an Entry (as JSON) for each file/dir - for (NSString* name in contents) { - // see if is dir or file - NSString* entryPath = [fileSystemPath stringByAppendingPathComponent:name]; - BOOL bIsDir = NO; - [fileMgr fileExistsAtPath:entryPath isDirectory:&bIsDir]; - NSDictionary* entryDict = [self makeEntryForPath:[self fullPathForFileSystemPath:entryPath] isDirectory:bIsDir]; - [entries addObject:entryDict]; - } - } - return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:entries]; - } else { - // assume not found but could check error for more specific error conditions - return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR]; - } -} - -- (unsigned long long)truncateFile:(NSString*)filePath atPosition:(unsigned long long)pos -{ - unsigned long long newPos = 0UL; - - NSFileHandle* file = [NSFileHandle fileHandleForWritingAtPath:filePath]; - - if (file) { - [file truncateFileAtOffset:(unsigned long long)pos]; - newPos = [file offsetInFile]; - [file synchronizeFile]; - [file closeFile]; - } - return newPos; -} - -- (CDVPluginResult *)truncateFileAtURL:(CDVFilesystemURL *)localURI atPosition:(unsigned long long)pos -{ - unsigned long long newPos = [self truncateFile:[self filesystemPathForURL:localURI] atPosition:pos]; - return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:(int)newPos]; -} - -- (CDVPluginResult *)writeToFileAtURL:(CDVFilesystemURL *)localURL withData:(NSData*)encData append:(BOOL)shouldAppend -{ - NSString *filePath = [self filesystemPathForURL:localURL]; - - CDVPluginResult* result = nil; - CDVFileError errCode = INVALID_MODIFICATION_ERR; - int bytesWritten = 0; - - if (filePath) { - NSOutputStream* fileStream = [NSOutputStream outputStreamToFileAtPath:filePath append:shouldAppend]; - if (fileStream) { - NSUInteger len = [encData length]; - if (len == 0) { - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDouble:(double)len]; - } else { - [fileStream open]; - - bytesWritten = (int)[fileStream write:[encData bytes] maxLength:len]; - - [fileStream close]; - if (bytesWritten > 0) { - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:bytesWritten]; - // } else { - // can probably get more detailed error info via [fileStream streamError] - // errCode already set to INVALID_MODIFICATION_ERR; - // bytesWritten = 0; // may be set to -1 on error - } - } - } // else fileStream not created return INVALID_MODIFICATION_ERR - } else { - // invalid filePath - errCode = NOT_FOUND_ERR; - } - if (!result) { - // was an error - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:errCode]; - } - return result; -} - -/** - * Helper function to check to see if the user attempted to copy an entry into its parent without changing its name, - * or attempted to copy a directory into a directory that it contains directly or indirectly. - * - * IN: - * NSString* srcDir - * NSString* destinationDir - * OUT: - * YES copy/ move is allows - * NO move is onto itself - */ -- (BOOL)canCopyMoveSrc:(NSString*)src ToDestination:(NSString*)dest -{ - // This weird test is to determine if we are copying or moving a directory into itself. - // Copy /Documents/myDir to /Documents/myDir-backup is okay but - // Copy /Documents/myDir to /Documents/myDir/backup not okay - BOOL copyOK = YES; - NSRange range = [dest rangeOfString:src]; - - if (range.location != NSNotFound) { - NSRange testRange = {range.length - 1, ([dest length] - range.length)}; - NSRange resultRange = [dest rangeOfString:@"/" options:0 range:testRange]; - if (resultRange.location != NSNotFound) { - copyOK = NO; - } - } - return copyOK; -} - -- (void)copyFileToURL:(CDVFilesystemURL *)destURL withName:(NSString *)newName fromFileSystem:(NSObject<CDVFileSystem> *)srcFs atURL:(CDVFilesystemURL *)srcURL copy:(BOOL)bCopy callback:(void (^)(CDVPluginResult *))callback -{ - NSFileManager *fileMgr = [[NSFileManager alloc] init]; - NSString *destRootPath = [self filesystemPathForURL:destURL]; - BOOL bDestIsDir = NO; - BOOL bDestExists = [fileMgr fileExistsAtPath:destRootPath isDirectory:&bDestIsDir]; - - NSString *newFileSystemPath = [destRootPath stringByAppendingPathComponent:newName]; - NSString *newFullPath = [self fullPathForFileSystemPath:newFileSystemPath]; - - BOOL bNewIsDir = NO; - BOOL bNewExists = [fileMgr fileExistsAtPath:newFileSystemPath isDirectory:&bNewIsDir]; - - CDVPluginResult *result = nil; - int errCode = 0; - - if (!bDestExists) { - // the destination root does not exist - errCode = NOT_FOUND_ERR; - } - - else if ([srcFs isKindOfClass:[CDVLocalFilesystem class]]) { - /* Same FS, we can shortcut with NSFileManager operations */ - NSString *srcFullPath = [srcFs filesystemPathForURL:srcURL]; - - BOOL bSrcIsDir = NO; - BOOL bSrcExists = [fileMgr fileExistsAtPath:srcFullPath isDirectory:&bSrcIsDir]; - - if (!bSrcExists) { - // the source does not exist - errCode = NOT_FOUND_ERR; - } else if ([newFileSystemPath isEqualToString:srcFullPath]) { - // source and destination can not be the same - errCode = INVALID_MODIFICATION_ERR; - } else if (bSrcIsDir && (bNewExists && !bNewIsDir)) { - // can't copy/move dir to file - errCode = INVALID_MODIFICATION_ERR; - } else { // no errors yet - NSError* __autoreleasing error = nil; - BOOL bSuccess = NO; - if (bCopy) { - if (bSrcIsDir && ![self canCopyMoveSrc:srcFullPath ToDestination:newFileSystemPath]) { - // can't copy dir into self - errCode = INVALID_MODIFICATION_ERR; - } else if (bNewExists) { - // the full destination should NOT already exist if a copy - errCode = PATH_EXISTS_ERR; - } else { - bSuccess = [fileMgr copyItemAtPath:srcFullPath toPath:newFileSystemPath error:&error]; - } - } else { // move - // iOS requires that destination must not exist before calling moveTo - // is W3C INVALID_MODIFICATION_ERR error if destination dir exists and has contents - // - if (!bSrcIsDir && (bNewExists && bNewIsDir)) { - // can't move a file to directory - errCode = INVALID_MODIFICATION_ERR; - } else if (bSrcIsDir && ![self canCopyMoveSrc:srcFullPath ToDestination:newFileSystemPath]) { - // can't move a dir into itself - errCode = INVALID_MODIFICATION_ERR; - } else if (bNewExists) { - if (bNewIsDir && ([[fileMgr contentsOfDirectoryAtPath:newFileSystemPath error:NULL] count] != 0)) { - // can't move dir to a dir that is not empty - errCode = INVALID_MODIFICATION_ERR; - newFileSystemPath = nil; // so we won't try to move - } else { - // remove destination so can perform the moveItemAtPath - bSuccess = [fileMgr removeItemAtPath:newFileSystemPath error:NULL]; - if (!bSuccess) { - errCode = INVALID_MODIFICATION_ERR; // is this the correct error? - newFileSystemPath = nil; - } - } - } else if (bNewIsDir && [newFileSystemPath hasPrefix:srcFullPath]) { - // can't move a directory inside itself or to any child at any depth; - errCode = INVALID_MODIFICATION_ERR; - newFileSystemPath = nil; - } - - if (newFileSystemPath != nil) { - bSuccess = [fileMgr moveItemAtPath:srcFullPath toPath:newFileSystemPath error:&error]; - } - } - if (bSuccess) { - // should verify it is there and of the correct type??? - NSDictionary* newEntry = [self makeEntryForPath:newFullPath isDirectory:bSrcIsDir]; - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:newEntry]; - } else { - if (error) { - if (([error code] == NSFileReadUnknownError) || ([error code] == NSFileReadTooLargeError)) { - errCode = NOT_READABLE_ERR; - } else if ([error code] == NSFileWriteOutOfSpaceError) { - errCode = QUOTA_EXCEEDED_ERR; - } else if ([error code] == NSFileWriteNoPermissionError) { - errCode = NO_MODIFICATION_ALLOWED_ERR; - } - } - } - } - } else { - // Need to copy the hard way - [srcFs readFileAtURL:srcURL start:0 end:-1 callback:^(NSData* data, NSString* mimeType, CDVFileError errorCode) { - CDVPluginResult* result = nil; - if (data != nil) { - BOOL bSuccess = [data writeToFile:newFileSystemPath atomically:YES]; - if (bSuccess) { - // should verify it is there and of the correct type??? - NSDictionary* newEntry = [self makeEntryForPath:newFullPath isDirectory:NO]; - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:newEntry]; - } else { - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:ABORT_ERR]; - } - } else { - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode]; - } - callback(result); - }]; - return; // Async IO; return without callback. - } - if (result == nil) { - if (!errCode) { - errCode = INVALID_MODIFICATION_ERR; // Catch-all default - } - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errCode]; - } - callback(result); -} - -/* helper function to get the mimeType from the file extension - * IN: - * NSString* fullPath - filename (may include path) - * OUT: - * NSString* the mime type as type/subtype. nil if not able to determine - */ -+ (NSString*)getMimeTypeFromPath:(NSString*)fullPath -{ - NSString* mimeType = nil; - - if (fullPath) { - CFStringRef typeId = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)[fullPath pathExtension], NULL); - if (typeId) { - mimeType = (__bridge_transfer NSString*)UTTypeCopyPreferredTagWithClass(typeId, kUTTagClassMIMEType); - if (!mimeType) { - // special case for m4a - if ([(__bridge NSString*)typeId rangeOfString : @"m4a-audio"].location != NSNotFound) { - mimeType = @"audio/mp4"; - } else if ([[fullPath pathExtension] rangeOfString:@"wav"].location != NSNotFound) { - mimeType = @"audio/wav"; - } else if ([[fullPath pathExtension] rangeOfString:@"css"].location != NSNotFound) { - mimeType = @"text/css"; - } - } - CFRelease(typeId); - } - } - return mimeType; -} - -- (void)readFileAtURL:(CDVFilesystemURL *)localURL start:(NSInteger)start end:(NSInteger)end callback:(void (^)(NSData*, NSString* mimeType, CDVFileError))callback -{ - NSString *path = [self filesystemPathForURL:localURL]; - - NSString* mimeType = [CDVLocalFilesystem getMimeTypeFromPath:path]; - if (mimeType == nil) { - mimeType = @"*/*"; - } - NSFileHandle* file = [NSFileHandle fileHandleForReadingAtPath:path]; - if (start > 0) { - [file seekToFileOffset:start]; - } - - NSData* readData; - if (end < 0) { - readData = [file readDataToEndOfFile]; - } else { - readData = [file readDataOfLength:(end - start)]; - } - [file closeFile]; - - callback(readData, mimeType, readData != nil ? NO_ERROR : NOT_FOUND_ERR); -} - -- (void)getFileMetadataForURL:(CDVFilesystemURL *)localURL callback:(void (^)(CDVPluginResult *))callback -{ - NSString *path = [self filesystemPathForURL:localURL]; - CDVPluginResult *result; - NSFileManager* fileMgr = [[NSFileManager alloc] init]; - - NSError* __autoreleasing error = nil; - NSDictionary* fileAttrs = [fileMgr attributesOfItemAtPath:path error:&error]; - - if (fileAttrs) { - - // create dictionary of file info - NSMutableDictionary* fileInfo = [NSMutableDictionary dictionaryWithCapacity:5]; - - [fileInfo setObject:localURL.fullPath forKey:@"fullPath"]; - [fileInfo setObject:@"" forKey:@"type"]; // can't easily get the mimetype unless create URL, send request and read response so skipping - [fileInfo setObject:[path lastPathComponent] forKey:@"name"]; - - // Ensure that directories (and other non-regular files) report size of 0 - unsigned long long size = ([fileAttrs fileType] == NSFileTypeRegular ? [fileAttrs fileSize] : 0); - [fileInfo setObject:[NSNumber numberWithUnsignedLongLong:size] forKey:@"size"]; - - NSDate* modDate = [fileAttrs fileModificationDate]; - if (modDate) { - [fileInfo setObject:[NSNumber numberWithDouble:[modDate timeIntervalSince1970] * 1000] forKey:@"lastModifiedDate"]; - } - - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileInfo]; - - } else { - // didn't get fileAttribs - CDVFileError errorCode = ABORT_ERR; - NSLog(@"error getting metadata: %@", [error localizedDescription]); - if ([error code] == NSFileNoSuchFileError || [error code] == NSFileReadNoSuchFileError) { - errorCode = NOT_FOUND_ERR; - } - // log [NSNumber numberWithDouble: theMessage] objCtype to see what it returns - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:errorCode]; - } - - callback(result); -} - -@end diff --git a/plugins/cordova-plugin-file/src/ubuntu/file.cpp b/plugins/cordova-plugin-file/src/ubuntu/file.cpp deleted file mode 100644 index 395ab2dd..00000000 --- a/plugins/cordova-plugin-file/src/ubuntu/file.cpp +++ /dev/null @@ -1,912 +0,0 @@ -/* - * 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. - */ - -#include "file.h" - -#include <QApplication> - -namespace { - class FileError { - public: - static const QString kEncodingErr; - static const QString kTypeMismatchErr; - static const QString kNotFoundErr; - static const QString kSecurityErr; - static const QString kAbortErr; - static const QString kNotReadableErr; - static const QString kNoModificationAllowedErr; - static const QString kInvalidStateErr; - static const QString kSyntaxErr; - static const QString kInvalidModificationErr; - static const QString kQuotaExceededErr; - static const QString kPathExistsErr; - }; - - bool checkFileName(const QString &name) { - if (name.contains(":")){ - return false; - } - return true; - } -}; - -const QString FileError::kEncodingErr("FileError.ENCODING_ERR"); -const QString FileError::kTypeMismatchErr("FileError.TYPE_MISMATCH_ERR"); -const QString FileError::kNotFoundErr("FileError.NOT_FOUND_ERR"); -const QString FileError::kSecurityErr("FileError.SECURITY_ERR"); -const QString FileError::kAbortErr("FileError.ABORT_ERR"); -const QString FileError::kNotReadableErr("FileError.NOT_READABLE_ERR"); -const QString FileError::kNoModificationAllowedErr("FileError.NO_MODIFICATION_ALLOWED_ERR"); -const QString FileError::kInvalidStateErr("FileError.INVALID_STATE_ERR"); -const QString FileError::kSyntaxErr("FileError.SYNTAX_ERR"); -const QString FileError::kInvalidModificationErr("FileError.INVALID_MODIFICATION_ERR"); -const QString FileError::kQuotaExceededErr("FileError.QUOTA_EXCEEDED_ERR"); -const QString FileError::kPathExistsErr("FileError.PATH_EXISTS_ERR"); - -File::File(Cordova *cordova) : - CPlugin(cordova), - _persistentDir(QString("%1/.local/share/%2/persistent").arg(QDir::homePath()).arg(QCoreApplication::applicationName())) { - QDir::root().mkpath(_persistentDir.absolutePath()); -} - -QVariantMap File::file2map(const QFileInfo &fileInfo) { - QVariantMap res; - - res.insert("name", fileInfo.fileName()); - QPair<QString, QString> r = GetRelativePath(fileInfo); - res.insert("fullPath", QString("/") + r.second); - res.insert("filesystemName", r.first); - - res.insert("nativeURL", QString("file://localhost") + fileInfo.absoluteFilePath()); - res.insert("isDirectory", (int)fileInfo.isDir()); - res.insert("isFile", (int)fileInfo.isFile()); - - return res; -} - -QVariantMap File::dir2map(const QDir &dir) { - return file2map(QFileInfo(dir.absolutePath())); -} - -QPair<QString, QString> File::GetRelativePath(const QFileInfo &fileInfo) { - QString fullPath = fileInfo.isDir() ? QDir::cleanPath(fileInfo.absoluteFilePath()) : fileInfo.absoluteFilePath(); - - QString relativePath1 = _persistentDir.relativeFilePath(fullPath); - QString relativePath2 = QDir::temp().relativeFilePath(fullPath); - - if (!(relativePath1[0] != '.' || relativePath2[0] != '.')) { - if (relativePath1.size() > relativePath2.size()) { - return QPair<QString, QString>("temporary", relativePath2); - } else { - return QPair<QString, QString>("persistent", relativePath1); - } - } - - if (relativePath1[0] != '.') - return QPair<QString, QString>("persistent", relativePath1); - return QPair<QString, QString>("temporary", relativePath2); -} - -void File::requestFileSystem(int scId, int ecId, unsigned short type, unsigned long long size) { - QDir dir; - - if (size >= 1000485760){ - this->callback(ecId, FileError::kQuotaExceededErr); - return; - } - - if (type == 0) - dir = QDir::temp(); - else - dir = _persistentDir; - - if (type > 1) { - this->callback(ecId, FileError::kSyntaxErr); - return; - } else { - QVariantMap res; - res.insert("root", dir2map(dir)); - if (type == 0) - res.insert("name", "temporary"); - else - res.insert("name", "persistent"); - - this->cb(scId, res); - } -} - -QPair<bool, QFileInfo> File::resolveURI(int ecId, const QString &uri) { - QPair<bool, QFileInfo> result; - - result.first = false; - - QUrl url = QUrl::fromUserInput(uri); - - if (url.scheme() == "file" && url.isValid()) { - result.first = true; - result.second = QFileInfo(url.path()); - return result; - } - - if (url.scheme() != "cdvfile") { - if (ecId) - this->callback(ecId, FileError::kTypeMismatchErr); - return result; - } - - QString path = url.path().replace("//", "/"); - //NOTE: colon is not safe in url, it is not a valid path in Win and Mac, simple disable it here. - if (path.contains(":") || !url.isValid()){ - if (ecId) - this->callback(ecId, FileError::kEncodingErr); - return result; - } - if (!path.startsWith("/persistent/") && !path.startsWith("/temporary/")) { - if (ecId) - this->callback(ecId, FileError::kEncodingErr); - return result; - } - - result.first = true; - if (path.startsWith("/persistent/")) { - QString relativePath = path.mid(QString("/persistent/").size()); - result.second = QFileInfo(_persistentDir.filePath(relativePath)); - } else { - QString relativePath = path.mid(QString("/temporary/").size()); - result.second = QFileInfo(QDir::temp().filePath(relativePath)); - } - return result; -} - -QPair<bool, QFileInfo> File::resolveURI(const QString &uri) { - return resolveURI(0, uri); -} - - -void File::_getLocalFilesystemPath(int scId, int ecId, const QString& uri) { - QPair<bool, QFileInfo> f1 = resolveURI(ecId, uri); - - if (!f1.first) - return; - - this->cb(scId, f1.second.absoluteFilePath()); -} - -void File::resolveLocalFileSystemURI(int scId, int ecId, const QString &uri) { - if (uri[0] == '/' || uri[0] == '.') { - this->callback(ecId, FileError::kEncodingErr); - return; - } - - QPair<bool, QFileInfo> f1 = resolveURI(ecId, uri); - - if (!f1.first) - return; - - QFileInfo fileInfo = f1.second; - if (!fileInfo.exists()) { - this->callback(ecId, FileError::kNotFoundErr); - return; - } - - this->cb(scId, file2map(fileInfo)); -} - -void File::getFile(int scId, int ecId, const QString &parentPath, const QString &rpath, const QVariantMap &options) { - QPair<bool, QFileInfo> f1 = resolveURI(ecId, parentPath + "/" + rpath); - if (!f1.first) - return; - - bool create = options.value("create").toBool(); - bool exclusive = options.value("exclusive").toBool(); - QFile file(f1.second.absoluteFilePath()); - - // if create is false and the path represents a directory, return error - QFileInfo fileInfo = f1.second; - if ((!create) && fileInfo.isDir()) { - this->callback(ecId, FileError::kTypeMismatchErr); - return; - } - - // if file does exist, and create is true and exclusive is true, return error - if (file.exists()) { - if (create && exclusive) { - this->callback(ecId, FileError::kPathExistsErr); - return; - } - } - else { - // if file does not exist and create is false, return error - if (!create) { - this->callback(ecId, FileError::kNotFoundErr); - return; - } - - file.open(QIODevice::WriteOnly); - file.close(); - - // Check if creation was successfull - if (!file.exists()) { - this->callback(ecId, FileError::kNoModificationAllowedErr); - return; - } - } - - this->cb(scId, file2map(QFileInfo(file))); -} - -void File::getDirectory(int scId, int ecId, const QString &parentPath, const QString &rpath, const QVariantMap &options) { - QPair<bool, QFileInfo> f1 = resolveURI(ecId, parentPath + "/" + rpath); - if (!f1.first) - return; - - bool create = options.value("create").toBool(); - bool exclusive = options.value("exclusive").toBool(); - QDir dir(f1.second.absoluteFilePath()); - - QFileInfo &fileInfo = f1.second; - if ((!create) && fileInfo.isFile()) { - this->callback(ecId, FileError::kTypeMismatchErr); - return; - } - - if (dir.exists()) { - if (create && exclusive) { - this->callback(ecId, FileError::kPathExistsErr); - return; - } - } - else { - if (!create) { - this->callback(ecId, FileError::kNotFoundErr); - return; - } - - QString folderName = dir.dirName(); - dir.cdUp(); - dir.mkdir(folderName); - dir.cd(folderName); - - if (!dir.exists()) { - this->callback(ecId, FileError::kNoModificationAllowedErr); - return; - } - } - - this->cb(scId, dir2map(dir)); -} - -void File::removeRecursively(int scId, int ecId, const QString &uri) { - QPair<bool, QFileInfo> f1 = resolveURI(ecId, uri); - - if (!f1.first) - return; - - QDir dir(f1.second.absoluteFilePath()); - if (File::rmDir(dir)) - this->cb(scId); - else - this->callback(ecId, FileError::kNoModificationAllowedErr); -} - -void File::write(int scId, int ecId, const QString &uri, const QString &_data, unsigned long long position, bool binary) { - QPair<bool, QFileInfo> f1 = resolveURI(ecId, uri); - - if (!f1.first) - return; - - QFile file(f1.second.absoluteFilePath()); - - file.open(QIODevice::WriteOnly); - file.close(); - - if (!file.exists()) { - this->callback(ecId, FileError::kNotFoundErr); - return; - } - - QFileInfo fileInfo(file); - if (!file.open(QIODevice::ReadWrite)) { - this->callback(ecId, FileError::kNoModificationAllowedErr); - return; - } - - if (!binary) { - QTextStream textStream(&file); - textStream.setCodec("UTF-8"); - textStream.setAutoDetectUnicode(true); - - if (!textStream.seek(position)) { - file.close(); - fileInfo.refresh(); - - this->callback(ecId, FileError::kInvalidModificationErr); - return; - } - - textStream << _data; - textStream.flush(); - } else { - QByteArray data(_data.toUtf8()); - if (!file.seek(position)) { - file.close(); - fileInfo.refresh(); - - this->callback(ecId, FileError::kInvalidModificationErr); - return; - } - - file.write(data.data(), data.length()); - } - - file.flush(); - file.close(); - fileInfo.refresh(); - - this->cb(scId, fileInfo.size() - position); -} - -void File::truncate(int scId, int ecId, const QString &uri, unsigned long long size) { - QPair<bool, QFileInfo> f1 = resolveURI(ecId, uri); - - if (!f1.first) - return; - - QFile file(f1.second.absoluteFilePath()); - - if (!file.exists()) { - this->callback(ecId, FileError::kNotFoundErr); - return; - } - - if (!file.resize(size)) { - this->callback(ecId, FileError::kNoModificationAllowedErr); - return; - } - - this->cb(scId, size); -} - -void File::getParent(int scId, int ecId, const QString &uri) { - QPair<bool, QFileInfo> f1 = resolveURI(ecId, uri); - - if (!f1.first) - return; - QDir dir(f1.second.absoluteFilePath()); - - //can't cdup more than app's root - // Try to change into upper directory - if (dir != _persistentDir && dir != QDir::temp()){ - if (!dir.cdUp()) { - this->callback(ecId, FileError::kNotFoundErr); - return; - } - - } - this->cb(scId, dir2map(dir)); -} - -void File::remove(int scId, int ecId, const QString &uri) { - QPair<bool, QFileInfo> f1 = resolveURI(ecId, uri); - if (!f1.first) - return; - - QFileInfo &fileInfo = f1.second; - //TODO: fix - if (!fileInfo.exists() || (fileInfo.absoluteFilePath() == _persistentDir.absolutePath()) || (QDir::temp() == fileInfo.absoluteFilePath())) { - this->callback(ecId, FileError::kNoModificationAllowedErr); - return; - } - - if (fileInfo.isDir()) { - QDir dir(fileInfo.absoluteFilePath()); - if (dir.rmdir(dir.absolutePath())) { - this->cb(scId); - return; - } - } else { - QFile file(fileInfo.absoluteFilePath()); - if (file.remove()) { - this->cb(scId); - return; - } - } - - this->callback(ecId, FileError::kInvalidModificationErr); -} - -void File::getFileMetadata(int scId, int ecId, const QString &uri) { - QPair<bool, QFileInfo> f1 = resolveURI(ecId, uri); - - if (!f1.first) - return; - QFileInfo &fileInfo = f1.second; - - if (!fileInfo.exists()) { - this->callback(ecId, FileError::kNotFoundErr); - } else { - QMimeType mime = _db.mimeTypeForFile(fileInfo.fileName()); - - QString args = QString("{name: %1, fullPath: %2, type: %3, lastModifiedDate: new Date(%4), size: %5}") - .arg(CordovaInternal::format(fileInfo.fileName())).arg(CordovaInternal::format(fileInfo.absoluteFilePath())) - .arg(CordovaInternal::format(mime.name())).arg(fileInfo.lastModified().toMSecsSinceEpoch()) - .arg(fileInfo.size()); - - this->callback(scId, args); - } -} - -void File::getMetadata(int scId, int ecId, const QString &uri) { - QPair<bool, QFileInfo> f1 = resolveURI(ecId, uri); - - if (!f1.first) - return; - QFileInfo &fileInfo = f1.second; - - if (!fileInfo.exists()) - this->callback(ecId, FileError::kNotFoundErr); - else { - QVariantMap obj; - obj.insert("modificationTime", fileInfo.lastModified().toMSecsSinceEpoch()); - obj.insert("size", fileInfo.isDir() ? 0 : fileInfo.size()); - this->cb(scId, obj); - } -} - -void File::readEntries(int scId, int ecId, const QString &uri) { - QPair<bool, QFileInfo> f1 = resolveURI(ecId, uri); - - if (!f1.first) - return; - QDir dir(f1.second.absoluteFilePath()); - QString entriesList; - - if (!dir.exists()) { - this->callback(ecId, FileError::kNotFoundErr); - return; - } - - for (const QFileInfo &fileInfo: dir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot)) { - entriesList += CordovaInternal::format(file2map(fileInfo)) + ","; - } - // Remove trailing comma - if (entriesList.size() > 0) - entriesList.remove(entriesList.size() - 1, 1); - - entriesList = "new Array(" + entriesList + ")"; - - this->callback(scId, entriesList); -} - -void File::readAsText(int scId, int ecId, const QString &uri, const QString &/*encoding*/, int sliceStart, int sliceEnd) { - QPair<bool, QFileInfo> f1 = resolveURI(ecId, uri); - - if (!f1.first) - return; - - QFile file(f1.second.absoluteFilePath()); - - if (!file.exists()) { - this->callback(ecId, FileError::kNotFoundErr); - return; - } - - if (!file.open(QIODevice::ReadOnly)) { - this->callback(ecId, FileError::kNotReadableErr); - return; - } - - QByteArray content = file.readAll(); - - if (sliceEnd == -1) - sliceEnd = content.size(); - if (sliceEnd < 0) { - sliceEnd++; - sliceEnd = std::max(0, content.size() + sliceEnd); - } - if (sliceEnd > content.size()) - sliceEnd = content.size(); - - if (sliceStart < 0) - sliceStart = std::max(0, content.size() + sliceStart); - if (sliceStart > content.size()) - sliceStart = content.size(); - - if (sliceStart > sliceEnd) - sliceEnd = sliceStart; - - //FIXME: encoding - content = content.mid(sliceStart, sliceEnd - sliceStart); - - this->cb(scId, content); -} - -void File::readAsArrayBuffer(int scId, int ecId, const QString &uri, int sliceStart, int sliceEnd) { - const QString str2array("\ - (function strToArray(str) { \ - var res = new Uint8Array(str.length); \ - for (var i = 0; i < str.length; i++) { \ - res[i] = str.charCodeAt(i); \ - } \ - return res; \ - })(\"%1\")"); - - QPair<bool, QFileInfo> f1 = resolveURI(ecId, uri); - - if (!f1.first) - return; - - QFile file(f1.second.absoluteFilePath()); - - if (!file.exists()) { - this->callback(ecId, FileError::kNotFoundErr); - return; - } - - if (!file.open(QIODevice::ReadOnly)) { - this->callback(ecId, FileError::kNotReadableErr); - return; - } - QString res; - QByteArray content = file.readAll(); - - if (sliceEnd == -1) - sliceEnd = content.size(); - if (sliceEnd < 0) { - sliceEnd++; - sliceEnd = std::max(0, content.size() + sliceEnd); - } - if (sliceEnd > content.size()) - sliceEnd = content.size(); - - if (sliceStart < 0) - sliceStart = std::max(0, content.size() + sliceStart); - if (sliceStart > content.size()) - sliceStart = content.size(); - - if (sliceStart > sliceEnd) - sliceEnd = sliceStart; - - content = content.mid(sliceStart, sliceEnd - sliceStart); - - res.reserve(content.length() * 6); - for (uchar c: content) { - res += "\\x"; - res += QString::number(c, 16).rightJustified(2, '0').toUpper(); - } - - this->callback(scId, str2array.arg(res)); -} - -void File::readAsBinaryString(int scId, int ecId, const QString &uri, int sliceStart, int sliceEnd) { - QPair<bool, QFileInfo> f1 = resolveURI(ecId, uri); - - if (!f1.first) - return; - - QFile file(f1.second.absoluteFilePath()); - - if (!file.exists()) { - this->callback(ecId, FileError::kNotFoundErr); - return; - } - - if (!file.open(QIODevice::ReadOnly)) { - this->callback(ecId, FileError::kNotReadableErr); - return; - } - QString res; - QByteArray content = file.readAll(); - - if (sliceEnd == -1) - sliceEnd = content.size(); - if (sliceEnd < 0) { - sliceEnd++; - sliceEnd = std::max(0, content.size() + sliceEnd); - } - if (sliceEnd > content.size()) - sliceEnd = content.size(); - - if (sliceStart < 0) - sliceStart = std::max(0, content.size() + sliceStart); - if (sliceStart > content.size()) - sliceStart = content.size(); - - if (sliceStart > sliceEnd) - sliceEnd = sliceStart; - - content = content.mid(sliceStart, sliceEnd - sliceStart); - - res.reserve(content.length() * 6); - for (uchar c: content) { - res += "\\x"; - res += QString::number(c, 16).rightJustified(2, '0').toUpper(); - } - this->callback(scId, "\"" + res + "\""); -} - -void File::readAsDataURL(int scId, int ecId, const QString &uri, int sliceStart, int sliceEnd) { - QPair<bool, QFileInfo> f1 = resolveURI(ecId, uri); - - if (!f1.first) - return; - - QFile file(f1.second.absoluteFilePath()); - QFileInfo &fileInfo = f1.second; - - if (!file.exists()) { - this->callback(ecId, FileError::kNotReadableErr); - return; - } - - if (!file.open(QIODevice::ReadOnly)) { - this->callback(ecId, FileError::kNotReadableErr); - return; - } - - QByteArray content = file.readAll(); - QString contentType(_db.mimeTypeForFile(fileInfo.fileName()).name()); - - if (sliceEnd == -1) - sliceEnd = content.size(); - if (sliceEnd < 0) { - sliceEnd++; - sliceEnd = std::max(0, content.size() + sliceEnd); - } - if (sliceEnd > content.size()) - sliceEnd = content.size(); - - if (sliceStart < 0) - sliceStart = std::max(0, content.size() + sliceStart); - if (sliceStart > content.size()) - sliceStart = content.size(); - - if (sliceStart > sliceEnd) - sliceEnd = sliceStart; - - content = content.mid(sliceStart, sliceEnd - sliceStart); - - this->cb(scId, QString("data:%1;base64,").arg(contentType) + content.toBase64()); -} - -bool File::rmDir(const QDir &dir) { - if (dir == _persistentDir || dir == QDir::temp()) {//can't remove root dir - return false; - } - bool result = true; - if (dir.exists()) { - // Iterate over entries and remove them - Q_FOREACH(const QFileInfo &fileInfo, dir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot)) { - if (fileInfo.isDir()) { - result = rmDir(fileInfo.absoluteFilePath()); - } - else { - result = QFile::remove(fileInfo.absoluteFilePath()); - } - - if (!result) { - return result; - } - } - - // Finally remove the current dir - return dir.rmdir(dir.absolutePath()); - } - return result; -} - -bool File::copyFile(int scId, int ecId,const QString& sourceUri, const QString& destinationUri, const QString& newName) { - QPair<bool, QFileInfo> destDir = resolveURI(ecId, destinationUri); - QPair<bool, QFileInfo> sourceFile = resolveURI(ecId, sourceUri); - - if (!destDir.first || !sourceFile.first) - return false; - - if (!checkFileName(newName)) { - this->callback(ecId, FileError::kEncodingErr); - return false; - } - - if (destDir.second.isFile()) { - this->callback(ecId, FileError::kInvalidModificationErr); - return false; - } - - if (!destDir.second.isDir()) { - this->callback(ecId, FileError::kNotFoundErr); - return false; - } - - QFileInfo &fileInfo = sourceFile.second; - QString fileName((newName.isEmpty()) ? fileInfo.fileName() : newName); - QString destinationFile(QDir(destDir.second.absoluteFilePath()).filePath(fileName)); - if (QFile::copy(fileInfo.absoluteFilePath(), destinationFile)){ - this->cb(scId, file2map(QFileInfo(destinationFile))); - return true; - } - this->callback(ecId, FileError::kInvalidModificationErr); - return false; -} - -void File::copyDir(int scId, int ecId,const QString& sourceUri, const QString& destinationUri, const QString& newName) { - QPair<bool, QFileInfo> destDir = resolveURI(ecId, destinationUri); - QPair<bool, QFileInfo> sourceDir = resolveURI(ecId, sourceUri); - - if (!destDir.first || !sourceDir.first) - return; - if (!checkFileName(newName)) { - this->callback(ecId, FileError::kEncodingErr); - return; - } - - QString targetName = ((newName.isEmpty()) ? sourceDir.second.fileName() : newName); - QString target(QDir(destDir.second.absoluteFilePath()).filePath(targetName)); - - if (QFileInfo(target).isFile()){ - this->callback(ecId, FileError::kInvalidModificationErr); - return; - } - - // check: copy directory into itself - if (QDir(sourceDir.second.absoluteFilePath()).relativeFilePath(target)[0] != '.'){ - this->callback(ecId, FileError::kInvalidModificationErr); - return; - } - - if (!QDir(target).exists()){ - QDir(destDir.second.absoluteFilePath()).mkdir(target);; - } else{ - this->callback(ecId, FileError::kInvalidModificationErr); - return; - } - - if (copyFolder(sourceDir.second.absoluteFilePath(), target)){ - this->cb(scId, dir2map(QDir(target))); - return; - } - this->callback(ecId, FileError::kInvalidModificationErr); - return; -} - -void File::copyTo(int scId, int ecId, const QString& source, const QString& destinationDir, const QString& newName) { - QPair<bool, QFileInfo> f1 = resolveURI(ecId, source); - - if (!f1.first) - return; - - if (f1.second.isDir()) - copyDir(scId, ecId, source, destinationDir, newName); - else - copyFile(scId, ecId, source, destinationDir, newName); -} - -void File::moveFile(int scId, int ecId,const QString& sourceUri, const QString& destinationUri, const QString& newName) { - QPair<bool, QFileInfo> sourceFile = resolveURI(ecId, sourceUri); - QPair<bool, QFileInfo> destDir = resolveURI(ecId, destinationUri); - - if (!destDir.first || !sourceFile.first) - return; - if (!checkFileName(newName)) { - this->callback(ecId, FileError::kEncodingErr); - return; - } - - QString fileName = ((newName.isEmpty()) ? sourceFile.second.fileName() : newName); - QString target = QDir(destDir.second.absoluteFilePath()).filePath(fileName); - - if (sourceFile.second == QFileInfo(target)) { - this->callback(ecId, FileError::kInvalidModificationErr); - return; - } - - if (!destDir.second.exists()) { - this->callback(ecId, FileError::kNotFoundErr); - return; - } - if (!destDir.second.isDir()){ - this->callback(ecId, FileError::kInvalidModificationErr); - return; - } - - if (QFileInfo(target).exists()) { - if (!QFile::remove(target)) { - this->callback(ecId, FileError::kInvalidModificationErr); - return; - } - } - - QFile::rename(sourceFile.second.absoluteFilePath(), target); - this->cb(scId, file2map(QFileInfo(target))); -} - -void File::moveDir(int scId, int ecId,const QString& sourceUri, const QString& destinationUri, const QString& newName){ - QPair<bool, QFileInfo> sourceDir = resolveURI(ecId, sourceUri); - QPair<bool, QFileInfo> destDir = resolveURI(ecId, destinationUri); - - if (!destDir.first || !sourceDir.first) - return; - if (!checkFileName(newName)) { - this->callback(ecId, FileError::kEncodingErr); - return; - } - - QString fileName = ((newName.isEmpty()) ? sourceDir.second.fileName() : newName); - QString target = QDir(destDir.second.absoluteFilePath()).filePath(fileName); - - if (!destDir.second.exists()){ - this->callback(ecId, FileError::kNotFoundErr); - return; - } - - if (destDir.second.isFile()){ - this->callback(ecId, FileError::kInvalidModificationErr); - return; - } - - // check: copy directory into itself - if (QDir(sourceDir.second.absoluteFilePath()).relativeFilePath(target)[0] != '.'){ - this->callback(ecId, FileError::kInvalidModificationErr); - return; - } - - if (QFileInfo(target).exists() && !QDir(destDir.second.absoluteFilePath()).rmdir(fileName)) { - this->callback(ecId, FileError::kInvalidModificationErr); - return; - } - - if (copyFolder(sourceDir.second.absoluteFilePath(), target)) { - rmDir(sourceDir.second.absoluteFilePath()); - this->cb(scId, file2map(QFileInfo(target))); - } else { - this->callback(ecId, FileError::kNoModificationAllowedErr); - } -} - -void File::moveTo(int scId, int ecId, const QString& source, const QString& destinationDir, const QString& newName) { - QPair<bool, QFileInfo> f1 = resolveURI(ecId, source); - - if (!f1.first) - return; - - if (f1.second.isDir()) - moveDir(scId, ecId, source, destinationDir, newName); - else - moveFile(scId, ecId, source, destinationDir, newName); -} - -bool File::copyFolder(const QString& sourceFolder, const QString& destFolder) { - QDir sourceDir(sourceFolder); - if (!sourceDir.exists()) - return false; - QDir destDir(destFolder); - if (!destDir.exists()){ - destDir.mkdir(destFolder); - } - QStringList files = sourceDir.entryList(QDir::Files); - for (int i = 0; i< files.count(); i++) - { - QString srcName = sourceFolder + "/" + files[i]; - QString destName = destFolder + "/" + files[i]; - QFile::copy(srcName, destName); - } - files.clear(); - files = sourceDir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot); - for (int i = 0; i< files.count(); i++) - { - QString srcName = sourceFolder + "/" + files[i]; - QString destName = destFolder + "/" + files[i]; - copyFolder(srcName, destName); - } - return true; -} diff --git a/plugins/cordova-plugin-file/src/ubuntu/file.h b/plugins/cordova-plugin-file/src/ubuntu/file.h deleted file mode 100644 index de277623..00000000 --- a/plugins/cordova-plugin-file/src/ubuntu/file.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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. - */ - -#ifndef FILEAPI_H_SDASDASDAS -#define FILEAPI_H_SDASDASDAS - -#include <QNetworkReply> -#include <QtCore> - -#include <cplugin.h> -#include <cordova.h> - -class File: public CPlugin { - Q_OBJECT -public: - explicit File(Cordova *cordova); - - virtual const QString fullName() override { - return File::fullID(); - } - - virtual const QString shortName() override { - return "File"; - } - - static const QString fullID() { - return "File"; - } - QPair<bool, QFileInfo> resolveURI(const QString &uri); - QPair<bool, QFileInfo> resolveURI(int ecId, const QString &uri); - QVariantMap file2map(const QFileInfo &dir); - -public slots: - void requestFileSystem(int scId, int ecId, unsigned short type, unsigned long long size); - void resolveLocalFileSystemURI(int scId, int ecId, const QString&); - void getDirectory(int scId, int ecId, const QString&, const QString&, const QVariantMap&); - void getFile(int scId, int ecId, const QString &parentPath, const QString &rpath, const QVariantMap &options); - void readEntries(int scId, int ecId, const QString &uri); - void getParent(int scId, int ecId, const QString &uri); - void copyTo(int scId, int ecId, const QString& source, const QString& destinationDir, const QString& newName); - void moveTo(int scId, int ecId, const QString& source, const QString& destinationDir, const QString& newName); - void getFileMetadata(int scId, int ecId, const QString &); - void getMetadata(int scId, int ecId, const QString &); - void remove(int scId, int ecId, const QString &); - void removeRecursively(int scId, int ecId, const QString&); - void write(int scId, int ecId, const QString&, const QString&, unsigned long long position, bool binary); - void readAsText(int scId, int ecId, const QString&, const QString &encoding, int sliceStart, int sliceEnd); - void readAsDataURL(int scId, int ecId, const QString&, int sliceStart, int sliceEnd); - void readAsArrayBuffer(int scId, int ecId, const QString&, int sliceStart, int sliceEnd); - void readAsBinaryString(int scId, int ecId, const QString&, int sliceStart, int sliceEnd); - void truncate(int scId, int ecId, const QString&, unsigned long long size); - - void _getLocalFilesystemPath(int scId, int ecId, const QString&); -private: - void moveFile(int scId, int ecId,const QString&, const QString&, const QString&); - void moveDir(int scId, int ecId,const QString&, const QString&, const QString&); - bool copyFile(int scId, int ecId, const QString&, const QString&, const QString&); - void copyDir(int scId, int ecId, const QString&, const QString&, const QString&); - bool rmDir(const QDir &dir); - bool copyFolder(const QString&, const QString&); - - QPair<QString, QString> GetRelativePath(const QFileInfo &fileInfo); - QVariantMap dir2map(const QDir &dir); - - QMimeDatabase _db; - const QDir _persistentDir; - QNetworkAccessManager _manager; -}; - -#endif diff --git a/plugins/cordova-plugin-file/src/windows/FileProxy.js b/plugins/cordova-plugin-file/src/windows/FileProxy.js deleted file mode 100644 index d1769b7b..00000000 --- a/plugins/cordova-plugin-file/src/windows/FileProxy.js +++ /dev/null @@ -1,1186 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - * -*/ - -var cordova = require('cordova'); -var File = require('./File'), - FileError = require('./FileError'), - Flags = require('./Flags'), - FileSystem = require('./FileSystem'), - LocalFileSystem = require('./LocalFileSystem'), - utils = require('cordova/utils'); - -function Entry(isFile, isDirectory, name, fullPath, filesystemName, nativeURL) { - this.isFile = !!isFile; - this.isDirectory = !!isDirectory; - this.name = name || ''; - this.fullPath = fullPath || ''; - this.filesystemName = filesystemName || null; - this.nativeURL = nativeURL || null; -} - -var FileEntry = function(name, fullPath, filesystemName, nativeURL) { - FileEntry.__super__.constructor.apply(this, [true, false, name, fullPath, filesystemName, nativeURL]); -}; - -utils.extend(FileEntry, Entry); - -var DirectoryEntry = function(name, fullPath, filesystemName, nativeURL) { - DirectoryEntry.__super__.constructor.call(this, false, true, name, fullPath, filesystemName, nativeURL); -}; - -utils.extend(DirectoryEntry, Entry); - - -var getFolderFromPathAsync = Windows.Storage.StorageFolder.getFolderFromPathAsync; -var getFileFromPathAsync = Windows.Storage.StorageFile.getFileFromPathAsync; - -function writeBytesAsync(storageFile, data, position) { - return storageFile.openAsync(Windows.Storage.FileAccessMode.readWrite) - .then(function (output) { - output.seek(position); - var dataWriter = new Windows.Storage.Streams.DataWriter(output); - dataWriter.writeBytes(data); - return dataWriter.storeAsync().then(function (size) { - output.size = position+size; - return dataWriter.flushAsync().then(function() { - output.close(); - return size; - }); - }); - }); -} - -function writeTextAsync(storageFile, data, position) { - return storageFile.openAsync(Windows.Storage.FileAccessMode.readWrite) - .then(function (output) { - output.seek(position); - var dataWriter = new Windows.Storage.Streams.DataWriter(output); - dataWriter.writeString(data); - return dataWriter.storeAsync().then(function (size) { - output.size = position+size; - return dataWriter.flushAsync().then(function() { - output.close(); - return size; - }); - }); - }); -} - -function writeBlobAsync(storageFile, data, position) { - return storageFile.openAsync(Windows.Storage.FileAccessMode.readWrite) - .then(function (output) { - output.seek(position); - var dataSize = data.size; - var input = (data.detachStream || data.msDetachStream).call(data); - - // Copy the stream from the blob to the File stream - return Windows.Storage.Streams.RandomAccessStream.copyAsync(input, output) - .then(function () { - output.size = position+dataSize; - return output.flushAsync().then(function () { - input.close(); - output.close(); - - return dataSize; - }); - }); - }); -} - -function writeArrayBufferAsync(storageFile, data, position) { - return writeBlobAsync(storageFile, new Blob([data]), position); -} - -function cordovaPathToNative(path) { - // turn / into \\ - var cleanPath = path.replace(/\//g, '\\'); - // turn \\ into \ - cleanPath = cleanPath.replace(/\\+/g, '\\'); - return cleanPath; -} - -function nativePathToCordova(path) { - var cleanPath = path.replace(/\\/g, '/'); - return cleanPath; -} - -var driveRE = new RegExp("^[/]*([A-Z]:)"); -var invalidNameRE = /[\\?*|"<>:]/; -function validName(name) { - return !invalidNameRE.test(name.replace(driveRE,'')); -} - -function sanitize(path) { - var slashesRE = new RegExp('/{2,}','g'); - var components = path.replace(slashesRE, '/').split(/\/+/); - // Remove double dots, use old school array iteration instead of RegExp - // since it is impossible to debug them - for (var index = 0; index < components.length; ++index) { - if (components[index] === "..") { - components.splice(index, 1); - if (index > 0) { - // if we're not in the start of array then remove preceeding path component, - // In case if relative path points above the root directory, just ignore double dots - // See file.spec.111 should not traverse above above the root directory for test case - components.splice(index-1, 1); - --index; - } - } - } - return components.join('/'); -} - -var WinFS = function(name, root) { - this.winpath = root.winpath; - if (this.winpath && !/\/$/.test(this.winpath)) { - this.winpath += "/"; - } - this.makeNativeURL = function(path) { - return encodeURI(this.root.nativeURL + sanitize(path.replace(':','%3A')));}; - root.fullPath = '/'; - if (!root.nativeURL) - root.nativeURL = 'file://'+sanitize(this.winpath + root.fullPath).replace(':','%3A'); - WinFS.__super__.constructor.call(this, name, root); -}; - -utils.extend(WinFS, FileSystem); - -WinFS.prototype.__format__ = function(fullPath) { - var path = sanitize('/'+this.name+(fullPath[0]==='/'?'':'/')+encodeURI(fullPath)); - return 'cdvfile://localhost' + path; -}; - -var AllFileSystems; - -function getAllFS() { - if (!AllFileSystems) { - var storageFolderPermanent = Windows.Storage.ApplicationData.current.localFolder.path, - storageFolderTemporary = Windows.Storage.ApplicationData.current.temporaryFolder.path; - AllFileSystems = { - 'persistent': - Object.freeze(new WinFS('persistent', { - name: 'persistent', - nativeURL: 'ms-appdata:///local', - winpath: nativePathToCordova(Windows.Storage.ApplicationData.current.localFolder.path) - })), - 'temporary': - Object.freeze(new WinFS('temporary', { - name: 'temporary', - nativeURL: 'ms-appdata:///temp', - winpath: nativePathToCordova(Windows.Storage.ApplicationData.current.temporaryFolder.path) - })), - 'root': - Object.freeze(new WinFS('root', { - name: 'root', - //nativeURL: 'file:///' - winpath: '' - })) - }; - } - return AllFileSystems; -} - -function getFS(name) { - return getAllFS()[name]; -} - -FileSystem.prototype.__format__ = function(fullPath) { - return getFS(this.name).__format__(fullPath); -}; - -require('./fileSystems').getFs = function(name, callback) { - setTimeout(function(){callback(getFS(name));}); -}; - -function getFilesystemFromPath(path) { - var res; - var allfs = getAllFS(); - Object.keys(allfs).some(function(fsn) { - var fs = allfs[fsn]; - if (path.indexOf(fs.winpath) === 0) - res = fs; - return res; - }); - return res; -} - -var msapplhRE = new RegExp('^ms-appdata://localhost/'); -function pathFromURL(url) { - url=url.replace(msapplhRE,'ms-appdata:///'); - var path = decodeURI(url); - // support for file name with parameters - if (/\?/g.test(path)) { - path = String(path).split("?")[0]; - } - if (path.indexOf("file:/")===0) { - if (path.indexOf("file://") !== 0) { - url = "file:///" + url.substr(6); - } - } - - ['file://','ms-appdata:///','cdvfile://localhost/'].every(function(p) { - if (path.indexOf(p)!==0) - return true; - var thirdSlash = path.indexOf("/", p.length); - if (thirdSlash < 0) { - path = ""; - } else { - path = sanitize(path.substr(thirdSlash)); - } - }); - - return path.replace('%3A',':').replace(driveRE,'$1'); -} - -function getFilesystemFromURL(url) { - url=url.replace(msapplhRE,'ms-appdata:///'); - var res; - if (url.indexOf("file:/")===0) - res = getFilesystemFromPath(pathFromURL(url)); - else { - var allfs = getAllFS(); - Object.keys(allfs).every(function(fsn) { - var fs = allfs[fsn]; - if (url.indexOf(fs.root.nativeURL) === 0 || - url.indexOf('cdvfile://localhost/'+fs.name+'/') === 0) - { - res = fs; - return false; - } - return true; - }); - } - return res; -} - -function getFsPathForWinPath(fs, wpath) { - var path = nativePathToCordova(wpath); - if (path.indexOf(fs.winpath) !== 0) - return null; - return path.replace(fs.winpath,'/'); -} - -var WinError = { - invalidArgument: -2147024809, - fileNotFound: -2147024894, - accessDenied: -2147024891 -}; - -function openPath(path, ops) { - ops=ops?ops:{}; - return new WinJS.Promise(function (complete,failed) { - getFileFromPathAsync(path).done( - function(file) { - complete({file:file}); - }, - function(err) { - if (err.number != WinError.fileNotFound && err.number != WinError.invalidArgument) - failed(FileError.NOT_READABLE_ERR); - getFolderFromPathAsync(path) - .done( - function(dir) { - if (!ops.getContent) - complete({folder:dir}); - else - WinJS.Promise.join({ - files:dir.getFilesAsync(), - folders:dir.getFoldersAsync() - }).done( - function(a) { - complete({ - folder:dir, - files:a.files, - folders:a.folders - }); - }, - function(err) { - failed(FileError.NOT_READABLE_ERR); - } - ); - }, - function(err) { - if (err.number == WinError.fileNotFound || err.number == WinError.invalidArgument) - complete({}); - else - failed(FileError.NOT_READABLE_ERR); - } - ); - } - ); - }); -} - -function copyFolder(src,dst,name) { - name = name?name:src.name; - return new WinJS.Promise(function (complete,failed) { - WinJS.Promise.join({ - fld:dst.createFolderAsync(name, Windows.Storage.CreationCollisionOption.openIfExists), - files:src.getFilesAsync(), - folders:src.getFoldersAsync() - }).done( - function(the) { - if (!(the.files.length || the.folders.length)) { - complete(); - return; - } - var todo = the.files.length; - var copyfolders = function() { - if (!todo--) { - complete(); - return; - } - copyFolder(the.folders[todo],dst) - .done(function() {copyfolders(); }, failed); - }; - var copyfiles = function() { - if (!todo--) { - todo = the.folders.length; - copyfolders(); - return; - } - the.files[todo].copyAsync(the.fld) - .done(function() {copyfiles(); }, failed); - }; - copyfiles(); - }, - failed - ); - }); -} - -function moveFolder(src,dst,name) { - name = name?name:src.name; - return new WinJS.Promise(function (complete,failed) { - var pending = []; - WinJS.Promise.join({ - fld:dst.createFolderAsync(name, Windows.Storage.CreationCollisionOption.openIfExists), - files:src.getFilesAsync(), - folders:src.getFoldersAsync() - }).done( - function(the) { - if (!(the.files.length || the.folders.length)) { - complete(); - return; - } - var todo = the.files.length; - var movefolders = function() { - if (!todo--) { - src.deleteAsync().done(complete,failed); - return; - } - moveFolder(the.folders[todo],dst) - .done(movefolders,failed); - }; - var movefiles = function() { - if (!todo--) { - todo = the.folders.length; - movefolders(); - return; - } - the.files[todo].moveAsync(the.fld) - .done(function() {movefiles(); }, failed); - }; - movefiles(); - }, - failed - ); - }); -} - -function transport(success, fail, args, ops) { // ["fullPath","parent", "newName"] - var src = args[0]; - var parent = args[1]; - var name = args[2]; - - var srcFS = getFilesystemFromURL(src); - var dstFS = getFilesystemFromURL(parent); - var srcPath = pathFromURL(src); - var dstPath = pathFromURL(parent); - if (!(srcFS && dstFS && validName(name))){ - fail(FileError.ENCODING_ERR); - return; - } - - var srcWinPath = cordovaPathToNative(sanitize(srcFS.winpath + srcPath)); - var dstWinPath = cordovaPathToNative(sanitize(dstFS.winpath + dstPath)); - var tgtFsPath = sanitize(dstPath+'/'+name); - var tgtWinPath = cordovaPathToNative(sanitize(dstFS.winpath + dstPath+'/'+name)); - if (srcWinPath == dstWinPath || srcWinPath == tgtWinPath) { - fail(FileError.INVALID_MODIFICATION_ERR); - return; - } - - - WinJS.Promise.join({ - src:openPath(srcWinPath), - dst:openPath(dstWinPath), - tgt:openPath(tgtWinPath,{getContent:true}) - }) - .done( - function (the) { - if ((!the.dst.folder) || !(the.src.folder || the.src.file)) { - fail(FileError.NOT_FOUND_ERR); - return; - } - if ( (the.src.folder && the.tgt.file) - || (the.src.file && the.tgt.folder) - || (the.tgt.folder && (the.tgt.files.length || the.tgt.folders.length))) - { - fail(FileError.INVALID_MODIFICATION_ERR); - return; - } - if (the.src.file) - ops.fileOp(the.src.file,the.dst.folder, name, Windows.Storage.NameCollisionOption.replaceExisting) - .done( - function (storageFile) { - success(new FileEntry( - name, - tgtFsPath, - dstFS.name, - dstFS.makeNativeURL(tgtFsPath) - )); - }, - function (err) { - fail(FileError.INVALID_MODIFICATION_ERR); - } - ); - else - ops.folderOp(the.src.folder, the.dst.folder, name).done( - function () { - success(new DirectoryEntry( - name, - tgtFsPath, - dstFS.name, - dstFS.makeNativeURL(tgtFsPath) - )); - }, - function() { - fail(FileError.INVALID_MODIFICATION_ERR); - } - ); - }, - function(err) { - fail(FileError.INVALID_MODIFICATION_ERR); - } - ); -} - -module.exports = { - requestAllFileSystems: function() { - return getAllFS(); - }, - getFileMetadata: function (success, fail, args) { - module.exports.getMetadata(success, fail, args); - }, - - getMetadata: function (success, fail, args) { - var fs = getFilesystemFromURL(args[0]); - var path = pathFromURL(args[0]); - if (!fs || !validName(path)){ - fail(FileError.ENCODING_ERR); - return; - } - var fullPath = cordovaPathToNative(fs.winpath + path); - - var getMetadataForFile = function (storageFile) { - storageFile.getBasicPropertiesAsync().then( - function (basicProperties) { - success(new File(storageFile.name, storageFile.path, storageFile.fileType, basicProperties.dateModified, basicProperties.size)); - }, function () { - fail(FileError.NOT_READABLE_ERR); - } - ); - }; - - var getMetadataForFolder = function (storageFolder) { - storageFolder.getBasicPropertiesAsync().then( - function (basicProperties) { - var metadata = { - size: basicProperties.size, - lastModifiedDate: basicProperties.dateModified - }; - success(metadata); - }, - function () { - fail(FileError.NOT_READABLE_ERR); - } - ); - }; - - getFileFromPathAsync(fullPath).then(getMetadataForFile, - function () { - getFolderFromPathAsync(fullPath).then(getMetadataForFolder, - function () { - fail(FileError.NOT_FOUND_ERR); - } - ); - } - ); - }, - - getParent: function (win, fail, args) { // ["fullPath"] - var fs = getFilesystemFromURL(args[0]); - var path = pathFromURL(args[0]); - if (!fs || !validName(path)){ - fail(FileError.ENCODING_ERR); - return; - } - if (!path || (new RegExp('/[^/]*/?$')).test(path)) { - win(new DirectoryEntry(fs.root.name, fs.root.fullPath, fs.name, fs.makeNativeURL(fs.root.fullPath))); - return; - } - - var parpath = path.replace(new RegExp('/[^/]+/?$','g'),''); - var parname = path.substr(parpath.length); - var fullPath = cordovaPathToNative(fs.winpath + parpath); - - var result = new DirectoryEntry(parname, parpath, fs.name, fs.makeNativeURL(parpath)); - getFolderFromPathAsync(fullPath).done( - function () { win(result); }, - function () { fail(FileError.INVALID_STATE_ERR); } - ); - }, - - readAsText: function (win, fail, args) { - - var url = args[0], - enc = args[1], - startPos = args[2], - endPos = args[3]; - - var fs = getFilesystemFromURL(url); - var path = pathFromURL(url); - if (!fs){ - fail(FileError.ENCODING_ERR); - return; - } - var wpath = cordovaPathToNative(sanitize(fs.winpath + path)); - - var encoding = Windows.Storage.Streams.UnicodeEncoding.utf8; - if (enc == 'Utf16LE' || enc == 'utf16LE') { - encoding = Windows.Storage.Streams.UnicodeEncoding.utf16LE; - } else if (enc == 'Utf16BE' || enc == 'utf16BE') { - encoding = Windows.Storage.Streams.UnicodeEncoding.utf16BE; - } - - getFileFromPathAsync(wpath).then(function(file) { - return file.openReadAsync(); - }).then(function (stream) { - startPos = (startPos < 0) ? Math.max(stream.size + startPos, 0) : Math.min(stream.size, startPos); - endPos = (endPos < 0) ? Math.max(endPos + stream.size, 0) : Math.min(stream.size, endPos); - stream.seek(startPos); - - var readSize = endPos - startPos, - buffer = new Windows.Storage.Streams.Buffer(readSize); - - return stream.readAsync(buffer, readSize, Windows.Storage.Streams.InputStreamOptions.none); - }).done(function(buffer) { - win(Windows.Security.Cryptography.CryptographicBuffer.convertBinaryToString(encoding, buffer)); - },function() { - fail(FileError.NOT_FOUND_ERR); - }); - }, - - readAsBinaryString:function(win,fail,args) { - var url = args[0], - startPos = args[1], - endPos = args[2]; - - var fs = getFilesystemFromURL(url); - var path = pathFromURL(url); - if (!fs){ - fail(FileError.ENCODING_ERR); - return; - } - var wpath = cordovaPathToNative(sanitize(fs.winpath + path)); - - getFileFromPathAsync(wpath).then( - function (storageFile) { - Windows.Storage.FileIO.readBufferAsync(storageFile).done( - function (buffer) { - var dataReader = Windows.Storage.Streams.DataReader.fromBuffer(buffer); - // var fileContent = dataReader.readString(buffer.length); - var byteArray = new Uint8Array(buffer.length), - byteString = ""; - dataReader.readBytes(byteArray); - dataReader.close(); - for (var i = 0; i < byteArray.length; i++) { - var charByte = byteArray[i]; - // var charRepresentation = charByte <= 127 ? String.fromCharCode(charByte) : charByte.toString(16); - var charRepresentation = String.fromCharCode(charByte); - byteString += charRepresentation; - } - win(byteString.slice(startPos, endPos)); - } - ); - }, function () { - fail(FileError.NOT_FOUND_ERR); - } - ); - }, - - readAsArrayBuffer:function(win,fail,args) { - var url = args[0]; - var fs = getFilesystemFromURL(url); - var path = pathFromURL(url); - if (!fs){ - fail(FileError.ENCODING_ERR); - return; - } - var wpath = cordovaPathToNative(sanitize(fs.winpath + path)); - - getFileFromPathAsync(wpath).then( - function (storageFile) { - var blob = MSApp.createFileFromStorageFile(storageFile); - var url = URL.createObjectURL(blob, { oneTimeOnly: true }); - var xhr = new XMLHttpRequest(); - xhr.open("GET", url, true); - xhr.responseType = 'arraybuffer'; - xhr.onload = function () { - var resultArrayBuffer = xhr.response; - // get start and end position of bytes in buffer to be returned - var startPos = args[1] || 0, - endPos = args[2] || resultArrayBuffer.length; - // if any of them is specified, we'll slice output array - if (startPos !== 0 || endPos !== resultArrayBuffer.length) { - // slice method supported only on Windows 8.1, so we need to check if it's available - // see http://msdn.microsoft.com/en-us/library/ie/dn641192(v=vs.94).aspx - if (resultArrayBuffer.slice) { - resultArrayBuffer = resultArrayBuffer.slice(startPos, endPos); - } else { - // if slice isn't available, we'll use workaround method - var tempArray = new Uint8Array(resultArrayBuffer), - resBuffer = new ArrayBuffer(endPos - startPos), - resArray = new Uint8Array(resBuffer); - - for (var i = 0; i < resArray.length; i++) { - resArray[i] = tempArray[i + startPos]; - } - resultArrayBuffer = resBuffer; - } - } - win(resultArrayBuffer); - }; - xhr.send(); - }, function () { - fail(FileError.NOT_FOUND_ERR); - } - ); - }, - - readAsDataURL: function (win, fail, args) { - var url = args[0]; - var fs = getFilesystemFromURL(url); - var path = pathFromURL(url); - if (!fs){ - fail(FileError.ENCODING_ERR); - return; - } - var wpath = cordovaPathToNative(sanitize(fs.winpath + path)); - - getFileFromPathAsync(wpath).then( - function (storageFile) { - Windows.Storage.FileIO.readBufferAsync(storageFile).done( - function (buffer) { - var strBase64 = Windows.Security.Cryptography.CryptographicBuffer.encodeToBase64String(buffer); - //the method encodeToBase64String will add "77u/" as a prefix, so we should remove it - if(String(strBase64).substr(0,4) == "77u/") { - strBase64 = strBase64.substr(4); - } - var mediaType = storageFile.contentType; - var result = "data:" + mediaType + ";base64," + strBase64; - win(result); - } - ); - }, function () { - fail(FileError.NOT_FOUND_ERR); - } - ); - }, - - getDirectory: function (win, fail, args) { - var dirurl = args[0]; - var path = args[1]; - var options = args[2]; - - var fs = getFilesystemFromURL(dirurl); - var dirpath = pathFromURL(dirurl); - if (!fs || !validName(path)){ - fail(FileError.ENCODING_ERR); - return; - } - var fspath = sanitize(dirpath +'/'+ path); - var completePath = sanitize(fs.winpath + fspath); - - var name = completePath.substring(completePath.lastIndexOf('/')+1); - - var wpath = cordovaPathToNative(completePath.substring(0, completePath.lastIndexOf('/'))); - - var flag = ""; - if (options) { - flag = new Flags(options.create, options.exclusive); - } else { - flag = new Flags(false, false); - } - - getFolderFromPathAsync(wpath).done( - function (storageFolder) { - if (flag.create === true && flag.exclusive === true) { - storageFolder.createFolderAsync(name, Windows.Storage.CreationCollisionOption.failIfExists).done( - function (storageFolder) { - win(new DirectoryEntry(storageFolder.name, fspath, fs.name, fs.makeNativeURL(fspath))); - }, function (err) { - fail(FileError.PATH_EXISTS_ERR); - } - ); - } else if (flag.create === true && flag.exclusive === false) { - storageFolder.createFolderAsync(name, Windows.Storage.CreationCollisionOption.openIfExists).done( - function (storageFolder) { - win(new DirectoryEntry(storageFolder.name, fspath, fs.name, fs.makeNativeURL(fspath))); - }, function () { - fail(FileError.INVALID_MODIFICATION_ERR); - } - ); - } else if (flag.create === false) { - storageFolder.getFolderAsync(name).done( - function (storageFolder) { - win(new DirectoryEntry(storageFolder.name, fspath, fs.name, fs.makeNativeURL(fspath))); - }, - function () { - // check if path actually points to a file - storageFolder.getFileAsync(name).done( - function () { - fail(FileError.TYPE_MISMATCH_ERR); - }, function() { - fail(FileError.NOT_FOUND_ERR); - } - ); - } - ); - } - }, function () { - fail(FileError.NOT_FOUND_ERR); - } - ); - }, - - remove: function (win, fail, args) { - var fs = getFilesystemFromURL(args[0]); - var path = pathFromURL(args[0]); - if (!fs || !validName(path)){ - fail(FileError.ENCODING_ERR); - return; - } - - // FileSystem root can't be removed! - if (!path || path=='/'){ - fail(FileError.NO_MODIFICATION_ALLOWED_ERR); - return; - } - var fullPath = cordovaPathToNative(fs.winpath + path); - - getFileFromPathAsync(fullPath).then( - function (storageFile) { - storageFile.deleteAsync().done(win, function () { - fail(FileError.INVALID_MODIFICATION_ERR); - }); - }, - function () { - getFolderFromPathAsync(fullPath).done( - function (sFolder) { - sFolder.getFilesAsync() - // check for files - .then(function(fileList) { - if (fileList) { - if (fileList.length === 0) { - return sFolder.getFoldersAsync(); - } else { - fail(FileError.INVALID_MODIFICATION_ERR); - } - } - }) - // check for folders - .done(function (folderList) { - if (folderList) { - if (folderList.length === 0) { - sFolder.deleteAsync().done( - win, - function () { - fail(FileError.INVALID_MODIFICATION_ERR); - } - ); - } else { - fail(FileError.INVALID_MODIFICATION_ERR); - } - } - }); - }, - function () { - fail(FileError.NOT_FOUND_ERR); - } - ); - } - ); - }, - - removeRecursively: function (successCallback, fail, args) { - - var fs = getFilesystemFromURL(args[0]); - var path = pathFromURL(args[0]); - if (!fs || !validName(path)){ - fail(FileError.ENCODING_ERR); - return; - } - - // FileSystem root can't be removed! - if (!path || path=='/'){ - fail(FileError.NO_MODIFICATION_ALLOWED_ERR); - return; - } - var fullPath = cordovaPathToNative(fs.winpath + path); - - getFolderFromPathAsync(fullPath).done(function (storageFolder) { - storageFolder.deleteAsync().done(function (res) { - successCallback(res); - }, function (err) { - fail(err); - }); - - }, function () { - fail(FileError.FILE_NOT_FOUND_ERR); - }); - }, - - getFile: function (win, fail, args) { - - var dirurl = args[0]; - var path = args[1]; - var options = args[2]; - - var fs = getFilesystemFromURL(dirurl); - var dirpath = pathFromURL(dirurl); - if (!fs || !validName(path)){ - fail(FileError.ENCODING_ERR); - return; - } - var fspath = sanitize(dirpath +'/'+ path); - var completePath = sanitize(fs.winpath + fspath); - - var fileName = completePath.substring(completePath.lastIndexOf('/')+1); - - var wpath = cordovaPathToNative(completePath.substring(0, completePath.lastIndexOf('/'))); - - var flag = ""; - if (options !== null) { - flag = new Flags(options.create, options.exclusive); - } else { - flag = new Flags(false, false); - } - - getFolderFromPathAsync(wpath).done( - function (storageFolder) { - if (flag.create === true && flag.exclusive === true) { - storageFolder.createFileAsync(fileName, Windows.Storage.CreationCollisionOption.failIfExists).done( - function (storageFile) { - win(new FileEntry(storageFile.name, fspath, fs.name, fs.makeNativeURL(fspath))); - }, function () { - fail(FileError.PATH_EXISTS_ERR); - } - ); - } else if (flag.create === true && flag.exclusive === false) { - storageFolder.createFileAsync(fileName, Windows.Storage.CreationCollisionOption.openIfExists).done( - function (storageFile) { - win(new FileEntry(storageFile.name, fspath, fs.name, fs.makeNativeURL(fspath))); - }, function () { - fail(FileError.INVALID_MODIFICATION_ERR); - } - ); - } else if (flag.create === false) { - storageFolder.getFileAsync(fileName).done( - function (storageFile) { - win(new FileEntry(storageFile.name, fspath, fs.name, fs.makeNativeURL(fspath))); - }, function () { - // check if path actually points to a folder - storageFolder.getFolderAsync(fileName).done( - function () { - fail(FileError.TYPE_MISMATCH_ERR); - }, function () { - fail(FileError.NOT_FOUND_ERR); - }); - } - ); - } - }, function (err) { - fail( - err.number == WinError.accessDenied? - FileError.SECURITY_ERR: - FileError.NOT_FOUND_ERR - ); - } - ); - }, - - readEntries: function (win, fail, args) { // ["fullPath"] - var fs = getFilesystemFromURL(args[0]); - var path = pathFromURL(args[0]); - if (!fs || !validName(path)){ - fail(FileError.ENCODING_ERR); - return; - } - var fullPath = cordovaPathToNative(fs.winpath + path); - - var result = []; - - getFolderFromPathAsync(fullPath).done(function (storageFolder) { - var promiseArr = []; - var index = 0; - promiseArr[index++] = storageFolder.getFilesAsync().then(function (fileList) { - if (fileList !== null) { - for (var i = 0; i < fileList.length; i++) { - var fspath = getFsPathForWinPath(fs, fileList[i].path); - if (!fspath) { - fail(FileError.NOT_FOUND_ERR); - return; - } - result.push(new FileEntry(fileList[i].name, fspath, fs.name, fs.makeNativeURL(fspath))); - } - } - }); - promiseArr[index++] = storageFolder.getFoldersAsync().then(function (folderList) { - if (folderList !== null) { - for (var j = 0; j < folderList.length; j++) { - var fspath = getFsPathForWinPath(fs, folderList[j].path); - if (!fspath) { - fail(FileError.NOT_FOUND_ERR); - return; - } - result.push(new DirectoryEntry(folderList[j].name, fspath, fs.name, fs.makeNativeURL(fspath))); - } - } - }); - WinJS.Promise.join(promiseArr).then(function () { - win(result); - }); - - }, function () { fail(FileError.NOT_FOUND_ERR); }); - }, - - write: function (win, fail, args) { - - var url = args[0], - data = args[1], - position = args[2], - isBinary = args[3]; - - var fs = getFilesystemFromURL(url); - var path = pathFromURL(url); - if (!fs){ - fail(FileError.ENCODING_ERR); - return; - } - var completePath = sanitize(fs.winpath + path); - var fileName = completePath.substring(completePath.lastIndexOf('/')+1); - var dirpath = completePath.substring(0,completePath.lastIndexOf('/')); - var wpath = cordovaPathToNative(dirpath); - - function getWriteMethodForData(data, isBinary) { - - if (data instanceof Blob) { - return writeBlobAsync; - } - - if (data instanceof ArrayBuffer) { - return writeArrayBufferAsync; - } - - if (isBinary) { - return writeBytesAsync; - } - - if (typeof data === 'string') { - return writeTextAsync; - } - - throw new Error('Unsupported data type for write method'); - } - - var writePromise = getWriteMethodForData(data, isBinary); - - getFolderFromPathAsync(wpath).done( - function (storageFolder) { - storageFolder.createFileAsync(fileName, Windows.Storage.CreationCollisionOption.openIfExists).done( - function (storageFile) { - writePromise(storageFile, data, position).done( - function (bytesWritten) { - var written = bytesWritten || data.length; - win(written); - }, - function () { - fail(FileError.INVALID_MODIFICATION_ERR); - } - ); - }, - function () { - fail(FileError.INVALID_MODIFICATION_ERR); - } - ); - - }, - function () { - fail(FileError.NOT_FOUND_ERR); - } - ); - }, - - truncate: function (win, fail, args) { // ["fileName","size"] - var url = args[0]; - var size = args[1]; - - var fs = getFilesystemFromURL(url); - var path = pathFromURL(url); - if (!fs){ - fail(FileError.ENCODING_ERR); - return; - } - var completePath = sanitize(fs.winpath + path); - var wpath = cordovaPathToNative(completePath); - var dirwpath = cordovaPathToNative(completePath.substring(0,completePath.lastIndexOf('/'))); - - getFileFromPathAsync(wpath).done(function(storageFile){ - //the current length of the file. - var leng = 0; - - storageFile.getBasicPropertiesAsync().then(function (basicProperties) { - leng = basicProperties.size; - if (Number(size) >= leng) { - win(this.length); - return; - } - if (Number(size) >= 0) { - Windows.Storage.FileIO.readTextAsync(storageFile, Windows.Storage.Streams.UnicodeEncoding.utf8).then(function (fileContent) { - fileContent = fileContent.substr(0, size); - var fullPath = storageFile.path; - var name = storageFile.name; - storageFile.deleteAsync().then(function () { - return getFolderFromPathAsync(dirwpath); - }).done(function (storageFolder) { - storageFolder.createFileAsync(name).then(function (newStorageFile) { - Windows.Storage.FileIO.writeTextAsync(newStorageFile, fileContent).done(function () { - win(String(fileContent).length); - }, function () { - fail(FileError.NO_MODIFICATION_ALLOWED_ERR); - }); - }, function() { - fail(FileError.NO_MODIFICATION_ALLOWED_ERR); - }); - }); - }, function () { fail(FileError.NOT_FOUND_ERR); }); - } - }); - }, function () { fail(FileError.NOT_FOUND_ERR); }); - }, - - copyTo: function (success, fail, args) { // ["fullPath","parent", "newName"] - transport(success, fail, args, - { - fileOp:function(file,folder,name,coll) { - return file.copyAsync(folder,name,coll); - }, - folderOp:function(src,dst,name) { - return copyFolder(src,dst,name); - }} - ); - }, - - moveTo: function (success, fail, args) { - transport(success, fail, args, - { - fileOp:function(file,folder,name,coll) { - return file.moveAsync(folder,name,coll); - }, - folderOp:function(src,dst,name) { - return moveFolder(src,dst,name); - }} - ); - }, - tempFileSystem:null, - - persistentFileSystem:null, - - requestFileSystem: function (win, fail, args) { - - var type = args[0]; - var size = args[1]; - var MAX_SIZE = 10000000000; - if (size > MAX_SIZE) { - fail(FileError.QUOTA_EXCEEDED_ERR); - return; - } - - var fs; - switch (type) { - case LocalFileSystem.TEMPORARY: - fs = getFS('temporary'); - break; - case LocalFileSystem.PERSISTENT: - fs = getFS('persistent'); - break; - } - if (fs) - win(fs); - else - fail(FileError.NOT_FOUND_ERR); - }, - - resolveLocalFileSystemURI: function (success, fail, args) { - - var uri = args[0]; - var inputURL; - - var path = pathFromURL(uri); - var fs = getFilesystemFromURL(uri); - if (!fs || !validName(path)) { - fail(FileError.ENCODING_ERR); - return; - } - if (path.indexOf(fs.winpath) === 0) - path=path.substr(fs.winpath.length); - var abspath = cordovaPathToNative(fs.winpath+path); - - getFileFromPathAsync(abspath).done( - function (storageFile) { - success(new FileEntry(storageFile.name, path, fs.name, fs.makeNativeURL(path))); - }, function () { - getFolderFromPathAsync(abspath).done( - function (storageFolder) { - success(new DirectoryEntry(storageFolder.name, path, fs.name,fs.makeNativeURL(path))); - }, function () { - fail(FileError.NOT_FOUND_ERR); - } - ); - } - ); - } - - -}; - -require("cordova/exec/proxy").add("File",module.exports); diff --git a/plugins/cordova-plugin-file/src/wp/File.cs b/plugins/cordova-plugin-file/src/wp/File.cs deleted file mode 100644 index 203d8d42..00000000 --- a/plugins/cordova-plugin-file/src/wp/File.cs +++ /dev/null @@ -1,1800 +0,0 @@ -/* - 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.Diagnostics; -using System.IO; -using System.IO.IsolatedStorage; -using System.Runtime.Serialization; -using System.Security; -using System.Text; -using System.Windows; -using System.Windows.Resources; -using WPCordovaClassLib.Cordova.JSON; - -namespace WPCordovaClassLib.Cordova.Commands -{ - /// <summary> - /// Provides access to isolated storage - /// </summary> - 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; - - /// <summary> - /// Temporary directory name - /// </summary> - private readonly string TMP_DIRECTORY_NAME = "tmp"; - - /// <summary> - /// Represents error code for callback - /// </summary> - [DataContract] - public class ErrorCode - { - /// <summary> - /// Error code - /// </summary> - [DataMember(IsRequired = true, Name = "code")] - public int Code { get; set; } - - /// <summary> - /// Creates ErrorCode object - /// </summary> - public ErrorCode(int code) - { - this.Code = code; - } - } - - /// <summary> - /// Represents File action options. - /// </summary> - [DataContract] - public class FileOptions - { - /// <summary> - /// File path - /// </summary> - /// - 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; - } - } - - /// <summary> - /// Full entryPath - /// </summary> - [DataMember(Name = "fullPath")] - public string FullPath { get; set; } - - /// <summary> - /// Directory name - /// </summary> - [DataMember(Name = "dirName")] - public string DirectoryName { get; set; } - - /// <summary> - /// Path to create file/directory - /// </summary> - [DataMember(Name = "path")] - public string Path { get; set; } - - /// <summary> - /// The encoding to use to encode the file's content. Default is UTF8. - /// </summary> - [DataMember(Name = "encoding")] - public string Encoding { get; set; } - - /// <summary> - /// Uri to get file - /// </summary> - /// - 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; - } - } - - /// <summary> - /// Size to truncate file - /// </summary> - [DataMember(Name = "size")] - public long Size { get; set; } - - /// <summary> - /// Data to write in file - /// </summary> - [DataMember(Name = "data")] - public string Data { get; set; } - - /// <summary> - /// Position the writing starts with - /// </summary> - [DataMember(Name = "position")] - public int Position { get; set; } - - /// <summary> - /// Type of file system requested - /// </summary> - [DataMember(Name = "type")] - public int FileSystemType { get; set; } - - /// <summary> - /// New file/directory name - /// </summary> - [DataMember(Name = "newName")] - public string NewName { get; set; } - - /// <summary> - /// Destination directory to copy/move file/directory - /// </summary> - [DataMember(Name = "parent")] - public string Parent { get; set; } - - /// <summary> - /// Options for getFile/getDirectory methods - /// </summary> - [DataMember(Name = "options")] - public CreatingOptions CreatingOpt { get; set; } - - /// <summary> - /// Creates options object with default parameters - /// </summary> - public FileOptions() - { - this.SetDefaultValues(new StreamingContext()); - } - - /// <summary> - /// Initializes default values for class fields. - /// Implemented in separate method because default constructor is not invoked during deserialization. - /// </summary> - /// <param name="context"></param> - [OnDeserializing()] - public void SetDefaultValues(StreamingContext context) - { - this.Encoding = "UTF-8"; - this.FilePath = ""; - this.FileSystemType = -1; - } - } - - /// <summary> - /// Stores image info - /// </summary> - [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) - { - if (string.IsNullOrEmpty(filePath)) - { - throw new FileNotFoundException("File doesn't exist"); - } - - this.FullPath = filePath; - this.Size = 0; - this.FileName = string.Empty; - - using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) - { - bool IsFile = isoFile.FileExists(filePath); - bool IsDirectory = isoFile.DirectoryExists(filePath); - - if (!IsDirectory) - { - if (!IsFile) // special case, if isoFile cannot find it, it might still be part of the app-package - { - // attempt to get it from the resources - - Uri fileUri = new Uri(filePath, UriKind.Relative); - StreamResourceInfo streamInfo = Application.GetResourceStream(fileUri); - if (streamInfo != null) - { - this.Size = streamInfo.Stream.Length; - this.FileName = filePath.Substring(filePath.LastIndexOf("/") + 1); - } - else - { - throw new FileNotFoundException("File doesn't exist"); - } - } - else - { - using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(filePath, FileMode.Open, FileAccess.Read, isoFile)) - { - this.Size = stream.Length; - } - - this.FileName = System.IO.Path.GetFileName(filePath); - this.LastModifiedDate = isoFile.GetLastWriteTime(filePath).DateTime.ToString(); - } - } - - this.Type = MimeTypeMapper.GetMimeType(this.FileName); - } - } - } - - /// <summary> - /// Represents file or directory modification metadata - /// </summary> - [DataContract] - public class ModificationMetadata - { - /// <summary> - /// Modification time - /// </summary> - [DataMember] - public string modificationTime { get; set; } - } - - /// <summary> - /// Represents file or directory entry - /// </summary> - [DataContract] - public class FileEntry - { - - /// <summary> - /// File type - /// </summary> - [DataMember(Name = "isFile")] - public bool IsFile { get; set; } - - /// <summary> - /// Directory type - /// </summary> - [DataMember(Name = "isDirectory")] - public bool IsDirectory { get; set; } - - /// <summary> - /// File/directory name - /// </summary> - [DataMember(Name = "name")] - public string Name { get; set; } - - /// <summary> - /// Full path to file/directory - /// </summary> - [DataMember(Name = "fullPath")] - public string FullPath { get; set; } - - /// <summary> - /// URI encoded fullpath - /// </summary> - [DataMember(Name = "nativeURL")] - public string NativeURL - { - set { } - get - { - string escaped = Uri.EscapeUriString(this.FullPath); - escaped = escaped.Replace("//", "/"); - if (escaped.StartsWith("/")) - { - escaped = escaped.Insert(0, "/"); - } - return escaped; - } - } - - public bool IsResource { get; set; } - - public static FileEntry GetEntry(string filePath, bool bIsRes=false) - { - FileEntry entry = null; - try - { - entry = new FileEntry(filePath, bIsRes); - - } - catch (Exception ex) - { - Debug.WriteLine("Exception in GetEntry for filePath :: " + filePath + " " + ex.Message); - } - return entry; - } - - /// <summary> - /// Creates object and sets necessary properties - /// </summary> - /// <param name="filePath"></param> - public FileEntry(string filePath, bool bIsRes = false) - { - if (string.IsNullOrEmpty(filePath)) - { - throw new ArgumentException(); - } - - if(filePath.Contains(" ")) - { - Debug.WriteLine("FilePath with spaces :: " + filePath); - } - - using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) - { - IsResource = bIsRes; - IsFile = isoFile.FileExists(filePath); - 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 - { - if (IsResource) - { - this.Name = Path.GetFileName(filePath); - } - else - { - throw new FileNotFoundException(); - } - } - - try - { - this.FullPath = filePath.Replace('\\', '/'); // new Uri(filePath).LocalPath; - } - catch (Exception) - { - this.FullPath = filePath; - } - } - } - - /// <summary> - /// Extracts directory name from path string - /// Path should refer to a directory, for example \foo\ or /foo. - /// </summary> - /// <param name="path"></param> - /// <returns></returns> - 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]; - } - } - } - - - /// <summary> - /// Represents info about requested file system - /// </summary> - [DataContract] - public class FileSystemInfo - { - /// <summary> - /// file system type - /// </summary> - [DataMember(Name = "name", IsRequired = true)] - public string Name { get; set; } - - /// <summary> - /// Root directory entry - /// </summary> - [DataMember(Name = "root", EmitDefaultValue = false)] - public FileEntry Root { get; set; } - - /// <summary> - /// Creates class instance - /// </summary> - /// <param name="name"></param> - /// <param name="rootEntry"> Root directory</param> - public FileSystemInfo(string name, FileEntry rootEntry = null) - { - Name = name; - Root = rootEntry; - } - } - - [DataContract] - public class CreatingOptions - { - /// <summary> - /// Create file/directory if is doesn't exist - /// </summary> - [DataMember(Name = "create")] - public bool Create { get; set; } - - /// <summary> - /// Generate an exception if create=true and file/directory already exists - /// </summary> - [DataMember(Name = "exclusive")] - public bool Exclusive { get; set; } - - - } - - // returns null value if it fails. - private string[] getOptionStrings(string options) - { - string[] optStings = null; - try - { - optStings = JSON.JsonHelper.Deserialize<string[]>(options); - } - catch (Exception) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION), CurrentCommandCallbackId); - } - return optStings; - } - - /// <summary> - /// Gets amount of free space available for Isolated Storage - /// </summary> - /// <param name="options">No options is needed for this method</param> - public void getFreeDiskSpace(string options) - { - string callbackId = getOptionStrings(options)[0]; - - try - { - using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.OK, isoFile.AvailableFreeSpace), callbackId); - } - } - catch (IsolatedStorageException) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId); - } - catch (Exception ex) - { - if (!this.HandleException(ex)) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId); - } - } - } - - /// <summary> - /// Check if file exists - /// </summary> - /// <param name="options">File path</param> - public void testFileExists(string options) - { - IsDirectoryOrFileExist(options, false); - } - - /// <summary> - /// Check if directory exists - /// </summary> - /// <param name="options">directory name</param> - public void testDirectoryExists(string options) - { - IsDirectoryOrFileExist(options, true); - } - - /// <summary> - /// Check if file or directory exist - /// </summary> - /// <param name="options">File path/Directory name</param> - /// <param name="isDirectory">Flag to recognize what we should check</param> - public void IsDirectoryOrFileExist(string options, bool isDirectory) - { - string[] args = getOptionStrings(options); - string callbackId = args[1]; - FileOptions fileOptions = JSON.JsonHelper.Deserialize<FileOptions>(args[0]); - string filePath = args[0]; - - if (fileOptions == null) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION), callbackId); - } - - 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), callbackId); - } - } - catch (IsolatedStorageException) // default handler throws INVALID_MODIFICATION_ERR - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId); - } - catch (Exception ex) - { - if (!this.HandleException(ex)) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId); - } - } - - } - - public void readAsDataURL(string options) - { - string[] optStrings = getOptionStrings(options); - string filePath = optStrings[0]; - int startPos = int.Parse(optStrings[1]); - int endPos = int.Parse(optStrings[2]); - string callbackId = optStrings[3]; - - if (filePath != null) - { - try - { - string base64URL = null; - - using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) - { - if (!isoFile.FileExists(filePath)) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId); - return; - } - string mimeType = MimeTypeMapper.GetMimeType(filePath); - - using (IsolatedStorageFileStream stream = isoFile.OpenFile(filePath, FileMode.Open, FileAccess.Read)) - { - string base64String = GetFileContent(stream); - base64URL = "data:" + mimeType + ";base64," + base64String; - } - } - - DispatchCommandResult(new PluginResult(PluginResult.Status.OK, base64URL), callbackId); - } - catch (Exception ex) - { - if (!this.HandleException(ex)) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId); - } - } - } - } - - private byte[] readFileBytes(string filePath,int startPos,int endPos, IsolatedStorageFile isoFile) - { - byte[] buffer; - using (IsolatedStorageFileStream reader = isoFile.OpenFile(filePath, FileMode.Open, FileAccess.Read)) - { - if (startPos < 0) - { - startPos = Math.Max((int)reader.Length + startPos, 0); - } - else if (startPos > 0) - { - startPos = Math.Min((int)reader.Length, startPos); - } - if (endPos > 0) - { - endPos = Math.Min((int)reader.Length, endPos); - } - else if (endPos < 0) - { - endPos = Math.Max(endPos + (int)reader.Length, 0); - } - - buffer = new byte[endPos - startPos]; - reader.Seek(startPos, SeekOrigin.Begin); - reader.Read(buffer, 0, buffer.Length); - } - - return buffer; - } - - public void readAsArrayBuffer(string options) - { - string[] optStrings = getOptionStrings(options); - string filePath = optStrings[0]; - int startPos = int.Parse(optStrings[1]); - int endPos = int.Parse(optStrings[2]); - string callbackId = optStrings[3]; - - try - { - byte[] buffer; - - using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) - { - if (!isoFile.FileExists(filePath)) - { - readResourceAsText(options); - return; - } - buffer = readFileBytes(filePath, startPos, endPos, isoFile); - } - - DispatchCommandResult(new PluginResult(PluginResult.Status.OK, buffer), callbackId); - } - catch (Exception ex) - { - if (!this.HandleException(ex, callbackId)) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId); - } - } - } - - public void readAsBinaryString(string options) - { - string[] optStrings = getOptionStrings(options); - string filePath = optStrings[0]; - int startPos = int.Parse(optStrings[1]); - int endPos = int.Parse(optStrings[2]); - string callbackId = optStrings[3]; - - try - { - string result; - - using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) - { - if (!isoFile.FileExists(filePath)) - { - readResourceAsText(options); - return; - } - - byte[] buffer = readFileBytes(filePath, startPos, endPos, isoFile); - result = System.Text.Encoding.GetEncoding("iso-8859-1").GetString(buffer, 0, buffer.Length); - - } - - DispatchCommandResult(new PluginResult(PluginResult.Status.OK, result), callbackId); - } - catch (Exception ex) - { - if (!this.HandleException(ex, callbackId)) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId); - } - } - } - - public void readAsText(string options) - { - string[] optStrings = getOptionStrings(options); - string filePath = optStrings[0]; - string encStr = optStrings[1]; - int startPos = int.Parse(optStrings[2]); - int endPos = int.Parse(optStrings[3]); - string callbackId = optStrings[4]; - - try - { - string text = ""; - - using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) - { - if (!isoFile.FileExists(filePath)) - { - readResourceAsText(options); - return; - } - Encoding encoding = Encoding.GetEncoding(encStr); - - byte[] buffer = this.readFileBytes(filePath, startPos, endPos, isoFile); - text = encoding.GetString(buffer, 0, buffer.Length); - } - - // JIRA: https://issues.apache.org/jira/browse/CB-8792 - // Need to perform additional serialization here because NativeExecution is always trying - // to do JSON.parse() on command result. This leads to issue when trying to read JSON files - var resultText = JsonHelper.Serialize(text); - DispatchCommandResult(new PluginResult(PluginResult.Status.OK, resultText), callbackId); - } - catch (Exception ex) - { - if (!this.HandleException(ex, callbackId)) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId); - } - } - } - - /// <summary> - /// Reads application resource as a text - /// </summary> - /// <param name="options">Path to a resource</param> - public void readResourceAsText(string options) - { - string[] optStrings = getOptionStrings(options); - string pathToResource = optStrings[0]; - string encStr = optStrings[1]; - int start = int.Parse(optStrings[2]); - int endMarker = int.Parse(optStrings[3]); - string callbackId = optStrings[4]; - - try - { - if (pathToResource.StartsWith("/")) - { - pathToResource = pathToResource.Remove(0, 1); - } - - var resource = Application.GetResourceStream(new Uri(pathToResource, UriKind.Relative)); - - if (resource == null) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId); - return; - } - - string text; - StreamReader streamReader = new StreamReader(resource.Stream); - text = streamReader.ReadToEnd(); - - DispatchCommandResult(new PluginResult(PluginResult.Status.OK, text), callbackId); - } - catch (Exception ex) - { - if (!this.HandleException(ex, callbackId)) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId); - } - } - } - - public void truncate(string options) - { - string[] optStrings = getOptionStrings(options); - - string filePath = optStrings[0]; - int size = int.Parse(optStrings[1]); - string callbackId = optStrings[2]; - - try - { - long streamLength = 0; - - using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) - { - if (!isoFile.FileExists(filePath)) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId); - return; - } - - using (FileStream stream = new IsolatedStorageFileStream(filePath, FileMode.Open, FileAccess.ReadWrite, isoFile)) - { - if (0 <= size && size <= stream.Length) - { - stream.SetLength(size); - } - streamLength = stream.Length; - } - } - - DispatchCommandResult(new PluginResult(PluginResult.Status.OK, streamLength), callbackId); - } - catch (Exception ex) - { - if (!this.HandleException(ex, callbackId)) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId); - } - } - } - - //write:[filePath,data,position,isBinary,callbackId] - public void write(string options) - { - string[] optStrings = getOptionStrings(options); - - string filePath = optStrings[0]; - string data = optStrings[1]; - int position = int.Parse(optStrings[2]); - bool isBinary = bool.Parse(optStrings[3]); - string callbackId = optStrings[4]; - - try - { - if (string.IsNullOrEmpty(data)) - { - Debug.WriteLine("Expected some data to be send in the write command to {0}", filePath); - DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION), callbackId); - return; - } - - byte[] dataToWrite = isBinary ? JSON.JsonHelper.Deserialize<byte[]>(data) : - System.Text.Encoding.UTF8.GetBytes(data); - - using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) - { - // create the file if not exists - if (!isoFile.FileExists(filePath)) - { - var file = isoFile.CreateFile(filePath); - file.Close(); - } - - using (FileStream stream = new IsolatedStorageFileStream(filePath, FileMode.Open, FileAccess.ReadWrite, isoFile)) - { - if (0 <= position && position <= stream.Length) - { - stream.SetLength(position); - } - using (BinaryWriter writer = new BinaryWriter(stream)) - { - writer.Seek(0, SeekOrigin.End); - writer.Write(dataToWrite); - } - } - } - - DispatchCommandResult(new PluginResult(PluginResult.Status.OK, dataToWrite.Length), callbackId); - } - catch (Exception ex) - { - if (!this.HandleException(ex, callbackId)) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId); - } - } - } - - /// <summary> - /// Look up metadata about this entry. - /// </summary> - /// <param name="options">filePath to entry</param> - public void getMetadata(string options) - { - string[] optStings = getOptionStrings(options); - string filePath = optStings[0]; - string callbackId = optStings[1]; - - if (filePath != null) - { - try - { - using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) - { - if (isoFile.FileExists(filePath)) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.OK, - new ModificationMetadata() { modificationTime = isoFile.GetLastWriteTime(filePath).DateTime.ToString() }), callbackId); - } - else if (isoFile.DirectoryExists(filePath)) - { - string modTime = isoFile.GetLastWriteTime(filePath).DateTime.ToString(); - DispatchCommandResult(new PluginResult(PluginResult.Status.OK, new ModificationMetadata() { modificationTime = modTime }), callbackId); - } - else - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId); - } - - } - } - catch (IsolatedStorageException) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId); - } - catch (Exception ex) - { - if (!this.HandleException(ex)) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId); - } - } - } - - } - - - /// <summary> - /// Returns a File that represents the current state of the file that this FileEntry represents. - /// </summary> - /// <param name="filePath">filePath to entry</param> - /// <returns></returns> - public void getFileMetadata(string options) - { - string[] optStings = getOptionStrings(options); - string filePath = optStings[0]; - string callbackId = optStings[1]; - - if (!string.IsNullOrEmpty(filePath)) - { - try - { - FileMetadata metaData = new FileMetadata(filePath); - DispatchCommandResult(new PluginResult(PluginResult.Status.OK, metaData), callbackId); - } - catch (IsolatedStorageException) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId); - } - catch (Exception ex) - { - if (!this.HandleException(ex)) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_READABLE_ERR), callbackId); - } - } - } - else - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId); - } - } - - /// <summary> - /// Look up the parent DirectoryEntry containing this Entry. - /// If this Entry is the root of IsolatedStorage, its parent is itself. - /// </summary> - /// <param name="options"></param> - public void getParent(string options) - { - string[] optStings = getOptionStrings(options); - string filePath = optStings[0]; - string callbackId = optStings[1]; - - if (filePath != null) - { - try - { - if (string.IsNullOrEmpty(filePath)) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION),callbackId); - return; - } - - using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) - { - FileEntry entry; - - if (isoFile.FileExists(filePath) || isoFile.DirectoryExists(filePath)) - { - - - string path = this.GetParentDirectory(filePath); - entry = FileEntry.GetEntry(path); - DispatchCommandResult(new PluginResult(PluginResult.Status.OK, entry),callbackId); - } - else - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR),callbackId); - } - - } - } - catch (Exception ex) - { - if (!this.HandleException(ex)) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR),callbackId); - } - } - } - } - - public void remove(string options) - { - string[] args = getOptionStrings(options); - string filePath = args[0]; - string callbackId = args[1]; - - if (filePath != null) - { - try - { - if (filePath == "/" || filePath == "" || filePath == @"\") - { - throw new Exception("Cannot delete root file system") ; - } - using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) - { - if (isoFile.FileExists(filePath)) - { - isoFile.DeleteFile(filePath); - } - else - { - if (isoFile.DirectoryExists(filePath)) - { - isoFile.DeleteDirectory(filePath); - } - else - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR),callbackId); - return; - } - } - DispatchCommandResult(new PluginResult(PluginResult.Status.OK),callbackId); - } - } - catch (Exception ex) - { - if (!this.HandleException(ex)) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR),callbackId); - } - } - } - } - - public void removeRecursively(string options) - { - string[] args = getOptionStrings(options); - string filePath = args[0]; - string callbackId = args[1]; - - if (filePath != null) - { - if (string.IsNullOrEmpty(filePath)) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION),callbackId); - } - else - { - if (removeDirRecursively(filePath, callbackId)) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.OK), callbackId); - } - } - } - } - - public void readEntries(string options) - { - string[] args = getOptionStrings(options); - string filePath = args[0]; - string callbackId = args[1]; - - if (filePath != null) - { - try - { - if (string.IsNullOrEmpty(filePath)) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION),callbackId); - return; - } - - using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) - { - if (isoFile.DirectoryExists(filePath)) - { - string path = File.AddSlashToDirectory(filePath); - List<FileEntry> entries = new List<FileEntry>(); - 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),callbackId); - } - else - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR),callbackId); - } - } - } - catch (Exception ex) - { - if (!this.HandleException(ex)) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR),callbackId); - } - } - } - } - - public void requestFileSystem(string options) - { - // TODO: try/catch - string[] optVals = getOptionStrings(options); - //FileOptions fileOptions = new FileOptions(); - int fileSystemType = int.Parse(optVals[0]); - double size = double.Parse(optVals[1]); - string callbackId = optVals[2]; - - - IsolatedStorageFile.GetUserStoreForApplication(); - - if (size > (10 * 1024 * 1024)) // 10 MB, compier will clean this up! - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, QUOTA_EXCEEDED_ERR), callbackId); - return; - } - - try - { - if (size != 0) - { - using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) - { - long availableSize = isoFile.AvailableFreeSpace; - if (size > availableSize) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, QUOTA_EXCEEDED_ERR), callbackId); - return; - } - } - } - - if (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("/"))), callbackId); - } - else if (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))), callbackId); - } - else if (fileSystemType == RESOURCE) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.OK, new FileSystemInfo("resource")), callbackId); - } - else if (fileSystemType == APPLICATION) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.OK, new FileSystemInfo("application")), callbackId); - } - else - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR), callbackId); - } - - } - catch (Exception ex) - { - if (!this.HandleException(ex)) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR), callbackId); - } - } - } - - public void resolveLocalFileSystemURI(string options) - { - - string[] optVals = getOptionStrings(options); - string uri = optVals[0].Split('?')[0]; - string callbackId = optVals[1]; - - if (uri != null) - { - // a single '/' is valid, however, '/someDir' is not, but '/tmp//somedir' and '///someDir' are valid - if (uri.StartsWith("/") && uri.IndexOf("//") < 0 && uri != "/") - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ENCODING_ERR), callbackId); - return; - } - try - { - // fix encoded spaces - string path = Uri.UnescapeDataString(uri); - - FileEntry uriEntry = FileEntry.GetEntry(path); - if (uriEntry != null) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.OK, uriEntry), callbackId); - } - else - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId); - } - } - catch (Exception ex) - { - if (!this.HandleException(ex, callbackId)) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR), callbackId); - } - } - } - } - - 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 - - /// <summary> - /// Retrieves the parent directory name of the specified path, - /// </summary> - /// <param name="path">Path</param> - /// <returns>Parent directory name</returns> - private string GetParentDirectory(string path) - { - if (String.IsNullOrEmpty(path) || path == "/") - { - return "/"; - } - - if (path.EndsWith(@"/") || path.EndsWith(@"\")) - { - return this.GetParentDirectory(Path.GetDirectoryName(path)); - } - - string result = Path.GetDirectoryName(path); - if (result == null) - { - result = "/"; - } - - return result; - } - - private bool removeDirRecursively(string fullPath,string callbackId) - { - try - { - if (fullPath == "/") - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR),callbackId); - return false; - } - - using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) - { - if (isoFile.DirectoryExists(fullPath)) - { - string tempPath = File.AddSlashToDirectory(fullPath); - string[] files = isoFile.GetFileNames(tempPath + "*"); - if (files.Length > 0) - { - foreach (string file in files) - { - isoFile.DeleteFile(tempPath + file); - } - } - string[] dirs = isoFile.GetDirectoryNames(tempPath + "*"); - if (dirs.Length > 0) - { - foreach (string dir in dirs) - { - if (!removeDirRecursively(tempPath + dir, callbackId)) - { - return false; - } - } - } - isoFile.DeleteDirectory(fullPath); - } - else - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR),callbackId); - } - } - } - catch (Exception ex) - { - if (!this.HandleException(ex)) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR),callbackId); - return false; - } - } - return true; - } - - private bool CanonicalCompare(string pathA, string pathB) - { - string a = pathA.Replace("//", "/"); - string b = pathB.Replace("//", "/"); - - return a.Equals(b, StringComparison.OrdinalIgnoreCase); - } - - /* - * copyTo:["fullPath","parent", "newName"], - * moveTo:["fullPath","parent", "newName"], - */ - private void TransferTo(string options, bool move) - { - // TODO: try/catch - string[] optStrings = getOptionStrings(options); - string fullPath = optStrings[0]; - string parent = optStrings[1]; - string newFileName = optStrings[2]; - string callbackId = optStrings[3]; - - char[] invalids = Path.GetInvalidPathChars(); - - if (newFileName.IndexOfAny(invalids) > -1 || newFileName.IndexOf(":") > -1 ) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ENCODING_ERR), callbackId); - return; - } - - try - { - if ((parent == null) || (string.IsNullOrEmpty(parent)) || (string.IsNullOrEmpty(fullPath))) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId); - return; - } - - string parentPath = File.AddSlashToDirectory(parent); - string currentPath = 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), callbackId); - return; - } - string newName; - string newPath; - if (isFileExist) - { - newName = (string.IsNullOrEmpty(newFileName)) - ? Path.GetFileName(currentPath) - : newFileName; - - newPath = Path.Combine(parentPath, newName); - - // sanity check .. - // cannot copy file onto itself - if (CanonicalCompare(newPath,currentPath)) //(parent + newFileName)) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, INVALID_MODIFICATION_ERR), callbackId); - return; - } - else if (isoFile.DirectoryExists(newPath)) - { - // there is already a folder with the same name, operation is not allowed - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, INVALID_MODIFICATION_ERR), callbackId); - return; - } - else if (isoFile.FileExists(newPath)) - { // remove destination file if exists, in other case there will be exception - isoFile.DeleteFile(newPath); - } - - if (move) - { - isoFile.MoveFile(currentPath, newPath); - } - else - { - isoFile.CopyFile(currentPath, newPath, true); - } - } - else - { - newName = (string.IsNullOrEmpty(newFileName)) - ? currentPath - : newFileName; - - 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 - { - CopyDirectory(currentPath, newPath, isoFile); - } - } - FileEntry entry = FileEntry.GetEntry(newPath); - if (entry != null) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.OK, entry), callbackId); - } - else - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId); - } - } - - } - catch (Exception ex) - { - if (!this.HandleException(ex, callbackId)) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR), callbackId); - } - } - } - - private bool HandleException(Exception ex, string cbId="") - { - bool handled = false; - string callbackId = String.IsNullOrEmpty(cbId) ? this.CurrentCommandCallbackId : cbId; - if (ex is SecurityException) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, SECURITY_ERR), callbackId); - handled = true; - } - else if (ex is FileNotFoundException) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId); - handled = true; - } - else if (ex is ArgumentException) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ENCODING_ERR), callbackId); - handled = true; - } - else if (ex is IsolatedStorageException) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, INVALID_MODIFICATION_ERR), callbackId); - handled = true; - } - else if (ex is DirectoryNotFoundException) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId); - handled = true; - } - return handled; - } - - private void CopyDirectory(string sourceDir, string destDir, IsolatedStorageFile isoFile) - { - string path = File.AddSlashToDirectory(sourceDir); - - bool bExists = isoFile.DirectoryExists(destDir); - - if (!bExists) - { - 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,true); - } - } - string[] dirs = isoFile.GetDirectoryNames(path + "*"); - if (dirs.Length > 0) - { - foreach (string dir in dirs) - { - CopyDirectory(path + dir, destDir + dir, isoFile); - } - } - } - - private string RemoveExtraSlash(string path) { - if (path.StartsWith("//")) { - path = path.Remove(0, 1); - path = RemoveExtraSlash(path); - } - return path; - } - - private string ResolvePath(string parentPath, string path) - { - string absolutePath = null; - - if (path.Contains("..")) - { - if (parentPath.Length > 1 && parentPath.StartsWith("/") && parentPath !="/") - { - parentPath = RemoveExtraSlash(parentPath); - } - - string fullPath = Path.GetFullPath(Path.Combine(parentPath, path)); - absolutePath = fullPath.Replace(Path.GetPathRoot(fullPath), @"//"); - } - else - { - absolutePath = Path.Combine(parentPath + "/", path); - } - return absolutePath; - } - - private void GetFileOrDirectory(string options, bool getDirectory) - { - FileOptions fOptions = new FileOptions(); - string[] args = getOptionStrings(options); - - fOptions.FullPath = args[0]; - fOptions.Path = args[1]; - - string callbackId = args[3]; - - try - { - fOptions.CreatingOpt = JSON.JsonHelper.Deserialize<CreatingOptions>(args[2]); - } - catch (Exception) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION), callbackId); - return; - } - - try - { - if ((string.IsNullOrEmpty(fOptions.Path)) || (string.IsNullOrEmpty(fOptions.FullPath))) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId); - return; - } - - string path; - - if (fOptions.Path.Split(':').Length > 2) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ENCODING_ERR), callbackId); - return; - } - - try - { - path = ResolvePath(fOptions.FullPath, fOptions.Path); - } - catch (Exception) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ENCODING_ERR), callbackId); - return; - } - - using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication()) - { - bool isFile = isoFile.FileExists(path); - bool isDirectory = isoFile.DirectoryExists(path); - bool create = (fOptions.CreatingOpt == null) ? false : fOptions.CreatingOpt.Create; - bool exclusive = (fOptions.CreatingOpt == null) ? false : fOptions.CreatingOpt.Exclusive; - if (create) - { - if (exclusive && (isoFile.FileExists(path) || isoFile.DirectoryExists(path))) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, PATH_EXISTS_ERR), callbackId); - return; - } - - // need to make sure the parent exists - // it is an error to create a directory whose immediate parent does not yet exist - // see issue: https://issues.apache.org/jira/browse/CB-339 - string[] pathParts = path.Split('/'); - string builtPath = pathParts[0]; - for (int n = 1; n < pathParts.Length - 1; n++) - { - builtPath += "/" + pathParts[n]; - if (!isoFile.DirectoryExists(builtPath)) - { - Debug.WriteLine(String.Format("Error :: Parent folder \"{0}\" does not exist, when attempting to create \"{1}\"",builtPath,path)); - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId); - return; - } - } - - if ((getDirectory) && (!isDirectory)) - { - isoFile.CreateDirectory(path); - } - else - { - if ((!getDirectory) && (!isFile)) - { - - IsolatedStorageFileStream fileStream = isoFile.CreateFile(path); - fileStream.Close(); - } - } - } - else // (not create) - { - if ((!isFile) && (!isDirectory)) - { - if (path.IndexOf("//www") == 0) - { - Uri fileUri = new Uri(path.Remove(0,2), UriKind.Relative); - StreamResourceInfo streamInfo = Application.GetResourceStream(fileUri); - if (streamInfo != null) - { - FileEntry _entry = FileEntry.GetEntry(fileUri.OriginalString,true); - - DispatchCommandResult(new PluginResult(PluginResult.Status.OK, _entry), callbackId); - - //using (BinaryReader br = new BinaryReader(streamInfo.Stream)) - //{ - // byte[] data = br.ReadBytes((int)streamInfo.Stream.Length); - - //} - - } - else - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId); - } - - - } - else - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId); - } - return; - } - if (((getDirectory) && (!isDirectory)) || ((!getDirectory) && (!isFile))) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, TYPE_MISMATCH_ERR), callbackId); - return; - } - } - FileEntry entry = FileEntry.GetEntry(path); - if (entry != null) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.OK, entry), callbackId); - } - else - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NOT_FOUND_ERR), callbackId); - } - } - } - catch (Exception ex) - { - if (!this.HandleException(ex)) - { - DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, NO_MODIFICATION_ALLOWED_ERR), callbackId); - } - } - } - - private static string AddSlashToDirectory(string dirPath) - { - if (dirPath.EndsWith("/")) - { - return dirPath; - } - else - { - return dirPath + "/"; - } - } - - /// <summary> - /// Returns file content in a form of base64 string - /// </summary> - /// <param name="stream">File stream</param> - /// <returns>Base64 representation of the file</returns> - 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 - - } -} |
