├── .npmignore ├── tests ├── anyfile.txt ├── archives │ ├── www1.zip │ └── www2.zip ├── scripts │ ├── stop-server.sh │ └── start-server.sh ├── package.json ├── plugin.xml └── tests.js ├── .gitignore ├── sample ├── img │ └── logo.png ├── index.html ├── css │ └── index.css └── js │ └── index.js ├── .editorconfig ├── .travis.yml ├── NOTICE ├── src ├── browser │ └── Sync.js ├── ios │ ├── minizip │ │ ├── mztools.h │ │ ├── crypt.h │ │ ├── ioapi.h │ │ ├── ioapi.c │ │ ├── mztools.c │ │ ├── zip.h │ │ └── unzip.h │ ├── SSZipArchive.h │ └── ContentSync.h ├── windows │ ├── ZipWinProj │ │ ├── Properties │ │ │ └── AssemblyInfo.cs │ │ ├── ZipWinProj.csproj │ │ └── PGZipInflate.cs │ └── SyncProxy.js └── wp8 │ └── Unzip.cs ├── .jshintrc ├── .github ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── CONTRIBUTING.md ├── .bithoundrc ├── package.json ├── spec ├── helper │ └── cordova.js └── index.spec.js ├── plugin.xml ├── www └── index.js ├── LICENSE ├── CHANGELOG.md └── README.md /.npmignore: -------------------------------------------------------------------------------- 1 | sample/ 2 | spec/ 3 | tests/ -------------------------------------------------------------------------------- /tests/anyfile.txt: -------------------------------------------------------------------------------- 1 | Test file that is enumerated before the directories. 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac 2 | .DS_Store 3 | 4 | # Node 5 | /node_modules/ 6 | npm-debug.log 7 | -------------------------------------------------------------------------------- /sample/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phonegap/phonegap-plugin-contentsync/HEAD/sample/img/logo.png -------------------------------------------------------------------------------- /tests/archives/www1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phonegap/phonegap-plugin-contentsync/HEAD/tests/archives/www1.zip -------------------------------------------------------------------------------- /tests/archives/www2.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phonegap/phonegap-plugin-contentsync/HEAD/tests/archives/www2.zip -------------------------------------------------------------------------------- /tests/scripts/stop-server.sh: -------------------------------------------------------------------------------- 1 | ps aux | grep -e "python -m SimpleHTTPServer" | grep -v grep | awk '{print "kill -1 " $2}' | sh 2 | -------------------------------------------------------------------------------- /tests/scripts/start-server.sh: -------------------------------------------------------------------------------- 1 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 2 | cd $DIR/../archives && python -m "SimpleHTTPServer" 4321 & 3 | -------------------------------------------------------------------------------- /tests/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "phonegap-plugin-contentsync-tests", 3 | "version": "1.0.0", 4 | "description": "Tests for phonegap-plugin-contentsync", 5 | "main": "tests.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "Apache-2.0" 11 | } 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file is for unifying the coding style of different editors and IDEs. 2 | # editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | end_of_line = lf 9 | indent_size = 4 10 | indent_style = space 11 | insert_final_newline = true 12 | trim_trailing_whitespace = true 13 | 14 | [*.json] 15 | indent_size = 2 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.10' 4 | branches: 5 | only: 6 | - master 7 | notifications: 8 | slack: 9 | secure: VcJej2eTOwkU9oh/MHXKw/efUMw+o15Fd8yzc8/vR0QVG6ubYaIPlMK9iPTI9+MgkP+DIHxvsVr84XQvM40RSr9M35N21zEJjG/P/He8slmca6MAM9v7V8XEZX9xlptshb276l8DfeJs3sF8YZNnulkqoTBeuXNii5pGUtwG7m0= 10 | email: 11 | - PhoneGapCI@adobe.com 12 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | PhoneGap GUI 2 | Copyright 2013 Adobe Systems Incorporated 3 | 4 | This software is licensed under the Apache License, Version 2.0 (see 5 | LICENSE file). 6 | 7 | This software uses the following third party libraries that may have 8 | licenses differing from that of the software itself. You can find the 9 | libraries and their respective licenses below. 10 | 11 | PhoneGap is a registered trademark or trademark of Adobe Systems Incorporated. 12 | -------------------------------------------------------------------------------- /src/browser/Sync.js: -------------------------------------------------------------------------------- 1 | function notSupported() { 2 | console.log('ContentSync is not supported on browser platform'); 3 | } 4 | 5 | var ContentSync = function() {}; 6 | ContentSync.prototype.on = function() { notSupported(); }; 7 | ContentSync.prototype.emit = function() { notSupported(); }; 8 | ContentSync.prototype.cancel = function() { notSupported(); }; 9 | 10 | function sync() { 11 | notSupported(); 12 | return new ContentSync(); 13 | } 14 | 15 | module.exports = { 16 | sync: sync, 17 | unzip: notSupported, 18 | download: notSupported, 19 | ContentSync: ContentSync 20 | }; 21 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "asi": false, 3 | "boss": false, 4 | "camelcase": true, 5 | "curly": true, 6 | "eqeqeq": true, 7 | "eqnull": false, 8 | "es5": false, 9 | "evil": false, 10 | "expr": false, 11 | "forin": true, 12 | "funcscope": false, 13 | "jasmine": true, 14 | "immed": true, 15 | "indent": 4, 16 | "latedef": true, 17 | "loopfunc": false, 18 | "maxerr": 7, 19 | "newcap": true, 20 | "node": true, 21 | "nonew": true, 22 | "plusplus": false, 23 | "quotmark": "single", 24 | "shadow": false, 25 | "strict": false, 26 | "supernew": false, 27 | "trailing": true, 28 | "undef": true, 29 | "white": true 30 | } 31 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Expected Behaviour 2 | 3 | ### Actual Behaviour 4 | 5 | ### Reproduce Scenario (including but not limited to) 6 | 7 | #### Steps to Reproduce 8 | 9 | #### Platform and Version (eg. Android 5.0 or iOS 9.2.1) 10 | 11 | #### (Android) What device vendor (e.g. Samsung, HTC, Sony...) 12 | 13 | #### Cordova CLI version and cordova platform version 14 | 15 | cordova --version # e.g. 6.0.0 16 | cordova platform version android # e.g. 4.1.1 17 | 18 | #### Plugin version 19 | 20 | cordova plugin version | grep phonegap-plugin-contentsync # e.g. 1.5.3 21 | 22 | #### Sample Code that illustrates the problem 23 | 24 | #### Logs taken while reproducing problem 25 | -------------------------------------------------------------------------------- /src/ios/minizip/mztools.h: -------------------------------------------------------------------------------- 1 | /* 2 | Additional tools for Minizip 3 | Code: Xavier Roche '2004 4 | License: Same as ZLIB (www.gzip.org) 5 | */ 6 | 7 | #ifndef _zip_tools_H 8 | #define _zip_tools_H 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | #ifndef _ZLIB_H 15 | #include "zlib.h" 16 | #endif 17 | 18 | #include "unzip.h" 19 | 20 | /* Repair a ZIP file (missing central directory) 21 | file: file to recover 22 | fileOut: output file after recovery 23 | fileOutTmp: temporary file name used for recovery 24 | */ 25 | extern int ZEXPORT unzRepair(const char* file, 26 | const char* fileOut, 27 | const char* fileOutTmp, 28 | uLong* nRecovered, 29 | uLong* bytesRecovered); 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /tests/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | PhoneGap Content Sync Plugin Tests 8 | Apache 2.0 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/windows/ZipWinProj/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Resources; 2 | using System.Reflection; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | 6 | // General Information about an assembly is controlled through the following 7 | // set of attributes. Change these attribute values to modify the information 8 | // associated with an assembly. 9 | [assembly: AssemblyTitle("ZipWinProj")] 10 | [assembly: AssemblyDescription("")] 11 | [assembly: AssemblyConfiguration("")] 12 | [assembly: AssemblyCompany("")] 13 | [assembly: AssemblyProduct("ZipWinProj")] 14 | [assembly: AssemblyCopyright("Copyright © 2015")] 15 | [assembly: AssemblyTrademark("")] 16 | [assembly: AssemblyCulture("")] 17 | [assembly: NeutralResourcesLanguage("en")] 18 | 19 | // Version information for an assembly consists of the following four values: 20 | // 21 | // Major Version 22 | // Minor Version 23 | // Build Number 24 | // Revision 25 | // 26 | // You can specify all the values or you can default the Build and Revision Numbers 27 | // by using the '*' as shown below: 28 | // [assembly: AssemblyVersion("1.0.*")] 29 | [assembly: AssemblyVersion("1.0.0.0")] 30 | [assembly: AssemblyFileVersion("1.0.0.0")] 31 | -------------------------------------------------------------------------------- /.bithoundrc: -------------------------------------------------------------------------------- 1 | { 2 | "ignore": [ 3 | "**/deps/**", 4 | "**/node_modules/**", 5 | "**/thirdparty/**", 6 | "**/third_party/**", 7 | "**/vendor/**", 8 | "**/**-min-**", 9 | "**/**-min.**", 10 | "**/**.min.**", 11 | "**/**jquery.?(ui|effects)-*.*.?(*).?(cs|j)s", 12 | "**/**jquery-*.*.?(*).?(cs|j)s", 13 | "**/prototype?(*).js", 14 | "**/**?(*).ts", 15 | "**/mootools*.*.*.js", 16 | "**/dojo.js", 17 | "**/MochiKit.js", 18 | "**/yahoo-*.js", 19 | "**/yui*.js", 20 | "**/ckeditor*.js", 21 | "**/tiny_mce*.js", 22 | "**/tiny_mce/?(langs|plugins|themes|utils)/**", 23 | "**/MathJax/**", 24 | "**/shBrush*.js", 25 | "**/shCore.js", 26 | "**/shLegacy.js", 27 | "**/modernizr.custom.?(*).js", 28 | "**/knockout-*.*.*.debug.js", 29 | "**/extjs/*.js", 30 | "**/extjs/*.xml", 31 | "**/extjs/*.txt", 32 | "**/extjs/*.html", 33 | "**/extjs/*.properties", 34 | "**/extjs/.sencha", 35 | "**/extjs/docs/**", 36 | "**/extjs/builds/**", 37 | "**/extjs/cmd/**", 38 | "**/extjs/examples/**", 39 | "**/extjs/locale/**", 40 | "**/extjs/packages/**", 41 | "**/extjs/plugins/**", 42 | "**/extjs/resources/**", 43 | "**/extjs/src/**", 44 | "**/extjs/welcome/**", 45 | "bower_components/**" 46 | ], 47 | "test": [ 48 | "**/test/**", 49 | "**/tests/**", 50 | "**/spec/**", 51 | "**/specs/**" 52 | ] 53 | } 54 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "phonegap-plugin-contentsync", 3 | "description": "Fetch and cache content for your PhoneGap app.", 4 | "version": "1.4.2", 5 | "homepage": "http://github.com/phonegap/phonegap-plugin-contentsync#readme", 6 | "repository": { 7 | "type": "git", 8 | "url": "git://github.com/phonegap/phonegap-plugin-contentsync.git" 9 | }, 10 | "bugs": { 11 | "url": "https://github.com/phonegap/phonegap-plugin-contentsync/issues" 12 | }, 13 | "cordova": { 14 | "id": "phonegap-plugin-contentsync", 15 | "platforms": [ 16 | "ios", 17 | "android", 18 | "windows8", 19 | "windows", 20 | "wp8", 21 | "browser", 22 | "osx" 23 | ] 24 | }, 25 | "keywords": [ 26 | "ecosystem:cordova", 27 | "ecosystem:phonegap", 28 | "cordova-ios", 29 | "cordova-android", 30 | "cordova-windows8", 31 | "cordova-windows", 32 | "cordova-wp8", 33 | "cordova-browser", 34 | "cordova-osx" 35 | ], 36 | "engines": [ 37 | { 38 | "name": "cordova", 39 | "version": ">=3.0.0" 40 | } 41 | ], 42 | "author": "Adobe PhoneGap Team", 43 | "license": "Apache-2.0", 44 | "scripts": { 45 | "test": "jasmine-node --color spec", 46 | "paramedic": "./tests/scripts/start-server.sh && cordova-paramedic --platform ios && ./tests/scripts/stop-server.sh" 47 | }, 48 | "devDependencies": { 49 | "jasmine-node": "1.14.5", 50 | "pluginpub": "^0.0.9" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Description 4 | 5 | 6 | ## Related Issue 7 | 8 | 9 | 10 | 11 | 12 | ## Motivation and Context 13 | 14 | 15 | ## How Has This Been Tested? 16 | 17 | 18 | 19 | 20 | ## Screenshots (if appropriate): 21 | 22 | ## Types of changes 23 | 24 | - [ ] Bug fix (non-breaking change which fixes an issue) 25 | - [ ] New feature (non-breaking change which adds functionality) 26 | - [ ] Breaking change (fix or feature that would cause existing functionality to change) 27 | 28 | ## Checklist: 29 | 30 | 31 | - [ ] My code follows the code style of this project. 32 | - [ ] My change requires a change to the documentation. 33 | - [ ] I have updated the documentation accordingly. 34 | - [ ] I have read the **CONTRIBUTING** document. 35 | - [ ] I have added tests to cover my changes. 36 | - [ ] All new and existing tests passed. 37 | -------------------------------------------------------------------------------- /src/ios/SSZipArchive.h: -------------------------------------------------------------------------------- 1 | // 2 | // SSZipArchive.h 3 | // SSZipArchive 4 | // 5 | // Created by Sam Soffes on 7/21/10. 6 | // Copyright (c) Sam Soffes 2010-2014. All rights reserved. 7 | // 8 | 9 | #ifndef _SSZIPARCHIVE_H 10 | #define _SSZIPARCHIVE_H 11 | 12 | #import 13 | #include "unzip.h" 14 | 15 | @protocol SSZipArchiveDelegate; 16 | 17 | @interface SSZipArchive : NSObject 18 | 19 | // Unzip 20 | + (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination; 21 | + (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination overwrite:(BOOL)overwrite password:(NSString *)password error:(NSError **)error; 22 | 23 | + (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination delegate:(id)delegate; 24 | + (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination overwrite:(BOOL)overwrite password:(NSString *)password error:(NSError **)error delegate:(id)delegate; 25 | 26 | // Zip 27 | + (BOOL)createZipFileAtPath:(NSString *)path withFilesAtPaths:(NSArray *)filenames; 28 | + (BOOL)createZipFileAtPath:(NSString *)path withContentsOfDirectory:(NSString *)directoryPath; 29 | 30 | - (id)initWithPath:(NSString *)path; 31 | - (BOOL)open; 32 | - (BOOL)writeFile:(NSString *)path; 33 | - (BOOL)writeData:(NSData *)data filename:(NSString *)filename; 34 | - (BOOL)close; 35 | 36 | @end 37 | 38 | 39 | @protocol SSZipArchiveDelegate 40 | 41 | @optional 42 | 43 | - (void)zipArchiveWillUnzipArchiveAtPath:(NSString *)path zipInfo:(unz_global_info)zipInfo; 44 | - (void)zipArchiveDidUnzipArchiveAtPath:(NSString *)path zipInfo:(unz_global_info)zipInfo unzippedPath:(NSString *)unzippedPath; 45 | 46 | - (void)zipArchiveWillUnzipFileAtIndex:(NSInteger)fileIndex totalFiles:(NSInteger)totalFiles archivePath:(NSString *)archivePath fileInfo:(unz_file_info)fileInfo; 47 | - (void)zipArchiveDidUnzipFileAtIndex:(NSInteger)fileIndex totalFiles:(NSInteger)totalFiles archivePath:(NSString *)archivePath fileInfo:(unz_file_info)fileInfo; 48 | 49 | - (void)zipArchiveProgressEvent:(NSInteger)loaded total:(NSInteger)total; 50 | @end 51 | 52 | #endif /* _SSZIPARCHIVE_H */ 53 | -------------------------------------------------------------------------------- /sample/index.html: -------------------------------------------------------------------------------- 1 | 2 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | Hello World 29 | 30 | 31 |
32 |

Apache Cordova

33 | 39 |
40 |
41 |
42 |
43 |
44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/ios/ContentSync.h: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | */ 19 | #import 20 | #import 21 | #import 22 | 23 | enum ProgressState { 24 | Stopped = 0, 25 | Downloading, 26 | Extracting, 27 | Complete 28 | }; 29 | typedef NSUInteger ProgressState; 30 | 31 | enum ErrorCodes { 32 | INVALID_URL_ERR = 1, 33 | CONNECTION_ERR, 34 | UNZIP_ERR, 35 | LOCAL_ERR, 36 | #if !TARGET_OS_IOS // this is currently not added to ios. see issue-96 37 | IN_PROGRESS_ERR, 38 | #endif 39 | }; 40 | typedef NSUInteger ErrorCodes; 41 | 42 | @interface ContentSyncTask: NSObject 43 | 44 | @property (nonatomic) CDVInvokedUrlCommand* command; 45 | @property (nonatomic) NSURLSessionDownloadTask* downloadTask; 46 | @property (nonatomic) NSString* appId; 47 | @property (nonatomic) NSString* archivePath; 48 | @property (nonatomic) NSInteger progress; 49 | @property (nonatomic) BOOL extractArchive; 50 | 51 | @end 52 | 53 | @interface ContentSync : CDVPlugin 54 | 55 | @property (nonatomic) NSString* currentPath; 56 | @property (nonatomic) NSMutableArray *syncTasks; 57 | @property (nonatomic) NSURLSession* session; 58 | @property (nonatomic) NSMutableArray* trustedHosts; 59 | 60 | - (void) sync:(CDVInvokedUrlCommand*)command; 61 | - (void) cancel:(CDVInvokedUrlCommand*)command; 62 | - (void) download:(CDVInvokedUrlCommand*)command; 63 | - (void) unzip:(CDVInvokedUrlCommand*)command; 64 | 65 | @end 66 | 67 | /** 68 | * NSURLProtocolNoCache 69 | * 70 | * Custom URL Protocol handler to prevent caching of local assets. 71 | */ 72 | 73 | @interface NSURLProtocolNoCache : NSURLProtocol 74 | @end 75 | -------------------------------------------------------------------------------- /spec/helper/cordova.js: -------------------------------------------------------------------------------- 1 | /* global cordova:true */ 2 | 3 | /*! 4 | * Module dependencies. 5 | */ 6 | 7 | /** 8 | * cordova.js for node. 9 | * 10 | * Think of this as cordova-node, which would be simliar to cordova-android 11 | * or cordova-browser. The purpose of this module is to enable testing 12 | * of a plugin's JavaScript interface. 13 | * 14 | * When this module is first required, it will insert a global cordova 15 | * instance, which can hijack cordova-specific commands within the pluin's 16 | * implementation. 17 | * 18 | * Remember to require this module before the plugin that you want to test. 19 | * 20 | * Example: 21 | * 22 | * var cordova = require('./helper/cordova'), 23 | * myPlugin = require('../www/myPlugin'); 24 | */ 25 | 26 | module.exports = global.cordova = cordova = { 27 | 28 | /** 29 | * cordova.require Mock. 30 | * 31 | * Hijacks all cordova.requires. By default, it returns an empty function. 32 | * You can define your own implementation of each required module before 33 | * or after it has been required. 34 | * 35 | * See `cordova.required` to learn how to add your own module implemtnation. 36 | */ 37 | 38 | require: function(moduleId) { 39 | // define a default function if it doesn't exist 40 | if (!cordova.required[moduleId]) { 41 | cordova.required[moduleId] = function() {}; 42 | } 43 | // create a new module mapping between the module Id and cordova.required. 44 | return new ModuleMap(moduleId); 45 | }, 46 | 47 | /** 48 | * Cordova module implementations. 49 | * 50 | * A key-value hash, where the key is the module such as 'cordova/exec' 51 | * and the value is the function or object returned. 52 | * 53 | * For example: 54 | * 55 | * var exec = require('cordova/exec'); 56 | * 57 | * Will map to: 58 | * 59 | * cordova.required['cordova/exec']; 60 | */ 61 | 62 | required: { 63 | // populated at runtime 64 | } 65 | }; 66 | 67 | /** 68 | * Module Mapper. 69 | * 70 | * Returns a function that when executed will lookup the implementation 71 | * in cordova.required[id]. 72 | * 73 | * @param {String} moduleId is the module name/path, such as 'cordova/exec' 74 | * @return {Function}. 75 | */ 76 | 77 | function ModuleMap(moduleId) { 78 | return function() { 79 | // lookup and execute the module's mock implementation, passing 80 | // in any parameters that were provided. 81 | return cordova.required[moduleId].apply(this, arguments); 82 | }; 83 | } 84 | -------------------------------------------------------------------------------- /src/windows/ZipWinProj/ZipWinProj.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | en-US 6 | 12.0 7 | Debug 8 | bin\Debug\ 9 | AnyCPU 10 | {6865B02D-95D5-4A75-A130-E08BDBF45A12} 11 | winmdobj 12 | Properties 13 | ZipWinProj 14 | ZipWinProj 15 | en-US 16 | 512 17 | {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 18 | Profile32 19 | v4.6 20 | 21 | 22 | true 23 | full 24 | false 25 | bin\Debug\ 26 | DEBUG;TRACE 27 | prompt 28 | 4 29 | 30 | 31 | pdbonly 32 | true 33 | bin\Release\ 34 | TRACE 35 | prompt 36 | 4 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 57 | -------------------------------------------------------------------------------- /src/windows/ZipWinProj/PGZipInflate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using System.IO.Compression; 5 | using System.Threading.Tasks; 6 | using Windows.Foundation; 7 | using Windows.Storage; 8 | using Windows.Storage.Streams; 9 | 10 | namespace ZipWinProj 11 | { 12 | public sealed class PGZipInflate 13 | { 14 | public static IAsyncAction InflateAsync(StorageFile zipSourceFile, StorageFolder destFolder) 15 | { 16 | return Inflate(zipSourceFile, destFolder).AsAsyncAction(); 17 | } 18 | 19 | private static async Task InflateEntryAsync(ZipArchiveEntry entry, StorageFolder destFolder, bool createSub = false) 20 | { 21 | 22 | string filePath = entry.FullName; 23 | 24 | if (!string.IsNullOrEmpty(filePath) && filePath.Contains("/") && !createSub) 25 | { 26 | // Create sub folder 27 | string subFolderName = Path.GetDirectoryName(filePath); 28 | 29 | StorageFolder subFolder; 30 | 31 | // Create or return the sub folder. 32 | subFolder = await destFolder.CreateFolderAsync(subFolderName, CreationCollisionOption.OpenIfExists); 33 | 34 | string newFilePath = Path.GetFileName(filePath); 35 | 36 | if (!string.IsNullOrEmpty(newFilePath)) 37 | { 38 | // Unzip file iteratively. 39 | await InflateEntryAsync(entry, subFolder, true); 40 | } 41 | } 42 | else 43 | { 44 | // Read uncompressed contents 45 | using (Stream entryStream = entry.Open()) 46 | { 47 | // Create a file to store the contents 48 | StorageFile uncompressedFile = await destFolder.CreateFileAsync(entry.Name, CreationCollisionOption.ReplaceExisting); 49 | 50 | using (IRandomAccessStream uncompressedFileStream = 51 | await uncompressedFile.OpenAsync(FileAccessMode.ReadWrite)) 52 | { 53 | using (Stream outstream = uncompressedFileStream.AsStreamForWrite()) 54 | { 55 | await entryStream.CopyToAsync(outstream); 56 | outstream.Flush(); 57 | } 58 | } 59 | } 60 | } 61 | } 62 | 63 | private static async Task Inflate(StorageFile zipFile, StorageFolder destFolder) 64 | { 65 | if (zipFile == null) 66 | { 67 | throw new Exception("StorageFile (zipFile) passed to Inflate is null"); 68 | } 69 | else if (destFolder == null) 70 | { 71 | throw new Exception("StorageFolder (destFolder) passed to Inflate is null"); 72 | } 73 | 74 | Stream zipStream = await zipFile.OpenStreamForReadAsync(); 75 | 76 | using (ZipArchive zipArchive = new ZipArchive(zipStream, ZipArchiveMode.Read)) 77 | { 78 | Debug.WriteLine("Count = " + zipArchive.Entries.Count); 79 | foreach (ZipArchiveEntry entry in zipArchive.Entries) 80 | { 81 | Debug.WriteLine("Extracting {0} to {1}", entry.FullName, destFolder.Path); 82 | try 83 | { 84 | await InflateEntryAsync(entry, destFolder); 85 | } 86 | catch (Exception ex) 87 | { 88 | Debug.WriteLine("Exception: " + ex.Message); 89 | } 90 | } 91 | } 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | We love pull requests from everyone. 4 | 5 | [Fork](https://help.github.com/articles/fork-a-repo/), then [clone](https://help.github.com/articles/cloning-a-repository/) the repo: 6 | 7 | ``` 8 | git clone git@github.com:your-username/phonegap-plugin-contentsync.git 9 | ``` 10 | 11 | Set up a branch for your feature or bugfix with a link to the original repo: 12 | 13 | ``` 14 | git checkout -b my-awesome-new-feature 15 | git push --set-upstream origin my-awesome-new-feature 16 | git remote add upstream https://github.com/phonegap/phonegap-plugin-contentsync.git 17 | ``` 18 | 19 | Set up the project: 20 | 21 | ``` 22 | npm install 23 | ``` 24 | 25 | Make sure the tests pass before changing anything: 26 | 27 | ``` 28 | npm test 29 | ``` 30 | 31 | Make your change. Add tests for your change. Make the tests pass: 32 | 33 | ``` 34 | npm test 35 | ``` 36 | 37 | Commit changes: 38 | 39 | ``` 40 | git commit -m "Cool stuff" 41 | ``` 42 | 43 | Consider starting the commit message with an applicable emoji: 44 | * :art: `:art:` when improving the format/structure of the code 45 | * :zap: `:zap:` when improving performance 46 | * :non-potable_water: `:non-potable_water:` when plugging memory leaks 47 | * :memo: `:memo:` when writing docs 48 | * :ambulance: `:ambulance:` a critical hotfix. 49 | * :sparkles: `:sparkles:` when introducing new features 50 | * :bookmark: `:bookmark:` when releasing / version tags 51 | * :rocket: `:rocket:` when deploying stuff 52 | * :penguin: `:penguin:` when fixing something on Android 53 | * :apple: `:apple:` when fixing something on iOS 54 | * :checkered_flag: `:checkered_flag:` when fixing something on Windows 55 | * :bug: `:bug:` when fixing a bug 56 | * :fire: `:fire:` when removing code or files 57 | * :green_heart: `:green_heart:` when fixing the CI build 58 | * :white_check_mark: `:white_check_mark:` when adding tests 59 | * :lock: `:lock:` when dealing with security 60 | * :arrow_up: `:arrow_up:` when upgrading dependencies 61 | * :arrow_down: `:arrow_down:` when downgrading dependencies 62 | * :shirt: `:shirt:` when removing linter warnings 63 | * :hammer: `:hammer:` when doing heavy refactoring 64 | * :heavy_minus_sign: `:heavy_minus_sign:` when removing a dependency. 65 | * :heavy_plus_sign: `:heavy_plus_sign:` when adding a dependency. 66 | * :wrench: `:wrench:` when changing configuration files. 67 | * :globe_with_meridians: `:globe_with_meridians:` when dealing with internationalization and localization. 68 | * :pencil2: `:pencil2:` when fixing typos. 69 | * :hankey: `:hankey:` when writing bad code that needs to be improved. 70 | * :package: `:package:` when updating compiled files or packages. 71 | 72 | Make sure your branch is up to date with the original repo: 73 | 74 | ``` 75 | git fetch upstream 76 | git merge upstream/master 77 | ``` 78 | 79 | Review your changes and any possible conflicts and push to your fork: 80 | 81 | ``` 82 | git push origin 83 | ``` 84 | 85 | [Submit a pull request](https://help.github.com/articles/creating-a-pull-request/). 86 | 87 | At this point you're waiting on us. We do our best to keep on top of all the pull requests. We may suggest some changes, improvements or alternatives. 88 | 89 | Some things that will increase the chance that your pull request is accepted: 90 | 91 | - Write tests. 92 | - Write a [good commit message](http://chris.beams.io/posts/git-commit/). 93 | - Make sure the PR merges cleanly with the latest master. 94 | - Describe your feature/bugfix and why it's needed/important in the pull request description. 95 | 96 | 97 | ## Editor Config 98 | 99 | The project uses [.editorconfig](http://editorconfig.org/) to define the coding 100 | style of each file. We recommend that you install the Editor Config extension 101 | for your preferred IDE. Consistency is key. 102 | 103 | ## JSHint 104 | 105 | The project uses [.jshintrc](http://jshint.com/) to define the JavaScript 106 | coding conventions. Most editors now have a JSHint add-on to provide on-save 107 | or on-edit linting. 108 | -------------------------------------------------------------------------------- /plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | content-sync 4 | PhoneGap Content Sync Plugin 5 | 6 | phonegap,content sync 7 | https://github.com/phonegap/phonegap-plugin-contentsync 8 | https://github.com/phonegap/phonegap-plugin-contentsync/issues 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /sample/css/index.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | * { 20 | -webkit-tap-highlight-color: rgba(0,0,0,0); /* make transparent link selection, adjust last value opacity 0 to 1.0 */ 21 | } 22 | 23 | body { 24 | -webkit-touch-callout: none; /* prevent callout to copy image, etc when tap to hold */ 25 | -webkit-text-size-adjust: none; /* prevent webkit from resizing text to fit */ 26 | -webkit-user-select: none; /* prevent copy paste, to allow, change 'none' to 'text' */ 27 | background-color:#E4E4E4; 28 | background-image:linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%); 29 | background-image:-webkit-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%); 30 | background-image:-ms-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%); 31 | background-image:-webkit-gradient( 32 | linear, 33 | left top, 34 | left bottom, 35 | color-stop(0, #A7A7A7), 36 | color-stop(0.51, #E4E4E4) 37 | ); 38 | background-attachment:fixed; 39 | font-family:'HelveticaNeue-Light', 'HelveticaNeue', Helvetica, Arial, sans-serif; 40 | font-size:12px; 41 | height:100%; 42 | margin:0px; 43 | padding:0px; 44 | text-transform:uppercase; 45 | width:100%; 46 | } 47 | 48 | /* Portrait layout (default) */ 49 | .app { 50 | background:url(../img/logo.png) no-repeat center top; /* 170px x 200px */ 51 | position:absolute; /* position in the center of the screen */ 52 | left:50%; 53 | top:50%; 54 | height:50px; /* text area height */ 55 | width:225px; /* text area width */ 56 | text-align:center; 57 | padding:180px 0px 0px 0px; /* image height is 200px (bottom 20px are overlapped with text) */ 58 | margin:-115px 0px 0px -112px; /* offset vertical: half of image height and text area height */ 59 | /* offset horizontal: half of text area width */ 60 | } 61 | 62 | /* Landscape layout (with min-width) */ 63 | @media screen and (min-aspect-ratio: 1/1) and (min-width:400px) { 64 | .app { 65 | background-position:left center; 66 | padding:75px 0px 75px 170px; /* padding-top + padding-bottom + text area = image height */ 67 | margin:-90px 0px 0px -198px; /* offset vertical: half of image height */ 68 | /* offset horizontal: half of image width and text area width */ 69 | } 70 | } 71 | 72 | h1 { 73 | font-size:24px; 74 | font-weight:normal; 75 | margin:0px; 76 | overflow:visible; 77 | padding:0px; 78 | text-align:center; 79 | } 80 | 81 | .event { 82 | border-radius:4px; 83 | -webkit-border-radius:4px; 84 | color:#FFFFFF; 85 | font-size:12px; 86 | margin:0px 30px; 87 | padding:2px 0px; 88 | } 89 | 90 | .event.listening { 91 | background-color:#333333; 92 | display:block; 93 | } 94 | 95 | .event.received { 96 | background-color:#4B946A; 97 | display:none; 98 | } 99 | 100 | @keyframes fade { 101 | from { opacity: 1.0; } 102 | 50% { opacity: 0.4; } 103 | to { opacity: 1.0; } 104 | } 105 | 106 | @-webkit-keyframes fade { 107 | from { opacity: 1.0; } 108 | 50% { opacity: 0.4; } 109 | to { opacity: 1.0; } 110 | } 111 | 112 | .blink { 113 | animation:fade 3000ms infinite; 114 | -webkit-animation:fade 3000ms infinite; 115 | } 116 | 117 | #progressbar { 118 | background-color: black; 119 | border-radius: 13px; /* (height of inner div) / 2 + padding */ 120 | padding: 3px; 121 | } 122 | 123 | #progressbar > div { 124 | background-color:#4B946A; 125 | width: 0%; /* Adjust with JavaScript */ 126 | height: 20px; 127 | border-radius: 10px; 128 | } 129 | -------------------------------------------------------------------------------- /src/ios/minizip/crypt.h: -------------------------------------------------------------------------------- 1 | /* crypt.h -- base code for crypt/uncrypt ZIPfile 2 | 3 | 4 | Version 1.01e, February 12th, 2005 5 | 6 | Copyright (C) 1998-2005 Gilles Vollant 7 | 8 | This code is a modified version of crypting code in Infozip distribution 9 | 10 | The encryption/decryption parts of this source code (as opposed to the 11 | non-echoing password parts) were originally written in Europe. The 12 | whole source package can be freely distributed, including from the USA. 13 | (Prior to January 2000, re-export from the US was a violation of US law.) 14 | 15 | This encryption code is a direct transcription of the algorithm from 16 | Roger Schlafly, described by Phil Katz in the file appnote.txt. This 17 | file (appnote.txt) is distributed with the PKZIP program (even in the 18 | version without encryption capabilities). 19 | 20 | If you don't need crypting in your application, just define symbols 21 | NOCRYPT and NOUNCRYPT. 22 | 23 | This code support the "Traditional PKWARE Encryption". 24 | 25 | The new AES encryption added on Zip format by Winzip (see the page 26 | http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong 27 | Encryption is not supported. 28 | */ 29 | 30 | #define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8)) 31 | 32 | /*********************************************************************** 33 | * Return the next byte in the pseudo-random sequence 34 | */ 35 | static int decrypt_byte(unsigned long* pkeys, const unsigned long* pcrc_32_tab) 36 | { 37 | unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an 38 | * unpredictable manner on 16-bit systems; not a problem 39 | * with any known compiler so far, though */ 40 | 41 | temp = ((unsigned)(*(pkeys+2)) & 0xffff) | 2; 42 | return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); 43 | } 44 | 45 | /*********************************************************************** 46 | * Update the encryption keys with the next byte of plain text 47 | */ 48 | static int update_keys(unsigned long* pkeys,const unsigned long* pcrc_32_tab,int c) 49 | { 50 | (*(pkeys+0)) = CRC32((*(pkeys+0)), c); 51 | (*(pkeys+1)) += (*(pkeys+0)) & 0xff; 52 | (*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1; 53 | { 54 | register int keyshift = (int)((*(pkeys+1)) >> 24); 55 | (*(pkeys+2)) = CRC32((*(pkeys+2)), keyshift); 56 | } 57 | return c; 58 | } 59 | 60 | 61 | /*********************************************************************** 62 | * Initialize the encryption keys and the random header according to 63 | * the given password. 64 | */ 65 | static void init_keys(const char* passwd,unsigned long* pkeys,const unsigned long* pcrc_32_tab) 66 | { 67 | *(pkeys+0) = 305419896L; 68 | *(pkeys+1) = 591751049L; 69 | *(pkeys+2) = 878082192L; 70 | while (*passwd != '\0') { 71 | update_keys(pkeys,pcrc_32_tab,(int)*passwd); 72 | passwd++; 73 | } 74 | } 75 | 76 | #define zdecode(pkeys,pcrc_32_tab,c) \ 77 | (update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab))) 78 | 79 | #define zencode(pkeys,pcrc_32_tab,c,t) \ 80 | (t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), t^(c)) 81 | 82 | #ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED 83 | 84 | #define RAND_HEAD_LEN 12 85 | /* "last resort" source for second part of crypt seed pattern */ 86 | # ifndef ZCR_SEED2 87 | # define ZCR_SEED2 3141592654UL /* use PI as default pattern */ 88 | # endif 89 | 90 | static int crypthead(const char* passwd, /* password string */ 91 | unsigned char* buf, /* where to write header */ 92 | int bufSize, 93 | unsigned long* pkeys, 94 | const unsigned long* pcrc_32_tab, 95 | unsigned long crcForCrypting) 96 | { 97 | int n; /* index in random header */ 98 | int t; /* temporary */ 99 | int c; /* random byte */ 100 | unsigned char header[RAND_HEAD_LEN-2]; /* random header */ 101 | static unsigned calls = 0; /* ensure different random header each time */ 102 | 103 | if (bufSize> 7) & 0xff; 118 | header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t); 119 | } 120 | /* Encrypt random header (last two bytes is high word of crc) */ 121 | init_keys(passwd, pkeys, pcrc_32_tab); 122 | for (n = 0; n < RAND_HEAD_LEN-2; n++) 123 | { 124 | buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t); 125 | } 126 | buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t); 127 | buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t); 128 | return n; 129 | } 130 | 131 | #endif 132 | -------------------------------------------------------------------------------- /sample/js/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | var app = { 20 | // Application Constructor 21 | initialize: function() { 22 | this.bindEvents(); 23 | }, 24 | // Bind Event Listeners 25 | // 26 | // Bind any events that are required on startup. Common events are: 27 | // 'load', 'deviceready', 'offline', and 'online'. 28 | bindEvents: function() { 29 | document.addEventListener('deviceready', this.onDeviceReady, false); 30 | }, 31 | // deviceready Event Handler 32 | // 33 | // The scope of 'this' is the event. In order to call the 'receivedEvent' 34 | // function, we must explicitly call 'app.receivedEvent(...);' 35 | onDeviceReady: function() { 36 | app.receivedEvent('deviceready'); 37 | }, 38 | // Update DOM on a Received Event 39 | receivedEvent: function(id) { 40 | var parentElement = document.getElementById(id); 41 | var listeningElement = parentElement.querySelector('.listening'); 42 | var receivedElement = parentElement.querySelector('.received'); 43 | 44 | listeningElement.setAttribute('style', 'display:none;'); 45 | receivedElement.setAttribute('style', 'display:block;'); 46 | 47 | console.log('Received Event: ' + id); 48 | 49 | document.getElementById("syncBtn").onclick = this.sync; 50 | document.getElementById("downloadExtractBtn").onclick = this.download; 51 | 52 | var sync = ContentSync.sync({id: 'myapp', type: 'local'}); 53 | sync.on('complete', function(data) { 54 | if(data.localPath) { 55 | var url = "file://"+data.localPath + "/www/index.html"; 56 | alert('Sync complete ' + data + ' changing document.location ' + url ); 57 | //document.location = url; 58 | ContentSync.loadUrl(url); 59 | } 60 | }); 61 | sync.on('error', function(e) { 62 | alert('no synced app. Loading main app'); 63 | }); 64 | }, 65 | 66 | setProgress: function(progress) { 67 | if(progress.status) { 68 | switch(progress.status) { 69 | case 1: 70 | document.getElementById('status').innerHTML = "Downloading..."; 71 | break; 72 | case 2: 73 | document.getElementById('status').innerHTML = "Extracting..."; 74 | break; 75 | case 3: 76 | document.getElementById('status').innerHTML = "Complete!"; 77 | break; 78 | default: 79 | document.getElementById('status').innerHTML = ""; 80 | } 81 | } 82 | if(progress.progress) { 83 | var progressBar = document.getElementById('progressbar').children[0]; 84 | progressBar.style.width = progress.progress + '%'; 85 | } 86 | }, 87 | 88 | 89 | sync: function() { 90 | //var url = "https://github.com/timkim/zipTest/archive/master.zip"; 91 | var url = "http://localhost:8000/www.zip"; 92 | //var sync = ContentSync.sync({ src: url, id: 'myapp', type: 'merge', copyCordovaAssets: true, copyRootApp: false, headers: false, trustHost: true }); 93 | //var sync = ContentSync.sync({ src: null, id: 'myapp', type: 'local', copyRootApp: true }); 94 | var sync = ContentSync.sync({ src: url, id: 'myapp', type: 'merge', copyCordovaAssets: true }); 95 | 96 | var setProgress = this.setProgress; 97 | 98 | sync.on('progress', function(progress) { 99 | console.log("Progress event", progress); 100 | app.setProgress(progress); 101 | }); 102 | sync.on('complete', function(data) { 103 | console.log("Complete", data); 104 | //ContentSync.loadUrl("file://"+data.localPath + "/zipTest-master/www/index.html"); 105 | //document.location = data.localPath + "/zipTest-master/www/index.html"; 106 | }); 107 | 108 | sync.on('error', function(e) { 109 | console.log("Something went wrong: ", e); 110 | document.getElementById('status').innerHTML = e; 111 | }); 112 | }, 113 | download: function() { 114 | document.getElementById("downloadExtractBtn").disabled = true; 115 | var url = "https://github.com/timkim/zipTest/archive/master.zip"; 116 | var extract = this.extract; 117 | var setProgress = this.setProgress; 118 | var callback = function(response) { 119 | console.log(response); 120 | if(response.progress) { 121 | app.setProgress(response); 122 | 123 | } 124 | if(response.archiveURL) { 125 | var archiveURL = response.archiveURL; 126 | document.getElementById("downloadExtractBtn").disabled = false; 127 | document.getElementById("downloadExtractBtn").innerHTML = "Extract"; 128 | document.getElementById("downloadExtractBtn").onclick = function() { 129 | app.extract(archiveURL); 130 | }; 131 | document.getElementById("status").innerHTML = archiveURL; 132 | } 133 | }; 134 | ContentSync.download(url, callback); 135 | }, 136 | extract: function(archiveURL) { 137 | window.requestFileSystem(PERSISTENT, 1024 * 1024, function(fs) { 138 | fs.root.getDirectory('zipOutPut', {create: true}, function(fileEntry) { 139 | var dirUrl = fileEntry.toURL(); 140 | var callback = function(response) { 141 | console.log(response); 142 | document.getElementById("downloadExtractBtn").style.display = "none"; 143 | document.getElementById("status").innerHTML = "Extracted"; 144 | } 145 | console.log(dirUrl, archiveURL); 146 | Zip.unzip(archiveURL, dirUrl, callback); 147 | }); 148 | }); 149 | } 150 | }; 151 | 152 | app.initialize(); 153 | -------------------------------------------------------------------------------- /src/ios/minizip/ioapi.h: -------------------------------------------------------------------------------- 1 | /* ioapi.h -- IO base function header for compress/uncompress .zip 2 | part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) 3 | 4 | Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) 5 | 6 | Modifications for Zip64 support 7 | Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) 8 | 9 | For more info read MiniZip_info.txt 10 | 11 | Changes 12 | 13 | Oct-2009 - Defined ZPOS64_T to fpos_t on windows and u_int64_t on linux. (might need to find a better why for this) 14 | Oct-2009 - Change to fseeko64, ftello64 and fopen64 so large files would work on linux. 15 | More if/def section may be needed to support other platforms 16 | Oct-2009 - Defined fxxxx64 calls to normal fopen/ftell/fseek so they would compile on windows. 17 | (but you should use iowin32.c for windows instead) 18 | 19 | */ 20 | 21 | #ifndef _ZLIBIOAPI64_H 22 | #define _ZLIBIOAPI64_H 23 | 24 | #if (!defined(_WIN32)) && (!defined(WIN32)) 25 | 26 | // Linux needs this to support file operation on files larger then 4+GB 27 | // But might need better if/def to select just the platforms that needs them. 28 | 29 | #ifndef __USE_FILE_OFFSET64 30 | #define __USE_FILE_OFFSET64 31 | #endif 32 | #ifndef __USE_LARGEFILE64 33 | #define __USE_LARGEFILE64 34 | #endif 35 | #ifndef _LARGEFILE64_SOURCE 36 | #define _LARGEFILE64_SOURCE 37 | #endif 38 | #ifndef _FILE_OFFSET_BIT 39 | #define _FILE_OFFSET_BIT 64 40 | #endif 41 | #endif 42 | 43 | #include 44 | #include 45 | #include "zlib.h" 46 | 47 | #define USE_FILE32API 48 | #if defined(USE_FILE32API) 49 | #define fopen64 fopen 50 | #define ftello64 ftell 51 | #define fseeko64 fseek 52 | #else 53 | #ifdef _MSC_VER 54 | #define fopen64 fopen 55 | #if (_MSC_VER >= 1400) && (!(defined(NO_MSCVER_FILE64_FUNC))) 56 | #define ftello64 _ftelli64 57 | #define fseeko64 _fseeki64 58 | #else // old MSC 59 | #define ftello64 ftell 60 | #define fseeko64 fseek 61 | #endif 62 | #endif 63 | #endif 64 | 65 | /* 66 | #ifndef ZPOS64_T 67 | #ifdef _WIN32 68 | #define ZPOS64_T fpos_t 69 | #else 70 | #include 71 | #define ZPOS64_T uint64_t 72 | #endif 73 | #endif 74 | */ 75 | 76 | #ifdef HAVE_MINIZIP64_CONF_H 77 | #include "mz64conf.h" 78 | #endif 79 | 80 | /* a type choosen by DEFINE */ 81 | #ifdef HAVE_64BIT_INT_CUSTOM 82 | typedef 64BIT_INT_CUSTOM_TYPE ZPOS64_T; 83 | #else 84 | #ifdef HAS_STDINT_H 85 | #include "stdint.h" 86 | typedef uint64_t ZPOS64_T; 87 | #else 88 | 89 | 90 | #if defined(_MSC_VER) || defined(__BORLANDC__) 91 | typedef unsigned __int64 ZPOS64_T; 92 | #else 93 | typedef unsigned long long int ZPOS64_T; 94 | #endif 95 | #endif 96 | #endif 97 | 98 | 99 | 100 | #ifdef __cplusplus 101 | extern "C" { 102 | #endif 103 | 104 | 105 | #define ZLIB_FILEFUNC_SEEK_CUR (1) 106 | #define ZLIB_FILEFUNC_SEEK_END (2) 107 | #define ZLIB_FILEFUNC_SEEK_SET (0) 108 | 109 | #define ZLIB_FILEFUNC_MODE_READ (1) 110 | #define ZLIB_FILEFUNC_MODE_WRITE (2) 111 | #define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3) 112 | 113 | #define ZLIB_FILEFUNC_MODE_EXISTING (4) 114 | #define ZLIB_FILEFUNC_MODE_CREATE (8) 115 | 116 | 117 | #ifndef ZCALLBACK 118 | #if (defined(WIN32) || defined(_WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK) 119 | #define ZCALLBACK CALLBACK 120 | #else 121 | #define ZCALLBACK 122 | #endif 123 | #endif 124 | 125 | 126 | 127 | 128 | typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, const char* filename, int mode)); 129 | typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size)); 130 | typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); 131 | typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream)); 132 | typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream)); 133 | 134 | typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream)); 135 | typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin)); 136 | 137 | 138 | /* here is the "old" 32 bits structure structure */ 139 | typedef struct zlib_filefunc_def_s 140 | { 141 | open_file_func zopen_file; 142 | read_file_func zread_file; 143 | write_file_func zwrite_file; 144 | tell_file_func ztell_file; 145 | seek_file_func zseek_file; 146 | close_file_func zclose_file; 147 | testerror_file_func zerror_file; 148 | voidpf opaque; 149 | } zlib_filefunc_def; 150 | 151 | typedef ZPOS64_T (ZCALLBACK *tell64_file_func) OF((voidpf opaque, voidpf stream)); 152 | typedef long (ZCALLBACK *seek64_file_func) OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); 153 | typedef voidpf (ZCALLBACK *open64_file_func) OF((voidpf opaque, const void* filename, int mode)); 154 | 155 | typedef struct zlib_filefunc64_def_s 156 | { 157 | open64_file_func zopen64_file; 158 | read_file_func zread_file; 159 | write_file_func zwrite_file; 160 | tell64_file_func ztell64_file; 161 | seek64_file_func zseek64_file; 162 | close_file_func zclose_file; 163 | testerror_file_func zerror_file; 164 | voidpf opaque; 165 | } zlib_filefunc64_def; 166 | 167 | void fill_fopen64_filefunc OF((zlib_filefunc64_def* pzlib_filefunc_def)); 168 | void fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); 169 | 170 | /* now internal definition, only for zip.c and unzip.h */ 171 | typedef struct zlib_filefunc64_32_def_s 172 | { 173 | zlib_filefunc64_def zfile_func64; 174 | open_file_func zopen32_file; 175 | tell_file_func ztell32_file; 176 | seek_file_func zseek32_file; 177 | } zlib_filefunc64_32_def; 178 | 179 | 180 | #define ZREAD64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zread_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) 181 | #define ZWRITE64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zwrite_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) 182 | //#define ZTELL64(filefunc,filestream) ((*((filefunc).ztell64_file)) ((filefunc).opaque,filestream)) 183 | //#define ZSEEK64(filefunc,filestream,pos,mode) ((*((filefunc).zseek64_file)) ((filefunc).opaque,filestream,pos,mode)) 184 | #define ZCLOSE64(filefunc,filestream) ((*((filefunc).zfile_func64.zclose_file)) ((filefunc).zfile_func64.opaque,filestream)) 185 | #define ZERROR64(filefunc,filestream) ((*((filefunc).zfile_func64.zerror_file)) ((filefunc).zfile_func64.opaque,filestream)) 186 | 187 | voidpf call_zopen64 OF((const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode)); 188 | long call_zseek64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin)); 189 | ZPOS64_T call_ztell64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream)); 190 | 191 | void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32); 192 | 193 | #define ZOPEN64(filefunc,filename,mode) (call_zopen64((&(filefunc)),(filename),(mode))) 194 | #define ZTELL64(filefunc,filestream) (call_ztell64((&(filefunc)),(filestream))) 195 | #define ZSEEK64(filefunc,filestream,pos,mode) (call_zseek64((&(filefunc)),(filestream),(pos),(mode))) 196 | 197 | #ifdef __cplusplus 198 | } 199 | #endif 200 | 201 | #endif 202 | -------------------------------------------------------------------------------- /src/ios/minizip/ioapi.c: -------------------------------------------------------------------------------- 1 | /* ioapi.h -- IO base function header for compress/uncompress .zip 2 | part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) 3 | 4 | Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) 5 | 6 | Modifications for Zip64 support 7 | Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) 8 | 9 | For more info read MiniZip_info.txt 10 | 11 | */ 12 | 13 | #if (defined(_WIN32)) 14 | #define _CRT_SECURE_NO_WARNINGS 15 | #endif 16 | 17 | #include "ioapi.h" 18 | 19 | voidpf call_zopen64 (const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode) 20 | { 21 | if (pfilefunc->zfile_func64.zopen64_file != NULL) 22 | return (*(pfilefunc->zfile_func64.zopen64_file)) (pfilefunc->zfile_func64.opaque,filename,mode); 23 | else 24 | { 25 | return (*(pfilefunc->zopen32_file))(pfilefunc->zfile_func64.opaque,(const char*)filename,mode); 26 | } 27 | } 28 | 29 | long call_zseek64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin) 30 | { 31 | if (pfilefunc->zfile_func64.zseek64_file != NULL) 32 | return (*(pfilefunc->zfile_func64.zseek64_file)) (pfilefunc->zfile_func64.opaque,filestream,offset,origin); 33 | else 34 | { 35 | uLong offsetTruncated = (uLong)offset; 36 | if (offsetTruncated != offset) 37 | return -1; 38 | else 39 | return (*(pfilefunc->zseek32_file))(pfilefunc->zfile_func64.opaque,filestream,offsetTruncated,origin); 40 | } 41 | } 42 | 43 | ZPOS64_T call_ztell64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream) 44 | { 45 | if (pfilefunc->zfile_func64.zseek64_file != NULL) 46 | return (*(pfilefunc->zfile_func64.ztell64_file)) (pfilefunc->zfile_func64.opaque,filestream); 47 | else 48 | { 49 | uLong tell_uLong = (*(pfilefunc->ztell32_file))(pfilefunc->zfile_func64.opaque,filestream); 50 | if ((tell_uLong) == ((uLong)-1)) 51 | return (ZPOS64_T)-1; 52 | else 53 | return tell_uLong; 54 | } 55 | } 56 | 57 | void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32) 58 | { 59 | p_filefunc64_32->zfile_func64.zopen64_file = NULL; 60 | p_filefunc64_32->zopen32_file = p_filefunc32->zopen_file; 61 | p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file; 62 | p_filefunc64_32->zfile_func64.zread_file = p_filefunc32->zread_file; 63 | p_filefunc64_32->zfile_func64.zwrite_file = p_filefunc32->zwrite_file; 64 | p_filefunc64_32->zfile_func64.ztell64_file = NULL; 65 | p_filefunc64_32->zfile_func64.zseek64_file = NULL; 66 | p_filefunc64_32->zfile_func64.zclose_file = p_filefunc32->zclose_file; 67 | 68 | #ifndef __clang_analyzer__ 69 | p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file; 70 | #endif 71 | 72 | p_filefunc64_32->zfile_func64.opaque = p_filefunc32->opaque; 73 | p_filefunc64_32->zseek32_file = p_filefunc32->zseek_file; 74 | p_filefunc64_32->ztell32_file = p_filefunc32->ztell_file; 75 | } 76 | 77 | 78 | 79 | static voidpf ZCALLBACK fopen_file_func OF((voidpf opaque, const char* filename, int mode)); 80 | static uLong ZCALLBACK fread_file_func OF((voidpf opaque, voidpf stream, void* buf, uLong size)); 81 | static uLong ZCALLBACK fwrite_file_func OF((voidpf opaque, voidpf stream, const void* buf,uLong size)); 82 | static ZPOS64_T ZCALLBACK ftell64_file_func OF((voidpf opaque, voidpf stream)); 83 | static long ZCALLBACK fseek64_file_func OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); 84 | static int ZCALLBACK fclose_file_func OF((voidpf opaque, voidpf stream)); 85 | static int ZCALLBACK ferror_file_func OF((voidpf opaque, voidpf stream)); 86 | 87 | static voidpf ZCALLBACK fopen_file_func (voidpf opaque, const char* filename, int mode) 88 | { 89 | FILE* file = NULL; 90 | const char* mode_fopen = NULL; 91 | if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) 92 | mode_fopen = "rb"; 93 | else 94 | if (mode & ZLIB_FILEFUNC_MODE_EXISTING) 95 | mode_fopen = "r+b"; 96 | else 97 | if (mode & ZLIB_FILEFUNC_MODE_CREATE) 98 | mode_fopen = "wb"; 99 | 100 | if ((filename!=NULL) && (mode_fopen != NULL)) 101 | file = fopen(filename, mode_fopen); 102 | return file; 103 | } 104 | 105 | static voidpf ZCALLBACK fopen64_file_func (voidpf opaque, const void* filename, int mode) 106 | { 107 | FILE* file = NULL; 108 | const char* mode_fopen = NULL; 109 | if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) 110 | mode_fopen = "rb"; 111 | else 112 | if (mode & ZLIB_FILEFUNC_MODE_EXISTING) 113 | mode_fopen = "r+b"; 114 | else 115 | if (mode & ZLIB_FILEFUNC_MODE_CREATE) 116 | mode_fopen = "wb"; 117 | 118 | if ((filename!=NULL) && (mode_fopen != NULL)) 119 | file = fopen64((const char*)filename, mode_fopen); 120 | return file; 121 | } 122 | 123 | 124 | static uLong ZCALLBACK fread_file_func (voidpf opaque, voidpf stream, void* buf, uLong size) 125 | { 126 | uLong ret; 127 | ret = (uLong)fread(buf, 1, (size_t)size, (FILE *)stream); 128 | return ret; 129 | } 130 | 131 | static uLong ZCALLBACK fwrite_file_func (voidpf opaque, voidpf stream, const void* buf, uLong size) 132 | { 133 | uLong ret; 134 | ret = (uLong)fwrite(buf, 1, (size_t)size, (FILE *)stream); 135 | return ret; 136 | } 137 | 138 | static long ZCALLBACK ftell_file_func (voidpf opaque, voidpf stream) 139 | { 140 | long ret; 141 | ret = ftell((FILE *)stream); 142 | return ret; 143 | } 144 | 145 | 146 | static ZPOS64_T ZCALLBACK ftell64_file_func (voidpf opaque, voidpf stream) 147 | { 148 | ZPOS64_T ret; 149 | ret = ftello64((FILE *)stream); 150 | return ret; 151 | } 152 | 153 | static long ZCALLBACK fseek_file_func (voidpf opaque, voidpf stream, uLong offset, int origin) 154 | { 155 | int fseek_origin=0; 156 | long ret; 157 | switch (origin) 158 | { 159 | case ZLIB_FILEFUNC_SEEK_CUR : 160 | fseek_origin = SEEK_CUR; 161 | break; 162 | case ZLIB_FILEFUNC_SEEK_END : 163 | fseek_origin = SEEK_END; 164 | break; 165 | case ZLIB_FILEFUNC_SEEK_SET : 166 | fseek_origin = SEEK_SET; 167 | break; 168 | default: return -1; 169 | } 170 | ret = 0; 171 | if (fseek((FILE *)stream, offset, fseek_origin) != 0) 172 | ret = -1; 173 | return ret; 174 | } 175 | 176 | static long ZCALLBACK fseek64_file_func (voidpf opaque, voidpf stream, ZPOS64_T offset, int origin) 177 | { 178 | int fseek_origin=0; 179 | long ret; 180 | switch (origin) 181 | { 182 | case ZLIB_FILEFUNC_SEEK_CUR : 183 | fseek_origin = SEEK_CUR; 184 | break; 185 | case ZLIB_FILEFUNC_SEEK_END : 186 | fseek_origin = SEEK_END; 187 | break; 188 | case ZLIB_FILEFUNC_SEEK_SET : 189 | fseek_origin = SEEK_SET; 190 | break; 191 | default: return -1; 192 | } 193 | ret = 0; 194 | 195 | if(fseeko64((FILE *)stream, (long)offset, fseek_origin) != 0) 196 | ret = -1; 197 | 198 | return ret; 199 | } 200 | 201 | 202 | static int ZCALLBACK fclose_file_func (voidpf opaque, voidpf stream) 203 | { 204 | int ret; 205 | ret = fclose((FILE *)stream); 206 | return ret; 207 | } 208 | 209 | static int ZCALLBACK ferror_file_func (voidpf opaque, voidpf stream) 210 | { 211 | int ret; 212 | ret = ferror((FILE *)stream); 213 | return ret; 214 | } 215 | 216 | void fill_fopen_filefunc (pzlib_filefunc_def) 217 | zlib_filefunc_def* pzlib_filefunc_def; 218 | { 219 | pzlib_filefunc_def->zopen_file = fopen_file_func; 220 | pzlib_filefunc_def->zread_file = fread_file_func; 221 | pzlib_filefunc_def->zwrite_file = fwrite_file_func; 222 | pzlib_filefunc_def->ztell_file = ftell_file_func; 223 | pzlib_filefunc_def->zseek_file = fseek_file_func; 224 | pzlib_filefunc_def->zclose_file = fclose_file_func; 225 | pzlib_filefunc_def->zerror_file = ferror_file_func; 226 | pzlib_filefunc_def->opaque = NULL; 227 | } 228 | 229 | void fill_fopen64_filefunc (zlib_filefunc64_def* pzlib_filefunc_def) 230 | { 231 | pzlib_filefunc_def->zopen64_file = fopen64_file_func; 232 | pzlib_filefunc_def->zread_file = fread_file_func; 233 | pzlib_filefunc_def->zwrite_file = fwrite_file_func; 234 | pzlib_filefunc_def->ztell64_file = ftell64_file_func; 235 | pzlib_filefunc_def->zseek64_file = fseek64_file_func; 236 | pzlib_filefunc_def->zclose_file = fclose_file_func; 237 | pzlib_filefunc_def->zerror_file = ferror_file_func; 238 | pzlib_filefunc_def->opaque = NULL; 239 | } 240 | -------------------------------------------------------------------------------- /www/index.js: -------------------------------------------------------------------------------- 1 | /* global cordova:false */ 2 | 3 | /*! 4 | * Module dependencies. 5 | */ 6 | 7 | var exec = cordova.require('cordova/exec'); 8 | 9 | /** 10 | * ContentSync constructor. 11 | * 12 | * @param {Object} options to initiate a new content synchronization. 13 | * @param {String} src is a URL to the content sync end-point. 14 | * @param {String} id is used as a unique identifier for the sync operation 15 | * @param {Object} type defines the sync strategy applied to the content. 16 | * @param {String} replace completely removes existing content then copies new content. 17 | * @param {String} merge does not modify existing content, but adds new content. 18 | * @param {Object} headers are used to set the headers for when we send a request to the src URL 19 | * @param {Boolean} validateSrc whether to validate src url with a HEAD request before download (ios only, default true). 20 | * @return {ContentSync} instance that can be monitored and cancelled. 21 | */ 22 | 23 | var ContentSync = function(options) { 24 | this._handlers = { 25 | 'progress': [], 26 | 'cancel': [], 27 | 'error': [], 28 | 'complete': [] 29 | }; 30 | 31 | // require options parameter 32 | if (typeof options === 'undefined') { 33 | throw new Error('The options argument is required.'); 34 | } 35 | 36 | // require options.src parameter 37 | if (typeof options.src === 'undefined' && options.type !== "local") { 38 | throw new Error('The options.src argument is required for merge replace types.'); 39 | } 40 | 41 | // require options.id parameter 42 | if (typeof options.id === 'undefined') { 43 | throw new Error('The options.id argument is required.'); 44 | } 45 | 46 | // define synchronization strategy 47 | // 48 | // replace: This is the normal behavior. Existing content is replaced 49 | // completely by the imported content, i.e. is overridden or 50 | // deleted accordingly. 51 | // merge: Existing content is not modified, i.e. only new content is 52 | // added and none is deleted or modified. 53 | // local: Existing content is not modified, i.e. only new content is 54 | // added and none is deleted or modified. 55 | // 56 | if (typeof options.type === 'undefined') { 57 | options.type = 'replace'; 58 | } 59 | 60 | if (typeof options.headers === 'undefined') { 61 | options.headers = null; 62 | } 63 | 64 | if (typeof options.copyCordovaAssets === 'undefined') { 65 | options.copyCordovaAssets = false; 66 | } 67 | 68 | if (typeof options.copyRootApp === 'undefined') { 69 | options.copyRootApp = false; 70 | } 71 | 72 | if (typeof options.timeout === 'undefined') { 73 | options.timeout = 15.0; 74 | } 75 | 76 | if (typeof options.trustHost === 'undefined') { 77 | options.trustHost = false; 78 | } 79 | 80 | if (typeof options.manifest === 'undefined') { 81 | options.manifest = ""; 82 | } 83 | 84 | if (typeof options.validateSrc === 'undefined') { 85 | options.validateSrc = true; 86 | } 87 | 88 | // store the options to this object instance 89 | this.options = options; 90 | 91 | // triggered on update and completion 92 | var that = this; 93 | var success = function(result) { 94 | if (result && typeof result.progress !== 'undefined') { 95 | that.emit('progress', result); 96 | } else if (result && typeof result.localPath !== 'undefined') { 97 | that.emit('complete', result); 98 | } 99 | }; 100 | 101 | // triggered on error 102 | var fail = function(msg) { 103 | var e = (typeof msg === 'string') ? new Error(msg) : msg; 104 | that.emit('error', e); 105 | }; 106 | 107 | // wait at least one process tick to allow event subscriptions 108 | setTimeout(function() { 109 | exec(success, fail, 'Sync', 'sync', [options.src, options.id, options.type, options.headers, options.copyCordovaAssets, options.copyRootApp, options.timeout, options.trustHost, options.manifest, options.validateSrc]); 110 | }, 10); 111 | }; 112 | 113 | /** 114 | * Cancel the Content Sync 115 | * 116 | * After successfully canceling the content sync process, the `cancel` event 117 | * will be emitted. 118 | */ 119 | 120 | ContentSync.prototype.cancel = function() { 121 | var that = this; 122 | var onCancel = function() { 123 | that.emit('cancel'); 124 | }; 125 | setTimeout(function() { 126 | exec(onCancel, onCancel, 'Sync', 'cancel', [ that.options.id ]); 127 | }, 10); 128 | }; 129 | 130 | /** 131 | * Listen for an event. 132 | * 133 | * The following events are supported: 134 | * 135 | * - progress 136 | * - cancel 137 | * - error 138 | * - completion 139 | * 140 | * @param {String} eventName to subscribe to. 141 | * @param {Function} callback triggered on the event. 142 | */ 143 | 144 | ContentSync.prototype.on = function(eventName, callback) { 145 | if (this._handlers.hasOwnProperty(eventName)) { 146 | this._handlers[eventName].push(callback); 147 | } 148 | }; 149 | 150 | /** 151 | * Emit an event. 152 | * 153 | * This is intended for internal use only. 154 | * 155 | * @param {String} eventName is the event to trigger. 156 | * @param {*} all arguments are passed to the event listeners. 157 | * 158 | * @return {Boolean} is true when the event is triggered otherwise false. 159 | */ 160 | 161 | ContentSync.prototype.emit = function() { 162 | var args = Array.prototype.slice.call(arguments); 163 | var eventName = args.shift(); 164 | 165 | if (!this._handlers.hasOwnProperty(eventName)) { 166 | return false; 167 | } 168 | 169 | for (var i = 0, length = this._handlers[eventName].length; i < length; i++) { 170 | this._handlers[eventName][i].apply(undefined,args); 171 | } 172 | 173 | return true; 174 | }; 175 | 176 | /*! 177 | * Content Sync Plugin. 178 | */ 179 | 180 | module.exports = { 181 | /** 182 | * Synchronize the content. 183 | * 184 | * This method will instantiate a new copy of the ContentSync object 185 | * and start synchronizing. 186 | * 187 | * @param {Object} options 188 | * @return {ContentSync} instance 189 | */ 190 | 191 | sync: function(options) { 192 | return new ContentSync(options); 193 | }, 194 | 195 | /** 196 | * Unzip 197 | * 198 | * This call is to replicate Zip::unzip plugin 199 | * 200 | */ 201 | 202 | unzip: function(fileUrl, dirUrl, callback, progressCallback) { 203 | var win = function(result) { 204 | if (result && result.progress) { 205 | if (progressCallback) { 206 | progressCallback(result); 207 | } 208 | } else if (callback) { 209 | callback(0); 210 | } 211 | }; 212 | var fail = function(result) { 213 | if (callback) { 214 | callback(-1); 215 | } 216 | }; 217 | exec(win, fail, 'Zip', 'unzip', [fileUrl, dirUrl]); 218 | }, 219 | 220 | /** 221 | * Download 222 | * 223 | * This call is to replicate nothing but might be used instead of FileTransfer 224 | * 225 | */ 226 | 227 | download: function(url, headers, cb) { 228 | var callback = (typeof headers == "function" ? headers : cb); 229 | exec(callback, callback, 'Sync', 'download', [url, null, headers]); 230 | }, 231 | 232 | /** 233 | * loadUrl 234 | * 235 | * This method allows loading file:// urls when using WKWebViews on iOS. 236 | * 237 | */ 238 | 239 | loadUrl: function(url, cb) { 240 | if(!url) { 241 | throw new Error('URL is required.'); 242 | } 243 | exec(cb, cb, 'Sync', 'loadUrl', [url]); 244 | }, 245 | 246 | 247 | /** 248 | * ContentSync Object. 249 | * 250 | * Expose the ContentSync object for direct use 251 | * and testing. Typically, you should use the 252 | * .sync helper method. 253 | */ 254 | 255 | ContentSync: ContentSync, 256 | 257 | /** 258 | * PROGRESS_STATE enumeration. 259 | * 260 | * Maps to the `progress` event's `status` object. 261 | * The plugin user can customize the enumeration's mapped string 262 | * to a value that's appropriate for their app. 263 | */ 264 | 265 | PROGRESS_STATE: { 266 | 0: 'STOPPED', 267 | 1: 'DOWNLOADING', 268 | 2: 'EXTRACTING', 269 | 3: 'COMPLETE' 270 | }, 271 | 272 | /** 273 | * ERROR_STATE enumeration. 274 | * 275 | * Maps to the `error` event's `status` object. 276 | * The plugin user can customize the enumeration's mapped string 277 | * to a value that's appropriate for their app. 278 | */ 279 | 280 | ERROR_STATE: { 281 | 1: 'INVALID_URL_ERR', 282 | 2: 'CONNECTION_ERR', 283 | 3: 'UNZIP_ERR' 284 | } 285 | }; 286 | -------------------------------------------------------------------------------- /src/ios/minizip/mztools.c: -------------------------------------------------------------------------------- 1 | /* 2 | Additional tools for Minizip 3 | Code: Xavier Roche '2004 4 | License: Same as ZLIB (www.gzip.org) 5 | */ 6 | 7 | /* Code */ 8 | #include 9 | #include 10 | #include 11 | #include "zlib.h" 12 | #include "unzip.h" 13 | #include "mztools.h" 14 | 15 | #define READ_8(adr) ((unsigned char)*(adr)) 16 | #define READ_16(adr) ( READ_8(adr) | (READ_8(adr+1) << 8) ) 17 | #define READ_32(adr) ( READ_16(adr) | (READ_16((adr)+2) << 16) ) 18 | 19 | #define WRITE_8(buff, n) do { \ 20 | *((unsigned char*)(buff)) = (unsigned char) ((n) & 0xff); \ 21 | } while(0) 22 | #define WRITE_16(buff, n) do { \ 23 | WRITE_8((unsigned char*)(buff), n); \ 24 | WRITE_8(((unsigned char*)(buff)) + 1, (n) >> 8); \ 25 | } while(0) 26 | #define WRITE_32(buff, n) do { \ 27 | WRITE_16((unsigned char*)(buff), (n) & 0xffff); \ 28 | WRITE_16((unsigned char*)(buff) + 2, (n) >> 16); \ 29 | } while(0) 30 | 31 | extern int ZEXPORT unzRepair(file, fileOut, fileOutTmp, nRecovered, bytesRecovered) 32 | const char* file; 33 | const char* fileOut; 34 | const char* fileOutTmp; 35 | uLong* nRecovered; 36 | uLong* bytesRecovered; 37 | { 38 | int err = Z_OK; 39 | FILE* fpZip = fopen(file, "rb"); 40 | FILE* fpOut = fopen(fileOut, "wb"); 41 | FILE* fpOutCD = fopen(fileOutTmp, "wb"); 42 | if (fpZip != NULL && fpOut != NULL) { 43 | int entries = 0; 44 | uLong totalBytes = 0; 45 | char header[30]; 46 | char filename[256]; 47 | char extra[1024]; 48 | int offset = 0; 49 | int offsetCD = 0; 50 | while ( fread(header, 1, 30, fpZip) == 30 ) { 51 | int currentOffset = offset; 52 | 53 | /* File entry */ 54 | if (READ_32(header) == 0x04034b50) { 55 | unsigned int version = READ_16(header + 4); 56 | unsigned int gpflag = READ_16(header + 6); 57 | unsigned int method = READ_16(header + 8); 58 | unsigned int filetime = READ_16(header + 10); 59 | unsigned int filedate = READ_16(header + 12); 60 | unsigned int crc = READ_32(header + 14); /* crc */ 61 | unsigned int cpsize = READ_32(header + 18); /* compressed size */ 62 | unsigned int uncpsize = READ_32(header + 22); /* uncompressed sz */ 63 | unsigned int fnsize = READ_16(header + 26); /* file name length */ 64 | unsigned int extsize = READ_16(header + 28); /* extra field length */ 65 | filename[0] = extra[0] = '\0'; 66 | 67 | /* Header */ 68 | if (fwrite(header, 1, 30, fpOut) == 30) { 69 | offset += 30; 70 | } else { 71 | err = Z_ERRNO; 72 | break; 73 | } 74 | 75 | /* Filename */ 76 | if (fnsize > 0) { 77 | if (fread(filename, 1, fnsize, fpZip) == fnsize) { 78 | if (fwrite(filename, 1, fnsize, fpOut) == fnsize) { 79 | offset += fnsize; 80 | } else { 81 | err = Z_ERRNO; 82 | break; 83 | } 84 | } else { 85 | err = Z_ERRNO; 86 | break; 87 | } 88 | } else { 89 | err = Z_STREAM_ERROR; 90 | break; 91 | } 92 | 93 | /* Extra field */ 94 | if (extsize > 0) { 95 | if (fread(extra, 1, extsize, fpZip) == extsize) { 96 | if (fwrite(extra, 1, extsize, fpOut) == extsize) { 97 | offset += extsize; 98 | } else { 99 | err = Z_ERRNO; 100 | break; 101 | } 102 | } else { 103 | err = Z_ERRNO; 104 | break; 105 | } 106 | } 107 | 108 | /* Data */ 109 | { 110 | int dataSize = cpsize; 111 | if (dataSize == 0) { 112 | dataSize = uncpsize; 113 | } 114 | if (dataSize > 0) { 115 | char* data = malloc(dataSize); 116 | if (data != NULL) { 117 | if ((int)fread(data, 1, dataSize, fpZip) == dataSize) { 118 | if ((int)fwrite(data, 1, dataSize, fpOut) == dataSize) { 119 | offset += dataSize; 120 | totalBytes += dataSize; 121 | } else { 122 | err = Z_ERRNO; 123 | } 124 | } else { 125 | err = Z_ERRNO; 126 | } 127 | free(data); 128 | if (err != Z_OK) { 129 | break; 130 | } 131 | } else { 132 | err = Z_MEM_ERROR; 133 | break; 134 | } 135 | } 136 | } 137 | 138 | /* Central directory entry */ 139 | { 140 | char centralDirectoryEntryHeader[46]; 141 | //char* comment = ""; 142 | //int comsize = (int) strlen(comment); 143 | WRITE_32(centralDirectoryEntryHeader, 0x02014b50); 144 | WRITE_16(centralDirectoryEntryHeader + 4, version); 145 | WRITE_16(centralDirectoryEntryHeader + 6, version); 146 | WRITE_16(centralDirectoryEntryHeader + 8, gpflag); 147 | WRITE_16(centralDirectoryEntryHeader + 10, method); 148 | WRITE_16(centralDirectoryEntryHeader + 12, filetime); 149 | WRITE_16(centralDirectoryEntryHeader + 14, filedate); 150 | WRITE_32(centralDirectoryEntryHeader + 16, crc); 151 | WRITE_32(centralDirectoryEntryHeader + 20, cpsize); 152 | WRITE_32(centralDirectoryEntryHeader + 24, uncpsize); 153 | WRITE_16(centralDirectoryEntryHeader + 28, fnsize); 154 | WRITE_16(centralDirectoryEntryHeader + 30, extsize); 155 | WRITE_16(centralDirectoryEntryHeader + 32, 0 /*comsize*/); 156 | WRITE_16(centralDirectoryEntryHeader + 34, 0); /* disk # */ 157 | WRITE_16(centralDirectoryEntryHeader + 36, 0); /* int attrb */ 158 | WRITE_32(centralDirectoryEntryHeader + 38, 0); /* ext attrb */ 159 | WRITE_32(centralDirectoryEntryHeader + 42, currentOffset); 160 | /* Header */ 161 | if (fwrite(centralDirectoryEntryHeader, 1, 46, fpOutCD) == 46) { 162 | offsetCD += 46; 163 | 164 | /* Filename */ 165 | if (fnsize > 0) { 166 | if (fwrite(filename, 1, fnsize, fpOutCD) == fnsize) { 167 | offsetCD += fnsize; 168 | } else { 169 | err = Z_ERRNO; 170 | break; 171 | } 172 | } else { 173 | err = Z_STREAM_ERROR; 174 | break; 175 | } 176 | 177 | /* Extra field */ 178 | if (extsize > 0) { 179 | if (fwrite(extra, 1, extsize, fpOutCD) == extsize) { 180 | offsetCD += extsize; 181 | } else { 182 | err = Z_ERRNO; 183 | break; 184 | } 185 | } 186 | 187 | /* Comment field */ 188 | /* 189 | if (comsize > 0) { 190 | if ((int)fwrite(comment, 1, comsize, fpOutCD) == comsize) { 191 | offsetCD += comsize; 192 | } else { 193 | err = Z_ERRNO; 194 | break; 195 | } 196 | } 197 | */ 198 | 199 | } else { 200 | err = Z_ERRNO; 201 | break; 202 | } 203 | } 204 | 205 | /* Success */ 206 | entries++; 207 | 208 | } else { 209 | break; 210 | } 211 | } 212 | 213 | /* Final central directory */ 214 | { 215 | int entriesZip = entries; 216 | char finalCentralDirectoryHeader[22]; 217 | //char* comment = ""; // "ZIP File recovered by zlib/minizip/mztools"; 218 | //int comsize = (int) strlen(comment); 219 | if (entriesZip > 0xffff) { 220 | entriesZip = 0xffff; 221 | } 222 | WRITE_32(finalCentralDirectoryHeader, 0x06054b50); 223 | WRITE_16(finalCentralDirectoryHeader + 4, 0); /* disk # */ 224 | WRITE_16(finalCentralDirectoryHeader + 6, 0); /* disk # */ 225 | WRITE_16(finalCentralDirectoryHeader + 8, entriesZip); /* hack */ 226 | WRITE_16(finalCentralDirectoryHeader + 10, entriesZip); /* hack */ 227 | WRITE_32(finalCentralDirectoryHeader + 12, offsetCD); /* size of CD */ 228 | WRITE_32(finalCentralDirectoryHeader + 16, offset); /* offset to CD */ 229 | WRITE_16(finalCentralDirectoryHeader + 20, 0 /*comsize*/); /* comment */ 230 | 231 | /* Header */ 232 | if (fwrite(finalCentralDirectoryHeader, 1, 22, fpOutCD) == 22) { 233 | 234 | /* Comment field */ 235 | /* 236 | if (comsize > 0) { 237 | if ((int)fwrite(comment, 1, comsize, fpOutCD) != comsize) { 238 | err = Z_ERRNO; 239 | } 240 | } 241 | */ 242 | } else { 243 | err = Z_ERRNO; 244 | } 245 | } 246 | 247 | /* Final merge (file + central directory) */ 248 | fclose(fpOutCD); 249 | if (err == Z_OK) { 250 | fpOutCD = fopen(fileOutTmp, "rb"); 251 | if (fpOutCD != NULL) { 252 | int nRead; 253 | char buffer[8192]; 254 | while ( (nRead = (int)fread(buffer, 1, sizeof(buffer), fpOutCD)) > 0) { 255 | if ((int)fwrite(buffer, 1, nRead, fpOut) != nRead) { 256 | err = Z_ERRNO; 257 | break; 258 | } 259 | } 260 | fclose(fpOutCD); 261 | } 262 | } 263 | 264 | /* Close */ 265 | fclose(fpZip); 266 | fclose(fpOut); 267 | 268 | /* Wipe temporary file */ 269 | (void)remove(fileOutTmp); 270 | 271 | /* Number of recovered entries */ 272 | if (err == Z_OK) { 273 | if (nRecovered != NULL) { 274 | *nRecovered = entries; 275 | } 276 | if (bytesRecovered != NULL) { 277 | *bytesRecovered = totalBytes; 278 | } 279 | } 280 | } else { 281 | err = Z_STREAM_ERROR; 282 | } 283 | return err; 284 | } 285 | -------------------------------------------------------------------------------- /tests/tests.js: -------------------------------------------------------------------------------- 1 | exports.defineAutoTests = function() { 2 | 3 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 30000; 4 | 5 | describe('phonegap-plugin-contentsync', function() { 6 | 7 | it("should exist", function() { 8 | expect(window.ContentSync).toBeDefined(); 9 | expect(typeof window.ContentSync.sync == 'function').toBe(true); 10 | expect(typeof window.ContentSync.download == 'function').toBe(true); 11 | expect(typeof window.ContentSync.unzip == 'function').toBe(true); 12 | }); 13 | 14 | function syncArchive(url, done) { 15 | 16 | var progressEvent = null; 17 | var sync = ContentSync.sync({ src: url, id: 'myapps/myapp', type: 'replace', copyCordovaAssets: false, headers: false }); 18 | 19 | 20 | sync.on('progress', function(progress) { 21 | //console.log("in progress callback " + Object.getOwnPropertyNames(progress)); 22 | //console.log("onProgress :: " + progress.progress + " status = " + progress.status); 23 | if(!progressEvent) { 24 | progressEvent = progress; 25 | } 26 | }); 27 | 28 | sync.on('complete', function(data) { 29 | //console.log("progress = " + progressEvent); 30 | expect(progressEvent).toBeDefined("Progress should have been received"); 31 | 32 | //console.log("progressEvent.status = " + progressEvent.status); 33 | expect(progressEvent.status).toBeDefined("Progress event should have a status prop"); 34 | 35 | expect(progressEvent.progress).toBeDefined("Progress event should have a progress prop"); 36 | //console.log("progressEvent.progress = " + progressEvent.progress); 37 | 38 | //console.log("data = " + data); 39 | expect(data).toBeDefined("On complete, data is not null"); 40 | done(); 41 | }); 42 | 43 | sync.on('error', function (e) { 44 | expect(progressEvent).toBeDefined("Progress should have been received"); 45 | expect(e).toBe(null, "Error callback was called :: " + JSON.stringify(e)); 46 | //console.log("got error back :: " + e); 47 | done(); 48 | }); 49 | 50 | } 51 | 52 | it("can sync archive without www folder at root", function(done){ 53 | var url = "http://localhost:4321/www1.zip"; 54 | syncArchive(url, done); 55 | }, 60000); // wait a full 60 secs 56 | 57 | it("can sync archive with www folder at root", function(done){ 58 | var url = "http://localhost:4321/www2.zip"; 59 | syncArchive(url, done); 60 | }, 60000); // wait a full 60 secs 61 | 62 | it('reports error on 404', function(done){ 63 | var sync = ContentSync.sync({ 64 | src: 'https://www.google.com/error/not/found.zip', 65 | id: 'test' + (new Date().getTime()), // ensure that repeated tests work 66 | type: 'replace', 67 | copyCordovaAssets: false 68 | }); 69 | sync.on('complete', function() { 70 | fail('404 page should not complete'); 71 | done(); 72 | }); 73 | 74 | sync.on('error', function(e) { 75 | expect(e).toBeDefined('error should be reported'); 76 | done(); 77 | }); 78 | }, 60000); // wait a full 60 secs for slow Android emulator 79 | 80 | it('tests copyCordovaAssets works without copyRootApp', function(done) { 81 | var appId = 'copyCordovaAssets' + (new Date().getTime()); 82 | var sync = ContentSync.sync({ 83 | id: appId, 84 | copyCordovaAssets: true, 85 | type: 'local' 86 | }); 87 | 88 | sync.on('complete', function(data) { 89 | // cordova.js should be available in the synced directory 90 | testFileExists(appId + '/cordova.js', function success() { 91 | done(); 92 | }, function fail() { 93 | fail('cordova.js should exist in the synced directory.'); 94 | }); 95 | }); 96 | }); 97 | 98 | /** 99 | * Helper function that tests if the file at the given path exists 100 | */ 101 | function testFileExists(path, success, fail) { 102 | var filePath; 103 | if (path.indexOf('file://') === 0) { 104 | // test via system url 105 | filePath = path; 106 | } else { 107 | // test via cordova.file.dataDirectory location 108 | filePath = cordova.file.dataDirectory + '/' + path; 109 | } 110 | window.resolveLocalFileSystemURL(filePath, function(fileEntry) { 111 | expect(fileEntry).toBeDefined(); 112 | success(); 113 | }, function(e){ 114 | fail(path + ' should exist in local copy. Error code ' + e.code); 115 | }); 116 | } 117 | 118 | /** 119 | * Helper function that syncs and test if the local copy has the `/index.html` 120 | */ 121 | function syncAndTest(appId, useLocalPath, success, fail) { 122 | var sync = ContentSync.sync({ 123 | id: appId, 124 | type: 'local', 125 | copyRootApp: true 126 | }); 127 | sync.on('complete', function (data) { 128 | if (useLocalPath && cordova.platformId !== 'windows') { 129 | testFileExists('file://' + data.localPath + '/index.html', success, fail); 130 | } else { 131 | testFileExists(appId + '/index.html', success, fail); 132 | } 133 | }); 134 | sync.on('error', function (e) { 135 | fail(); 136 | }); 137 | } 138 | 139 | /** 140 | * Tests if the local copy is at the correct place and can be accessed via file plugin. 141 | */ 142 | it('local copy is accessible via file plugin', function(done) { 143 | var appId = 'test' + (new Date()).getTime(); // create new id every time 144 | syncAndTest(appId, false, done, function(e){ 145 | fail(e); 146 | done(); 147 | }) 148 | }, 60000); // wait a full 60 secs for slow Android emulator 149 | 150 | it('create local copy with www prefix', function(done) { 151 | var appId = 'www/local/test' + (new Date()).getTime(); // create new id every time 152 | syncAndTest(appId, true, done, function(e){ 153 | fail(e); 154 | done(); 155 | }) 156 | }, 60000); // wait a full 60 secs for slow Android emulator 157 | 158 | it('create local copy with www suffix', function(done) { 159 | var appId = 'local/test' + (new Date()).getTime() + '/www'; // create new id every time 160 | syncAndTest(appId, true, done, function(e){ 161 | fail(e); 162 | done(); 163 | }) 164 | }, 60000); // wait a full 60 secs for slow Android emulator 165 | 166 | /** 167 | * Test for invalid server name 168 | */ 169 | it('error on invalid server name', function(done) { 170 | var sync = ContentSync.sync({ 171 | src: 'http://servername', 172 | id: 'test' + (new Date().getTime()), // ensure that repeated tests work 173 | type: 'replace', 174 | copyCordovaAssets: false 175 | }); 176 | sync.on('complete', function() { 177 | fail('invalid server name should not complete'); 178 | done(); 179 | }); 180 | 181 | sync.on('error', function(e) { 182 | expect(e).toBeDefined('error should be reported'); 183 | done(); 184 | }); 185 | }); 186 | 187 | }); 188 | 189 | 190 | if(cordova.platformId == 'windows') { 191 | describe('phonegap-plugin-contentsync windows tests', function() { 192 | it("Has linked C# code", function(done){ 193 | // 194 | expect(ZipWinProj).toBeDefined("ZipWinProj should exist"); 195 | expect(ZipWinProj.PGZipInflate) 196 | .toBeDefined("ZipWinProj.PGZipInflate should exist"); 197 | expect(ZipWinProj.PGZipInflate.inflateAsync) 198 | .toBeDefined("ZipWinProj.PGZipInflate.inflateAsync should exist"); 199 | done(); 200 | }); 201 | 202 | }); 203 | } 204 | 205 | if (cordova.platformId === 'osx') { 206 | it("syncing the same id concurrently should fail", function(done) { 207 | 208 | var url = "https://github.com/timkim/zipTest/archive/master.zip"; 209 | var sync1 = ContentSync.sync({ 210 | src: url, 211 | id: 'myapps/myapp', 212 | type: 'replace', 213 | copyCordovaAssets: false, 214 | headers: false 215 | }); 216 | var sync2 = ContentSync.sync({ 217 | src: url, 218 | id: 'myapps/myapp', 219 | type: 'replace', 220 | copyCordovaAssets: false, 221 | headers: false 222 | }); 223 | 224 | var numFinished = 0; 225 | 226 | sync1.on('complete', function(data) { 227 | expect(data).toBeDefined("On complete, data is not null"); 228 | if (++numFinished == 2) { 229 | done(); 230 | } 231 | }); 232 | sync1.on('error', function(e) { 233 | fail(e); 234 | done(); 235 | }); 236 | 237 | sync2.on('complete', function(data) { 238 | fail('syncing concurrently the same id should fail.'); 239 | done(); 240 | }); 241 | sync2.on('error', function(e) { 242 | expect(e).toEqual(5); 243 | if (++numFinished == 2) { 244 | done(); 245 | } 246 | }); 247 | 248 | }, 60000); // wait a full 60 secs 249 | } 250 | 251 | }; 252 | 253 | exports.defineManualTests = function() { 254 | 255 | }; 256 | -------------------------------------------------------------------------------- /src/windows/SyncProxy.js: -------------------------------------------------------------------------------- 1 | // progress-state 2 | // 0:stopped, 1:downloading,2:extracting,3:complete 3 | 4 | // error-state 5 | // 1:invalid url 6 | // 2:connection err 7 | // 3:unzip err 8 | 9 | 10 | var appData = Windows.Storage.ApplicationData.current; 11 | var FileOpts = Windows.Storage.CreationCollisionOption; 12 | var getFolderFromPathAsync = Windows.Storage.StorageFolder.getFolderFromPathAsync; 13 | var getFileFromPathAsync = Windows.Storage.StorageFile.getFileFromPathAsync; 14 | var replaceExisting = Windows.Storage.NameCollisionOption.replaceExisting; 15 | var AppPath = Windows.ApplicationModel.Package.current.installedLocation.path; 16 | 17 | function cleanPath(pathStr) { 18 | return pathStr.replace(/\//g, "\\"); 19 | } 20 | 21 | function copyAndReplaceFileFromPathAsync(path,dest) { 22 | return Windows.Storage.StorageFile.getFileFromPathAsync(path) 23 | .then(function (file) { 24 | return file.copyAsync(dest, file.name, Windows.Storage.NameCollisionOption.replaceExisting); 25 | }); 26 | } 27 | 28 | function copyCordovaAssetsAsync(wwwFolder, destWWWFolder) { 29 | return getFolderFromPathAsync(wwwFolder.path + "\\plugins") 30 | .then(function (pluginsFolder) { 31 | return WinJS.Promise.join([recursiveCopyFolderAsync(pluginsFolder, destWWWFolder, null, false), 32 | copyAndReplaceFileFromPathAsync(wwwFolder.path + "\\cordova.js", destWWWFolder), 33 | copyAndReplaceFileFromPathAsync(wwwFolder.path + "\\cordova_plugins.js", destWWWFolder)]); 34 | }); 35 | } 36 | 37 | // this can throw exceptions, callers responsibility 38 | function startDownload(src, storageFile) { 39 | var uri = Windows.Foundation.Uri(src); 40 | var downloader = new Windows.Networking.BackgroundTransfer.BackgroundDownloader(); 41 | var download = downloader.createDownload(uri, storageFile); 42 | return download.startAsync(); 43 | } 44 | 45 | function recursiveCopyFolderAsync(src, dst, name, skipRoot) { 46 | name = name ? name : src.name; 47 | 48 | var getDestFolder = function () { return WinJS.Promise.wrap(dst); }; 49 | if (!skipRoot) { 50 | getDestFolder = function () { 51 | return dst.createFolderAsync(name, FileOpts.openIfExists) 52 | } 53 | } 54 | 55 | return new WinJS.Promise(function (complete, failed) { 56 | WinJS.Promise.join({ 57 | destFolder: getDestFolder(), 58 | files: src.getFilesAsync(), 59 | folders: src.getFoldersAsync() 60 | }) 61 | .done(function (resultObj) { 62 | //console.log("destFolder = " + resultObj.destFolder.path); 63 | if (!(resultObj.files.length || resultObj.folders.length)) { 64 | // nothing to copy 65 | complete(); 66 | return 1; 67 | } 68 | var fileCount = resultObj.files.length; 69 | var copyfolders = function () { 70 | if (!fileCount--) { 71 | complete(); 72 | return 2; 73 | } 74 | recursiveCopyFolderAsync(resultObj.folders[fileCount], resultObj.destFolder) 75 | .done(function () { 76 | copyfolders(); 77 | }, failed); 78 | }; 79 | var copyfiles = function () { 80 | if (!fileCount--) { 81 | // done with files, move on to folders 82 | fileCount = resultObj.folders.length; 83 | copyfolders(); 84 | return 3; 85 | } 86 | var file = resultObj.files[fileCount]; 87 | //console.log("copying " + file.name + " => " + resultObj.destFolder.name); 88 | file.copyAsync(resultObj.destFolder || dst, file.name, replaceExisting) 89 | .done(function () { 90 | copyfiles(); 91 | }, failed); 92 | }; 93 | copyfiles(); 94 | }, 95 | failed); 96 | }); 97 | } 98 | 99 | 100 | var Sync = { 101 | sync: function (cbSuccess, cbFail, options) { 102 | 103 | // Note, all defaults are set in base file www/index.js 104 | // so we can proceed knowing these are all defined. 105 | // options = [ src,id,type,headers,bCopyCordovaAssets,bCopyRootApp,timeout] ; 106 | 107 | var src = options[0]; 108 | var id = cleanPath(options[1]); 109 | var type = options[2]; 110 | var headers = options[3]; 111 | var bCopyCordovaAssets = options[4]; 112 | var bCopyRootApp = options[5]; 113 | var timeout = options[6]; 114 | var trustHost = options[7]; 115 | var manifest = options[8]; 116 | 117 | var destFolderPath = cleanPath(id); 118 | var rootTempFolder = null; 119 | 120 | var destFolder = null; 121 | var destZipFile = null; 122 | var destWWWFolder = null; 123 | var fileName = id; 124 | 125 | if (id.indexOf("\\") > -1) { 126 | var pathParts = id.split("\\"); 127 | fileName = pathParts.pop(); 128 | } 129 | 130 | if (fileName.indexOf(".zip") < 0) { // todo, could be some.zip/file ... 131 | fileName += ".zip"; 132 | } 133 | 134 | var folderExisted = false; 135 | function getOrCreateLocalFolder(folderPath) { 136 | console.log("folderPath = " + folderPath); 137 | return appData.localFolder.getFolderAsync(folderPath) 138 | .then(function (folder) { 139 | folderExisted = true; 140 | return folder; 141 | }, 142 | function (err) { 143 | // folder does not exist, let's create it 144 | console.log("error: " + err.description); 145 | return appData.localFolder.createFolderAsync(folderPath) 146 | .then(function (folder) { 147 | return folder; 148 | }, 149 | function (err) { 150 | 151 | }); 152 | }); 153 | } 154 | 155 | WinJS.Promise.join({ 156 | wwwFolder: getFolderFromPathAsync(AppPath + "\\www"), 157 | destFolder: getOrCreateLocalFolder(destFolderPath), 158 | destWWWFolder: getOrCreateLocalFolder(destFolderPath + "\\www") 159 | }).done(function (res) { 160 | if (folderExisted && type == 'local') { 161 | // get out of the promise chain 162 | cbSuccess({ 'localPath': destFolderPath, 'status': 3 }, { keepCallback: false }); 163 | } 164 | else { 165 | destFolder = res.destFolder; 166 | wwwFolder = res.wwwFolder; 167 | destWWWFolder = res.destWWWFolder; 168 | 169 | var job = WinJS.Promise.wrap(null); 170 | if (bCopyRootApp) { 171 | job = recursiveCopyFolderAsync(wwwFolder, destFolder, "www", true); 172 | } 173 | else { 174 | 175 | } 176 | 177 | job = job.then(function () { 178 | return destFolder.createFileAsync(fileName, FileOpts.replaceExisting).then(function (storageFile) { 179 | destZipFile = storageFile; 180 | }, 181 | function (err) { 182 | console.log(err); 183 | }); 184 | }); 185 | job = job.then(function (res) { 186 | try { 187 | if (src) { 188 | return startDownload(src, destZipFile); 189 | } 190 | else { 191 | return false; 192 | } 193 | } catch (e) { 194 | console.log(e.message); 195 | cbFail(1); // INVALID_URL_ERR 196 | } 197 | }).then(function downloadComplete(dlResult) { // download is done 198 | if (dlResult) { 199 | //console.log("download is complete " + dlResult); 200 | cbSuccess({ 'progress': 50, 'status': 2 }, { keepCallback: true }); // EXTRACTING 201 | 202 | return ZipWinProj.PGZipInflate.inflateAsync(dlResult.resultFile, destFolder) 203 | .then(function (obj) { 204 | //console.log("got a result from inflateAsync :: " + obj); 205 | return true; 206 | }, 207 | function (e) { 208 | //console.log("got err from inflateAsync :: " + e); 209 | cbFail(3); // UNZIP_ERR 210 | return false; 211 | }); 212 | } 213 | else { 214 | return false; 215 | } 216 | 217 | }, 218 | function (err) { // download error 219 | console.log(err); 220 | cbFail(1); // INVALID_URL_ERR 221 | return false; 222 | }, 223 | function (progressEvent) { 224 | var total = progressEvent.progress.totalBytesToReceive; 225 | var bytes = progressEvent.progress.bytesReceived; 226 | var progPercent = total ? Math.round(bytes / total * 50) : 0; 227 | cbSuccess({ 'progress': progPercent, 'status': 1 }, { keepCallback: true }); // 0:stopped, 1:downloading, 2:extracting, 3:complete 228 | }) 229 | .then(function maybeCopyCordovaAssets(res) { 230 | return bCopyCordovaAssets ? copyCordovaAssetsAsync(wwwFolder, destWWWFolder) : null; 231 | }, 232 | function (err) { 233 | console.log("got err : " + err); 234 | }) 235 | .then(function (boom) { 236 | cbSuccess({ 'localPath': destFolder, 'status': 3 }, { keepCallback: false }); 237 | }) 238 | 239 | } 240 | }, 241 | function (err) { 242 | console.log("Error: " + err.description); 243 | cbFail(2); 244 | }); 245 | }, 246 | cancel: function (cbSuccess, cbFail, options) { 247 | var id = options.id; 248 | if (DownloadQueue[id]) { 249 | 250 | var downloadJob = DownloadQueue[id]; 251 | if (!downloadJob.isCancelled) { // prevent multiple callbacks for the same cancel 252 | downloadJob.isCancelled = true; 253 | if (!downloadJob.request) { 254 | // todo: abort it 255 | } 256 | DownloadQueue[id] = null; 257 | } 258 | cbSuccess(); 259 | } 260 | else { 261 | // TODO: error, id not found 262 | cbFail(); 263 | } 264 | }, 265 | download: function (cbSuccess, cbFail, options) { 266 | var url = options[0]; 267 | var unknown = options[1]; 268 | var headers = options[2]; 269 | }, 270 | unzip: function (cbSuccess, cbFail, options) { 271 | var srcUrl = options[0]; 272 | var destUrl = options[1]; 273 | } 274 | }; 275 | 276 | 277 | 278 | require("cordova/exec/proxy").add("Sync", Sync); 279 | require("cordova/exec/proxy").add("Zip", Sync); 280 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2013 Adobe Systems Incorporated. 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [v1.4.2](https://github.com/phonegap/phonegap-plugin-contentsync/tree/v1.4.1) (2017-12-19) 4 | 5 | [Full Changelog](https://github.com/phonegap/phonegap-plugin-contentsync/compare/v1.4.1...v1.4.2) 6 | 7 | * 🔧✅ Add package.json to tests plugin so cordova-paramedic runs [view commit](https://github.com/phonegap/phonegap-plugin-contentsync/commit/d90c39dd07ff030588afa6b51fd912efb92ec41a) 8 | * #188 - [iOS] calling sync() with options.copyCordovaAssets set does not perform copy [view commit](https://github.com/phonegap/phonegap-plugin-contentsync/commit/41961afb96fafa0c2f954ff39c7884bae5f89416) 9 | 10 | ## [v1.4.1](https://github.com/phonegap/phonegap-plugin-contentsync/tree/v1.4.1) (2017-11-10) 11 | 12 | [Full Changelog](https://github.com/phonegap/phonegap-plugin-contentsync/compare/v1.4.0...v1.4.1) 13 | 14 | * #186 - [iOS] copyRootApp runs a second time on the second launch of the application, overwriting updates [view commit](https://github.com/phonegap/phonegap-plugin-contentsync/commit/931f34a6e68f291b12dfd527ca9a5b83a158e0fb) 15 | 16 | ## [v1.4.0](https://github.com/phonegap/phonegap-plugin-contentsync/tree/v1.4.0) (2016-07-09) 17 | 18 | [Full Changelog](https://github.com/phonegap/phonegap-plugin-contentsync/compare/v1.3.6...v1.4.0) 19 | 20 | * 1.4.0 [view commit](http://github.com/phonegap/phonegap-plugin-contentsync/commit/b991d1de4a5e0eb5fc18a72125b07b93aec1b142) 21 | * :bookmark: Bumping plugin version to 1.4.0 [view commit](http://github.com/phonegap/phonegap-plugin-contentsync/commit/21ee2f5018c9e226bd69e92a9ae5445f9fad0e1c) 22 | * :wrench: Add package-lock.json [view commit](http://github.com/phonegap/phonegap-plugin-contentsync/commit/a52a0c28a5897ba4f7535c680c9abed24467d8f3) 23 | * :penguin: Issue #185: Google Play Blocker: Unsafe implementation of TrustManager [view commit](http://github.com/phonegap/phonegap-plugin-contentsync/commit/dbafa4c63a83a7c0e6d25e97d552fc4bb3570ba7) 24 | * :wrench: Add github template files [view commit](http://github.com/phonegap/phonegap-plugin-contentsync/commit/867685a40db875216e758233cd9ff8a114351344) 25 | * Fix headline (#178) [view commit](http://github.com/phonegap/phonegap-plugin-contentsync/commit/be31081490c0536514be889724fd0a2bd710bbb5) 26 | * Merge branch 'Cognifide-master' [view commit](http://github.com/phonegap/phonegap-plugin-contentsync/commit/61dcc78d454408471fea04e29e806def5dedfa3e) 27 | * Merge branch 'master' of https://github.com/Cognifide/phonegap-plugin-contentsync into Cognifide-master [view commit](http://github.com/phonegap/phonegap-plugin-contentsync/commit/6ad41d32a89b07e33dd508896e6385b7bce3cd2c) 28 | * Issue #166 new copyRootApp behavior: merge [view commit](http://github.com/phonegap/phonegap-plugin-contentsync/commit/7c8e95b23d3e7d0f2ad17db2831e1a9d3e298bdf) 29 | * Merge pull request #1 from Cognifide/initial-copy-and-download-in-background [view commit](http://github.com/phonegap/phonegap-plugin-contentsync/commit/1049407c91d1d937aca72b82b88df3c3e531a39d) 30 | * Starting download only when src is not nil. Initial copy run in background. [view commit](http://github.com/phonegap/phonegap-plugin-contentsync/commit/323aba5233a85d984afb4375bcf5cd6f709ac2f4) 31 | * Updating CHANGELOG [view commit](http://github.com/phonegap/phonegap-plugin-contentsync/commit/5f68b365194f0350ff7367c5d2e5531f69d1cfa9) 32 | 33 | ## [v1.3.6](https://github.com/* remote origin 34 | 35 | Fetch URL: https://github.com//tree/v1.3.6) (2016-07-09) 36 | [Full Changelog](https://github.com/* remote origin 37 | Fetch URL: https://github.com//compare/v1.3.5...v1.3.6) 38 | 39 | * 1.3.6 [view commit](http://github.com/* remote origin 40 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/a1ef7a60ea31976b36686854a045d437eef0fa3f) 41 | * :bookmark: Bumping plugin version to 1.3.6 [view commit](http://github.com/* remote origin 42 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/b1cc6939d68da56a8d8196097be73826461e2f10) 43 | * add OutputPath to ZipWinProj.csproj (#173) [view commit](http://github.com/* remote origin 44 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/7912d9d840b54200f6649c7adc32f4129ceaa826) 45 | * Updating CHANGELOG [view commit](http://github.com/* remote origin 46 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/4d5fea0ac8b39f5b0eea049a31cd7f07bcf106bd) 47 | 48 | ## [v1.3.5](https://github.com/* remote origin 49 | 50 | Fetch URL: https://github.com//tree/v1.3.5) (2016-07-09) 51 | [Full Changelog](https://github.com/* remote origin 52 | Fetch URL: https://github.com//compare/v1.3.4...v1.3.5) 53 | 54 | * 1.3.5 [view commit](http://github.com/* remote origin 55 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/e90680484b04b77fba3d97f28dabccc57f7c6931) 56 | * :bookmark: Bumping plugin version to 1.3.5 [view commit](http://github.com/* remote origin 57 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/1a63fbfdc167033802aca7148c92960e42f9c05b) 58 | * Merge pull request #172 from phonegap/android-fix [view commit](http://github.com/* remote origin 59 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/752ab470c3c267aa87229671ea67dceb7d9f7d0d) 60 | * fixing compile error [view commit](http://github.com/* remote origin 61 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/7273dbd91bff6a4060bc17a391a49edc8262dd88) 62 | * Updating CHANGELOG [view commit](http://github.com/* remote origin 63 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/d388c5af6bc814ae42e1a2bd9ee99acb6a167384) 64 | 65 | ## [v1.3.4](https://github.com/* remote origin 66 | 67 | Fetch URL: https://github.com//tree/v1.3.4) (2016-07-09) 68 | [Full Changelog](https://github.com/* remote origin 69 | Fetch URL: https://github.com//compare/v1.3.3...v1.3.4) 70 | 71 | * 1.3.4 [view commit](http://github.com/* remote origin 72 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/72a7c0b9f843fc0eb61717270a54a7ae2f540d19) 73 | * :bookmark: Bumping plugin version to 1.3.4 [view commit](http://github.com/* remote origin 74 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/19ec8a224eff24efa5ac3e6eb23cf85365d0afa5) 75 | * Merge pull request #171 from ramboz/issue170 [view commit](http://github.com/* remote origin 76 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/c9c070970f76e998b5fa0178cdb49977650a117c) 77 | * :bug: Fixing potential NullPointerException while removing folders on Android [view commit](http://github.com/* remote origin 78 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/713690a27ad908db768a4ee58ef7151d3eeac73d) 79 | * Merge pull request #165 from brevityco/windows-fix [view commit](http://github.com/* remote origin 80 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/d88a36a932977c5503a5e44b6f2cbb235b180ecf) 81 | * Fix unarchiving memory issues [view commit](http://github.com/* remote origin 82 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/e403fab3c06fb8d9772c61fa63bd0bc7f961ec59) 83 | * Updating CHANGELOG [view commit](http://github.com/* remote origin 84 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/372ef32fb00a518c6c9f8f32faa52f09c6d33065) 85 | 86 | ## [v1.3.3](https://github.com/* remote origin 87 | 88 | Fetch URL: https://github.com//tree/v1.3.3) (2016-07-09) 89 | [Full Changelog](https://github.com/* remote origin 90 | Fetch URL: https://github.com//compare/v1.3.2...v1.3.3) 91 | 92 | * 1.3.3 [view commit](http://github.com/* remote origin 93 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/e13bff12f253bdb6346e422ce6cb44e9a62694a4) 94 | * :bookmark: Bumping plugin version to 1.3.3 [view commit](http://github.com/* remote origin 95 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/08f3dd84c2b17427fd7f424244b0b90262aef128) 96 | * Merge pull request #164 from ptit6sky/master [view commit](http://github.com/* remote origin 97 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/3a6cdae00ee1b7a13105a7643b96fb167ef60cfd) 98 | * Add missing semicolons [view commit](http://github.com/* remote origin 99 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/89ab231ecdc20a6221ba7f39b9828421a55def3c) 100 | * :bug::penguin: Issue #162: Replace of data after connection aborted (HTTP code 400 or 500) [view commit](http://github.com/* remote origin 101 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/fbd833923963ef443e7564a218d9d0d162e8e851) 102 | * Updating CHANGELOG [view commit](http://github.com/* remote origin 103 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/692facab73b520edba33ab5db7762d6e44813fef) 104 | 105 | ## [v1.3.2](https://github.com/* remote origin 106 | 107 | Fetch URL: https://github.com//tree/v1.3.2) (2016-07-09) 108 | [Full Changelog](https://github.com/* remote origin 109 | Fetch URL: https://github.com//compare/v1.3.1...v1.3.2) 110 | 111 | * 1.3.2 [view commit](http://github.com/* remote origin 112 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/c2b55c152926d5769a124cfb495ad2d2a2804d0b) 113 | * :bookmark: Bumping plugin version to 1.3.2 [view commit](http://github.com/* remote origin 114 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/6376cfaf4ce4fbf41c07733e7bbac9966ba593e2) 115 | * :bug::apple: Issue #157 - CopyRootApp issue with app store updates [view commit](http://github.com/* remote origin 116 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/0ec7d4b55fa7eb189cf9ca8082be8c8e860ef16f) 117 | * :bug::penguin: Issue #157 - CopyRootApp issue with app store updates [view commit](http://github.com/* remote origin 118 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/4662912dfe52ac7ff3fb17f656c9f665fd5c0a68) 119 | * validateSrc param: to opt out of the HEAD request to validate the url before downloading [view commit](http://github.com/* remote origin 120 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/6378d6aa529d1798bedc170ca1fa99c721c2fbdc) 121 | * Merge pull request #159 from phonegap/issue156 [view commit](http://github.com/* remote origin 122 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/ea83d0a3a813250bf31fc6090f5a3e42616a7712) 123 | * :art::apple: Issue #156: [iOS] - Requesting an resource with the same id does not replace the resource by default [view commit](http://github.com/* remote origin 124 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/5ccc322753eb06a53d6698f3196ea5b650d345eb) 125 | * :sparkles::apple::penguin: Issue #158: Expose HTTP Response codes [view commit](http://github.com/* remote origin 126 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/25882f39a01d26d7b9ac5c93ee63810d81953363) 127 | * :penguin::bug: Fixing NullPointerException possibility [view commit](http://github.com/* remote origin 128 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/103c0103bc090bbdfbcb694f1fb52b53f025f78b) 129 | * Updating CHANGELOG [view commit](http://github.com/* remote origin 130 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/4797cf8478bbfaaa8ff7a92d73a767781a701889) 131 | 132 | ## [v1.3.1](https://github.com/* remote origin 133 | 134 | Fetch URL: https://github.com//tree/v1.3.1) (2016-07-09) 135 | [Full Changelog](https://github.com/* remote origin 136 | Fetch URL: https://github.com//compare/v1.3.0...v1.3.1) 137 | 138 | * 1.3.1 [view commit](http://github.com/* remote origin 139 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/d695ddc6f9139b3b2579b5a808da179ae77a4411) 140 | * :bookmark: Bumping plugin version to 1.3.1 [view commit](http://github.com/* remote origin 141 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/dd91bd0381a7d9e2f414b54803cda07b919f7822) 142 | * :heavy_plus_sign: adding pluginpub dev dependency [view commit](http://github.com/* remote origin 143 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/b246e1366d913610af9c13b6f650053455d2ff26) 144 | * :bug::apple: Issue #156: [iOS] - Requesting an resource with the same id does not replace the resource by default [view commit](http://github.com/* remote origin 145 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/2c3f236a1da2e8ca2aebf609fddf2ee6d9c0078a) 146 | * Issue #155 adding pluginResult for loadUrl [view commit](http://github.com/* remote origin 147 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/47c5d0e717f8d7af8466e9870cf121ec205211d9) 148 | * Merge pull request #154 from rosatof/master [view commit](http://github.com/* remote origin 149 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/670899da72705441f5c250e38ea1d04e891aeb6c) 150 | * fixing sample app [view commit](http://github.com/* remote origin 151 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/b63680e8f0a10a95bff7a25a0b1dbdc03bd07321) 152 | * Merge pull request #153 from rosatof/master [view commit](http://github.com/* remote origin 153 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/6b9d54a90799e219a7730e9025357636a0ae1e47) 154 | * Avoid crash (NullPointerException) on Android if dir.list() return null [view commit](http://github.com/* remote origin 155 | Fetch URL: https://github.com/phonegap/phonegap-plugin-contentsync/commit/303eef0343b06cb40c3e0f973bb90fd58ba78235) 156 | 157 | # Change Log 158 | -------------------------------------------------------------------------------- /spec/index.spec.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Module dependencies. 3 | */ 4 | 5 | var cordova = require('./helper/cordova'), 6 | contentSync = require('../www'), 7 | execSpy, 8 | execWin, 9 | options; 10 | 11 | /*! 12 | * Specification. 13 | */ 14 | 15 | describe('phonegap-plugin-contentsync', function() { 16 | beforeEach(function() { 17 | options = { src: 'http://path/to/src.zip', id: 'app-1' }; 18 | execWin = jasmine.createSpy(); 19 | execSpy = spyOn(cordova.required, 'cordova/exec').andCallFake(execWin); 20 | }); 21 | 22 | describe('.sync', function() { 23 | it('should require the options parameter', function() { 24 | expect(function() { 25 | options = undefined; 26 | contentSync.sync(options); 27 | }).toThrow(); 28 | expect(execSpy).not.toHaveBeenCalled(); 29 | }); 30 | 31 | it('should require the options.src parameter for merge/replace', function() { 32 | expect(function() { 33 | options.src = undefined; 34 | contentSync.sync(options); 35 | }).toThrow(); 36 | expect(execSpy).not.toHaveBeenCalled(); 37 | }); 38 | 39 | it('should not require the options.src parameter for local', function() { 40 | expect(function() { 41 | options.src = undefined; 42 | options.src = "local"; 43 | contentSync.sync(options); 44 | }).not.toThrow(); 45 | expect(execSpy).not.toHaveBeenCalled(); 46 | }); 47 | 48 | it('should require the options.id parameter', function() { 49 | expect(function() { 50 | options.id = undefined; 51 | contentSync.sync(options); 52 | }).toThrow(); 53 | expect(execSpy).not.toHaveBeenCalled(); 54 | }); 55 | 56 | it('should return an instance of ContentSync', function() { 57 | var sync = contentSync.sync(options); 58 | expect(sync).toEqual(jasmine.any(contentSync.ContentSync)); 59 | }); 60 | }); 61 | 62 | describe('.loadUrl', function() { 63 | it('should raise an error if url is not provided', function() { 64 | expect(function() { 65 | contentSync.loadUrl(null); 66 | }).toThrow(); 67 | }); 68 | 69 | }); 70 | 71 | describe('ContentSync instance', function() { 72 | describe('cordova.exec', function() { 73 | it('should call cordova.exec on next process tick', function(done) { 74 | contentSync.sync(options); 75 | setTimeout(function() { 76 | expect(execSpy).toHaveBeenCalledWith( 77 | jasmine.any(Function), 78 | jasmine.any(Function), 79 | 'Sync', 80 | 'sync', 81 | jasmine.any(Object) 82 | ); 83 | done(); 84 | }, 100); 85 | }); 86 | 87 | describe('options.src', function() { 88 | it('should be passed to exec', function(done) { 89 | execSpy.andCallFake(function(win, fail, service, id, args) { 90 | expect(args[0]).toEqual(options.src); 91 | done(); 92 | }); 93 | contentSync.sync(options); 94 | }); 95 | }); 96 | 97 | describe('options.id', function() { 98 | it('should be passed to exec', function(done) { 99 | options.id = '1234567890'; 100 | execSpy.andCallFake(function(win, fail, service, id, args) { 101 | expect(args[1]).toEqual(options.id); 102 | done(); 103 | }); 104 | contentSync.sync(options); 105 | }); 106 | }); 107 | 108 | describe('options.type', function() { 109 | it('should default to "replace"', function(done) { 110 | execSpy.andCallFake(function(win, fail, service, id, args) { 111 | expect(args[2]).toEqual('replace'); 112 | done(); 113 | }); 114 | contentSync.sync(options); 115 | }); 116 | 117 | it('should be passed as whatever we specify', function(done) { 118 | options.type = 'superduper'; 119 | execSpy.andCallFake(function(win, fail, service, id, args) { 120 | expect(args[2]).toEqual(options.type); 121 | done(); 122 | }); 123 | contentSync.sync(options); 124 | }); 125 | }); 126 | 127 | describe('options.headers', function() { 128 | it('should default to null', function(done) { 129 | execSpy.andCallFake(function(win, fail, service, id, args) { 130 | expect(args[3]).toEqual(null); 131 | done(); 132 | }); 133 | contentSync.sync(options); 134 | }); 135 | 136 | it('should be passed as whatever we specify', function(done) { 137 | options.headers = { 'Authorization': 'SECRET_PASSWORD' }; 138 | execSpy.andCallFake(function(win, fail, service, id, args) { 139 | expect(args[3]).toEqual(options.headers); 140 | done(); 141 | }); 142 | contentSync.sync(options); 143 | }); 144 | }); 145 | 146 | describe('options.copyCordovaAssets', function() { 147 | it('should default to false', function(done) { 148 | execSpy.andCallFake(function(win, fail, service, id, args) { 149 | expect(args[4]).toEqual(false); 150 | done(); 151 | }); 152 | contentSync.sync(options); 153 | }); 154 | it('should be passed as whatever we specify', function(done) { 155 | options.copyCordovaAssets = true; 156 | execSpy.andCallFake(function(win, fail, service, id, args) { 157 | expect(args[4]).toEqual(options.copyCordovaAssets); 158 | done(); 159 | }); 160 | contentSync.sync(options); 161 | }); 162 | }); 163 | 164 | describe('options.copyRootApp', function() { 165 | it('should default to false', function(done) { 166 | execSpy.andCallFake(function(win, fail, service, id, args) { 167 | expect(args[5]).toEqual(false); 168 | done(); 169 | }); 170 | contentSync.sync(options); 171 | }); 172 | it('should be passed as whatever we specify', function(done) { 173 | options.copyRootApp = true; 174 | execSpy.andCallFake(function(win, fail, service, id, args) { 175 | expect(args[5]).toEqual(options.copyRootApp); 176 | done(); 177 | }); 178 | contentSync.sync(options); 179 | }); 180 | }); 181 | describe('options.timeout', function() { 182 | it('should default to 15.0', function(done) { 183 | execSpy.andCallFake(function(win, fail, service, id, args) { 184 | expect(args[6]).toEqual(15.0); 185 | done(); 186 | }); 187 | contentSync.sync(options); 188 | }); 189 | it('should be passed as whatever we specify', function(done) { 190 | options.timeout = 30.0; 191 | execSpy.andCallFake(function(win, fail, service, id, args) { 192 | expect(args[6]).toEqual(options.timeout); 193 | done(); 194 | }); 195 | contentSync.sync(options); 196 | }); 197 | }); 198 | describe('options.trustHost', function() { 199 | it('should default to false', function(done) { 200 | execSpy.andCallFake(function(win, fail, service, id, args) { 201 | expect(args[7]).toEqual(false); 202 | done(); 203 | }); 204 | contentSync.sync(options); 205 | }); 206 | it('should be passed as whatever we specify', function(done) { 207 | options.trustHost = true; 208 | execSpy.andCallFake(function(win, fail, service, id, args) { 209 | expect(args[7]).toEqual(options.trustHost); 210 | done(); 211 | }); 212 | contentSync.sync(options); 213 | }); 214 | }); 215 | describe('options.manifest', function() { 216 | it('should default to the empty string', function(done) { 217 | execSpy.andCallFake(function(win, fail, service, id, args) { 218 | expect(args[8]).toEqual(""); 219 | done(); 220 | }); 221 | contentSync.sync(options); 222 | }); 223 | it('should be passed as whatever we specify', function(done) { 224 | options.manifest = "manifest.json"; 225 | execSpy.andCallFake(function(win, fail, service, id, args) { 226 | expect(args[8]).toEqual(options.manifest); 227 | done(); 228 | }); 229 | contentSync.sync(options); 230 | }); 231 | }); 232 | }); 233 | 234 | describe('on "progress" event', function() { 235 | it('should be emitted with an argument', function(done) { 236 | execSpy.andCallFake(function(win, fail, service, id, args) { 237 | win({ 'progress': 1 }); 238 | }); 239 | var sync = contentSync.sync(options); 240 | sync.on('progress', function(data) { 241 | expect(data.progress).toEqual(1); 242 | done(); 243 | }); 244 | }); 245 | }); 246 | 247 | describe('on "complete" event', function() { 248 | beforeEach(function() { 249 | execSpy.andCallFake(function(win, fail, service, id, args) { 250 | win({ 251 | localPath: 'file:///path/to/content' 252 | }); 253 | }); 254 | }); 255 | 256 | it('should be emitted on success', function(done) { 257 | var sync = contentSync.sync(options); 258 | sync.on('complete', function(data) { 259 | done(); 260 | }); 261 | }); 262 | 263 | it('should provide the data.localPath argument', function(done) { 264 | var sync = contentSync.sync(options); 265 | sync.on('complete', function(data) { 266 | expect(data.localPath).toEqual('file:///path/to/content'); 267 | done(); 268 | }); 269 | }); 270 | }); 271 | 272 | describe('on "error" event', function() { 273 | it('should be emitted with an Error', function(done) { 274 | execSpy.andCallFake(function(win, fail, service, id, args) { 275 | fail('something went wrong'); 276 | }); 277 | var sync = contentSync.sync(options); 278 | sync.on('error', function(e) { 279 | expect(e).toEqual(jasmine.any(Error)); 280 | expect(e.message).toEqual('something went wrong'); 281 | done(); 282 | }); 283 | }); 284 | }); 285 | 286 | describe('.cancel()', function() { 287 | it('should delegate to exec', function(done) { 288 | var sync = contentSync.sync(options); 289 | sync.cancel(); 290 | setTimeout(function() { 291 | expect(execSpy).toHaveBeenCalled(); 292 | expect(execSpy.callCount).toEqual(2); // 1) sync, 2) cancel 293 | expect(execSpy.mostRecentCall.args).toEqual([ 294 | jasmine.any(Function), 295 | jasmine.any(Function), 296 | 'Sync', 297 | 'cancel', 298 | [ options.id ] 299 | ]); 300 | done(); 301 | }, 100); 302 | }); 303 | 304 | it('should emit the "cancel" event', function(done) { 305 | execSpy.andCallFake(function(win, fail, service, id, args) { 306 | win(); 307 | }); 308 | var sync = contentSync.sync(options); 309 | sync.on('cancel', function() { 310 | done(); 311 | }); 312 | sync.cancel(); 313 | }); 314 | }); 315 | }); 316 | 317 | describe('PROGRESS_STATE enumeration', function() { 318 | it('should defined 0 as STOPPED', function() { 319 | expect(contentSync.PROGRESS_STATE[0]).toEqual('STOPPED'); 320 | }); 321 | 322 | it('should defined 1 as DOWNLOADING', function() { 323 | expect(contentSync.PROGRESS_STATE[1]).toEqual('DOWNLOADING'); 324 | }); 325 | 326 | it('should defined 2 as EXTRACTING', function() { 327 | expect(contentSync.PROGRESS_STATE[2]).toEqual('EXTRACTING'); 328 | }); 329 | 330 | it('should defined 3 as COMPLETE', function() { 331 | expect(contentSync.PROGRESS_STATE[3]).toEqual('COMPLETE'); 332 | }); 333 | }); 334 | }); 335 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # phonegap-plugin-contentsync [![Build Status](https://travis-ci.org/phonegap/phonegap-plugin-contentsync.svg?branch=master)](https://travis-ci.org/phonegap/phonegap-plugin-contentsync) [![bitHound Score][bithound-img]][bithound-url] 2 | 3 | > Download and cache remotely hosted zipped content bundles, unzipping automatically. 4 | 5 | ## Installation 6 | 7 | This requires phonegap 5.0+ ( current stable v1.2.0 ) 8 | 9 | ``` 10 | phonegap plugin add phonegap-plugin-contentsync 11 | ``` 12 | 13 | It is also possible to install via repo url directly ( unstable ) 14 | 15 | ``` 16 | phonegap plugin add https://github.com/phonegap/phonegap-plugin-contentsync 17 | ``` 18 | 19 | ## Supported Platforms 20 | 21 | - Android 22 | - iOS 23 | - WP8 24 | 25 | 26 | ## Quick Example 27 | 28 | ```javascript 29 | // Create a new instance of ContentSync pointing to zipped resource 'movie-1.zip' - note 30 | // that the url need not end in zip - it just needs to point to something producing 31 | // a application/octet-stream mime type 32 | var sync = ContentSync.sync({ 33 | src: 'https://myserver/assets/movie-1.zip', 34 | id: 'movie-1' 35 | }); 36 | 37 | sync.on('progress', function(data) { 38 | // data.progress 39 | }); 40 | 41 | sync.on('complete', function(data) { 42 | // data.localPath 43 | }); 44 | 45 | sync.on('error', function(e) { 46 | // e 47 | }); 48 | 49 | sync.on('cancel', function() { 50 | // triggered if event is cancelled 51 | }); 52 | ``` 53 | 54 | #### Security note: 55 | 56 | For updating a production app using `ContentSync.sync`, **always** use HTTPS. [Other Updaters](https://sparkle-project.github.io/documentation/security/#http-mitm-vulnerability) have had vulnerabilities exposed when updating over insecure HTTP. 57 | 58 | ## API 59 | 60 | ### ContentSync.sync(options) 61 | 62 | Parameter | Description 63 | --------- | ------------ 64 | `options.src` | `String` URL to the remotely hosted content. For updates in production, this URL should *always* use HTTPS 65 | `options.id` | `String` Unique identifer to reference the cached content. 66 | `options.type` | `String` _(Optional)_ Defines the copy strategy for the cached content.
The type `replace` is the default behaviour that deletes the old content and caches the new content.
The type `merge` will add the new content to the existing content. This will replace existing files, add new files, but never delete files.
The type `local` returns the full path to the cached content if it exists or downloads it from `options.src` if it doesn't. `options.src` is not required if cached content actually exists. 67 | `options.headers` | `Object` _(Optional)_ Set of headers to use when requesting the remote content from `options.src`. 68 | `options.copyCordovaAssets` | `Boolean` _(Optional)_ Copies `cordova.js`, `cordova_plugins.js` and `plugins/` to sync'd folder. This operation happens after the source content has been cached, so it will override any existing Cordova assets. Default is `false`. 69 | `options.copyRootApp` | `Boolean` _(Optional)_ Copies the `www` folder to sync'd folder. This operation happens before the source content has been cached, then the source content is cached and finally it copies `cordova.js`, `cordova_plugins.js` and `plugins/` to sync'd folder to remain consistent with the installed plugins. Default is `false`. 70 | `options.timeout` | `Double` _(Optional)_ Request timeout. Default is 15 seconds. 71 | `options.trustHost` | `Boolean` _(Optional)_ Trust SSL host. Host defined in `options.src` will be trusted. Ignored if `options.src` is undefined. Not supported on Android. 72 | `options.manifest` | `String` _(Optional)_ If specified the `copyRootApp` functionality will use the list of files contained in the manifest file during it's initial copy. {Android only} 73 | `options.validateSrc` | `Boolean` _(Optional)_ Whether to validate src url with a HEAD request before download (ios only, default true). 74 | 75 | #### Returns 76 | 77 | - Instance of `ContentSync`. 78 | 79 | #### Example 80 | 81 | ```javascript 82 | var sync = ContentSync.sync({ 83 | src: 'https://myserver/app/1', 84 | id: 'app-1' 85 | }); 86 | ``` 87 | 88 | ### sync.on(event, callback) 89 | 90 | Parameter | Description 91 | --------- | ------------ 92 | `event` | `String` Name of the event to listen to. See below for all the event names. 93 | `callback` | `Function` is called when the event is triggered. 94 | 95 | ### sync.on('progress', callback) 96 | 97 | The event `progress` will be triggered on each update as the native platform downloads and caches the content. 98 | 99 | Callback Parameter | Description 100 | ------------------ | ----------- 101 | `data.progress` | `Integer` Progress percentage between `0 - 100`. The progress includes all actions required to cache the remote content locally. This is different on each platform, but often includes requesting, downloading, and extracting the cached content along with any system cleanup tasks. 102 | `data.status` | `Integer` Enumeration of `PROGRESS_STATE` to describe the current progress state. 103 | 104 | #### Example 105 | 106 | ```javascript 107 | sync.on('progress', function(data) { 108 | // data.progress 109 | // data.status 110 | }); 111 | ``` 112 | 113 | ### sync.on('complete', callback) 114 | 115 | The event `complete` will be triggered when the content has been successfully cached onto the device. 116 | 117 | Callback Parameter | Description 118 | ------------------ | ----------- 119 | `data.localPath` | `String` The file path to the cached content. The file path will be different on each platform and may be relative or absolute. However, it is guaraneteed to be a compatible reference in the browser. 120 | `data.cached` | `Boolean` Set to `true` if options.type is set to `local` and cached content exists. Set to `false` otherwise. 121 | 122 | #### Example 123 | 124 | ```javascript 125 | sync.on('complete', function(data) { 126 | // data.localPath 127 | // data.cached 128 | }); 129 | ``` 130 | 131 | ### sync.on('error', callback) 132 | 133 | The event `error` will trigger when an internal error occurs and the cache is aborted. 134 | 135 | Callback Parameter | Description 136 | ------------------ | ----------- 137 | `e.type` | `Integer` Enumeration of `ERROR_STATE` to describe the current error 138 | `e.responseCode` | `Integer` HTTP error code if available, `-1` otherwise 139 | 140 | #### Example 141 | 142 | ```javascript 143 | sync.on('error', function(e) { 144 | // e 145 | }); 146 | ``` 147 | 148 | ### sync.on('cancel', callback) 149 | 150 | The event `cancel` will trigger when `sync.cancel` is called. 151 | 152 | Callback Parameter | Description 153 | ------------------ | ----------- 154 | `no parameters` | 155 | 156 | #### Example 157 | 158 | ```javascript 159 | sync.on('cancel', function() { 160 | // user cancelled the sync operation 161 | }); 162 | ``` 163 | 164 | ### sync.cancel() 165 | 166 | Cancels the content sync operation and triggers the cancel callback. 167 | 168 | ```javascript 169 | var sync = ContentSync.sync({ 170 | src: 'https://myserver/app/1', 171 | id: 'app-1' 172 | }); 173 | 174 | sync.on('cancel', function() { 175 | console.log('content sync was cancelled'); 176 | }); 177 | 178 | sync.cancel(); 179 | ``` 180 | 181 | ### ContentSync.PROGRESS_STATE 182 | 183 | An enumeration that describes the current progress state. The mapped `String` 184 | values can be customized for the user's app. 185 | 186 | Integer | Description 187 | ------- | ----------- 188 | `0` | `STOPPED` 189 | `1` | `DOWNLOADING` 190 | `2` | `EXTRACTING` 191 | `3` | `COMPLETE` 192 | 193 | ### ContentSync.ERROR_STATE 194 | 195 | An enumeration that describes the received error. The mapped `String` 196 | values can be customized for the user's app. 197 | 198 | Error Code | Description 199 | ------------------ | ----------- 200 | `1` | `INVALID_URL_ERR` 201 | `2` | `CONNECTION_ERR` 202 | `3` | `UNZIP_ERR` 203 | 204 | ### ContentSync.unzip || Zip.unzip - ContentSync.download 205 | 206 | If you are using the [Chromium Zip plugin](https://github.com/MobileChromeApps/zip) this plugin won't work for you on iOS. However, it supports the same interface so you don't have to install both. 207 | 208 | ```javascript 209 | 210 | zip.unzip(, , , []); 211 | 212 | ``` 213 | 214 | There is also an extra convenience method that can be used to download an archive 215 | 216 | ```javascript 217 | 218 | ContentSync.download(url, headers, cb) 219 | 220 | ``` 221 | 222 | The progress events described above also apply for these methods. 223 | 224 | #### Example 225 | 226 | ```javascript 227 | ContentSync.PROGRESS_STATE[1] = 'Downloading the media content...'; 228 | ``` 229 | 230 | ### ContentSync.loadUrl (cordova-ios > 4.x with cordova-plugin-wkwebview-engine) 231 | 232 | Use this API to load assets after extraction on **cordova-ios > 4.x** and **cordova-plugin-wkwebview-engine**. Do not use `document.location` as it probably won't work. Make sure to prefix your url with `file://` 233 | 234 | ```javascript 235 | var sync = ContentSync.sync({ 236 | src: 'https://myserver/app/1', 237 | id: 'app-1' 238 | }); 239 | 240 | sync.on('complete', function(data) { 241 | ContentSync.loadUrl('file://' + data.localPath, function() { 242 | console.log('success'); 243 | }); 244 | }); 245 | ``` 246 | 247 | ## Working with the Native File System 248 | 249 | One of the main benefits of the content sync plugin is that it does not depend on the File or FileTransfer plugins. As a result the end user should not care where the ContentSync plugin stores it's files as long as it fills the requirements that it is private and removed when it's associated app is uninstalled. 250 | 251 | However, if you do need to use the File plugin to navigate the data downloaded by ContentSync you can use the following code snippet to get a [DirectoryEntry](https://cordova.apache.org/docs/en/3.0.0/cordova_file_file.md.html#DirectoryEntry) for the synced content. 252 | 253 | ```javascript 254 | var sync = ContentSync.sync({ 255 | src: 'https://myserver/app/1', 256 | id: 'app-1' 257 | }); 258 | 259 | sync.on('complete', function(data) { 260 | window.resolveLocalFileSystemURL("file://" + data.localPath, function(entry) { 261 | // entry is a DirectoryEntry object 262 | }, function(error) { 263 | console.log("Error: " + error.code); 264 | }); 265 | }); 266 | ``` 267 | 268 | As of version 1.2.0 of the plugin the location in which the plugin stores the synched content is equivaltent to the `cordova.file.dataDirectory` path from the `cordova-plugin-file` package. This is a change from previous versions so please be aware you may need to do a full sync after upgrading to version 1.2.0. 269 | 270 | Platform | Path 271 | ------------------ | ----------- 272 | Android | `/data/data//files/` 273 | iOS | `/var/mobile/Applications//Library/NoCloud/` 274 | 275 | ## Copy Root App 276 | 277 | The asset file system is pretty slow on Android so in order to speed up the initial copy of your app to the content sync location you can specify a manifest file on Android. The file must be in the format: 278 | 279 | ```javascript 280 | { 281 | 'files': [ 282 | 'img/logo.png', 283 | 'index.html', 284 | 'js/index.js' 285 | ] 286 | } 287 | ``` 288 | 289 | and if the file is placed in your apps `www` folder you would invoke it via: 290 | 291 | ```javascript 292 | var sync = ContentSync.sync({ 293 | src: 'https://myserver/app/1', 294 | id: 'app-1', 295 | copyRootApp: true, 296 | manifest: 'manifest.json' 297 | }); 298 | ``` 299 | 300 | This results in the `copyRootApp` taking about a third of the time as when a manifest file is not specified. 301 | 302 | ## Persistence of Synced Content 303 | 304 | Content downloaded via this plugin persists between runs of the application or reboots of the phone. The content will only be removed if the application is uninstalled or you use the File API to remove the location of the synched content. 305 | 306 | ## Native Requirements 307 | 308 | - There should be no dependency on the existing File or FileTransfer plugins. 309 | - The native cached file path should be uniquely identifiable with the `id` parameter. This will allow the Content Sync plugin to lookup the file path at a later time using the `id` parameter. 310 | - The first version of the plugin assumes that all cached content is downloaded as a compressed ZIP. The native implementation must properly extract content and clean up any temporary files, such as the downloaded zip. 311 | - The locally compiled Cordova web assets should be copied to the cached content. This includes `cordova.js`, `cordova_plugins.js`, and `plugins/**/*`. 312 | - Multiple syncs should be supported at the same time. 313 | 314 | ## Running Tests ( static tests against source code ) 315 | 316 | ``` 317 | npm test 318 | ``` 319 | 320 | ## Emulator Testing 321 | 322 | The emulator tests use cordova-paramedic and the cordova-plugin-test-framework. 323 | To run them you will need cordova-paramedic installed: 324 | 325 | npm install -g cordova-paramedic 326 | 327 | Some of the tests require a simple HTTP server to host .zip payloads: 328 | 329 | ./tests/scripts/start-server.sh 330 | 331 | Run the tests: 332 | 333 | // From the root of this repo 334 | // test ios : 335 | cordova-paramedic --platform ios --plugin . 336 | 337 | // test android : 338 | cordova-paramedic --platform android --plugin . 339 | 340 | Once complete, the simple HTTP server can be stopped: 341 | 342 | ./tests/scripts/stop-server.sh 343 | 344 | ## Contributing 345 | 346 | ### Editor Config 347 | 348 | The project uses [.editorconfig](http://editorconfig.org/) to define the coding 349 | style of each file. We recommend that you install the Editor Config extension 350 | for your preferred IDE. 351 | 352 | ### JSHint 353 | 354 | The project uses [.jshint](http://jshint.com/docs) to define the JavaScript 355 | coding conventions. Most editors now have a JSHint add-on to provide on-save 356 | or on-edit linting. 357 | 358 | #### Install JSHint for vim 359 | 360 | 1. Install [jshint](https://www.npmjs.com/package/jshint). 361 | 1. Install [jshint.vim](https://github.com/wookiehangover/jshint.vim). 362 | 363 | #### Install JSHint for Sublime 364 | 365 | 1. Install [Package Control](https://packagecontrol.io/installation) 366 | 1. Restart Sublime 367 | 1. Type `CMD+SHIFT+P` 368 | 1. Type _Install Package_ 369 | 1. Type _JSHint Gutter_ 370 | 1. Sublime -> Preferences -> Package Settings -> JSHint Gutter 371 | 1. Set `lint_on_load` and `lint_on_save` to `true` 372 | 373 | [travis-ci-img]: https://travis-ci.org/phonegap/phonegap-plugin-contentsync.svg?branch=master 374 | [travis-ci-url]: http://travis-ci.org/phonegap/phonegap-plugin-contentsync 375 | [bithound-img]: https://www.bithound.io/github/phonegap/phonegap-plugin-contentsync/badges/score.svg 376 | [bithound-url]: https://www.bithound.io/github/phonegap/phonegap-plugin-contentsync 377 | -------------------------------------------------------------------------------- /src/ios/minizip/zip.h: -------------------------------------------------------------------------------- 1 | /* zip.h -- IO on .zip files using zlib 2 | Version 1.1, February 14h, 2010 3 | part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) 4 | 5 | Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) 6 | 7 | Modifications for Zip64 support 8 | Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) 9 | 10 | For more info read MiniZip_info.txt 11 | 12 | --------------------------------------------------------------------------- 13 | 14 | Condition of use and distribution are the same than zlib : 15 | 16 | This software is provided 'as-is', without any express or implied 17 | warranty. In no event will the authors be held liable for any damages 18 | arising from the use of this software. 19 | 20 | Permission is granted to anyone to use this software for any purpose, 21 | including commercial applications, and to alter it and redistribute it 22 | freely, subject to the following restrictions: 23 | 24 | 1. The origin of this software must not be misrepresented; you must not 25 | claim that you wrote the original software. If you use this software 26 | in a product, an acknowledgment in the product documentation would be 27 | appreciated but is not required. 28 | 2. Altered source versions must be plainly marked as such, and must not be 29 | misrepresented as being the original software. 30 | 3. This notice may not be removed or altered from any source distribution. 31 | 32 | --------------------------------------------------------------------------- 33 | 34 | Changes 35 | 36 | See header of zip.h 37 | 38 | */ 39 | 40 | #ifndef _zip12_H 41 | #define _zip12_H 42 | 43 | #ifdef __cplusplus 44 | extern "C" { 45 | #endif 46 | 47 | //#define HAVE_BZIP2 48 | 49 | #ifndef _ZLIB_H 50 | #include "zlib.h" 51 | #endif 52 | 53 | #ifndef _ZLIBIOAPI_H 54 | #include "ioapi.h" 55 | #endif 56 | 57 | #ifdef HAVE_BZIP2 58 | #include "bzlib.h" 59 | #endif 60 | 61 | #define Z_BZIP2ED 12 62 | 63 | #if defined(STRICTZIP) || defined(STRICTZIPUNZIP) 64 | /* like the STRICT of WIN32, we define a pointer that cannot be converted 65 | from (void*) without cast */ 66 | typedef struct TagzipFile__ { int unused; } zipFile__; 67 | typedef zipFile__ *zipFile; 68 | #else 69 | typedef voidp zipFile; 70 | #endif 71 | 72 | #define ZIP_OK (0) 73 | #define ZIP_EOF (0) 74 | #define ZIP_ERRNO (Z_ERRNO) 75 | #define ZIP_PARAMERROR (-102) 76 | #define ZIP_BADZIPFILE (-103) 77 | #define ZIP_INTERNALERROR (-104) 78 | 79 | #ifndef DEF_MEM_LEVEL 80 | # if MAX_MEM_LEVEL >= 8 81 | # define DEF_MEM_LEVEL 8 82 | # else 83 | # define DEF_MEM_LEVEL MAX_MEM_LEVEL 84 | # endif 85 | #endif 86 | /* default memLevel */ 87 | 88 | /* tm_zip contain date/time info */ 89 | typedef struct tm_zip_s 90 | { 91 | uInt tm_sec; /* seconds after the minute - [0,59] */ 92 | uInt tm_min; /* minutes after the hour - [0,59] */ 93 | uInt tm_hour; /* hours since midnight - [0,23] */ 94 | uInt tm_mday; /* day of the month - [1,31] */ 95 | uInt tm_mon; /* months since January - [0,11] */ 96 | uInt tm_year; /* years - [1980..2044] */ 97 | } tm_zip; 98 | 99 | typedef struct 100 | { 101 | tm_zip tmz_date; /* date in understandable format */ 102 | uLong dosDate; /* if dos_date == 0, tmu_date is used */ 103 | /* uLong flag; */ /* general purpose bit flag 2 bytes */ 104 | 105 | uLong internal_fa; /* internal file attributes 2 bytes */ 106 | uLong external_fa; /* external file attributes 4 bytes */ 107 | } zip_fileinfo; 108 | 109 | typedef const char* zipcharpc; 110 | 111 | 112 | #define APPEND_STATUS_CREATE (0) 113 | #define APPEND_STATUS_CREATEAFTER (1) 114 | #define APPEND_STATUS_ADDINZIP (2) 115 | 116 | extern zipFile ZEXPORT zipOpen OF((const char *pathname, int append)); 117 | extern zipFile ZEXPORT zipOpen64 OF((const void *pathname, int append)); 118 | /* 119 | Create a zipfile. 120 | pathname contain on Windows XP a filename like "c:\\zlib\\zlib113.zip" or on 121 | an Unix computer "zlib/zlib113.zip". 122 | if the file pathname exist and append==APPEND_STATUS_CREATEAFTER, the zip 123 | will be created at the end of the file. 124 | (useful if the file contain a self extractor code) 125 | if the file pathname exist and append==APPEND_STATUS_ADDINZIP, we will 126 | add files in existing zip (be sure you don't add file that doesn't exist) 127 | If the zipfile cannot be opened, the return value is NULL. 128 | Else, the return value is a zipFile Handle, usable with other function 129 | of this zip package. 130 | */ 131 | 132 | /* Note : there is no delete function into a zipfile. 133 | If you want delete file into a zipfile, you must open a zipfile, and create another 134 | Of couse, you can use RAW reading and writing to copy the file you did not want delte 135 | */ 136 | 137 | extern zipFile ZEXPORT zipOpen2 OF((const char *pathname, 138 | int append, 139 | zipcharpc* globalcomment, 140 | zlib_filefunc_def* pzlib_filefunc_def)); 141 | 142 | extern zipFile ZEXPORT zipOpen2_64 OF((const void *pathname, 143 | int append, 144 | zipcharpc* globalcomment, 145 | zlib_filefunc64_def* pzlib_filefunc_def)); 146 | 147 | extern int ZEXPORT zipOpenNewFileInZip OF((zipFile file, 148 | const char* filename, 149 | const zip_fileinfo* zipfi, 150 | const void* extrafield_local, 151 | uInt size_extrafield_local, 152 | const void* extrafield_global, 153 | uInt size_extrafield_global, 154 | const char* comment, 155 | int method, 156 | int level)); 157 | 158 | extern int ZEXPORT zipOpenNewFileInZip64 OF((zipFile file, 159 | const char* filename, 160 | const zip_fileinfo* zipfi, 161 | const void* extrafield_local, 162 | uInt size_extrafield_local, 163 | const void* extrafield_global, 164 | uInt size_extrafield_global, 165 | const char* comment, 166 | int method, 167 | int level, 168 | int zip64)); 169 | 170 | /* 171 | Open a file in the ZIP for writing. 172 | filename : the filename in zip (if NULL, '-' without quote will be used 173 | *zipfi contain supplemental information 174 | if extrafield_local!=NULL and size_extrafield_local>0, extrafield_local 175 | contains the extrafield data the the local header 176 | if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global 177 | contains the extrafield data the the local header 178 | if comment != NULL, comment contain the comment string 179 | method contain the compression method (0 for store, Z_DEFLATED for deflate) 180 | level contain the level of compression (can be Z_DEFAULT_COMPRESSION) 181 | zip64 is set to 1 if a zip64 extended information block should be added to the local file header. 182 | this MUST be '1' if the uncompressed size is >= 0xffffffff. 183 | 184 | */ 185 | 186 | 187 | extern int ZEXPORT zipOpenNewFileInZip2 OF((zipFile file, 188 | const char* filename, 189 | const zip_fileinfo* zipfi, 190 | const void* extrafield_local, 191 | uInt size_extrafield_local, 192 | const void* extrafield_global, 193 | uInt size_extrafield_global, 194 | const char* comment, 195 | int method, 196 | int level, 197 | int raw)); 198 | 199 | 200 | extern int ZEXPORT zipOpenNewFileInZip2_64 OF((zipFile file, 201 | const char* filename, 202 | const zip_fileinfo* zipfi, 203 | const void* extrafield_local, 204 | uInt size_extrafield_local, 205 | const void* extrafield_global, 206 | uInt size_extrafield_global, 207 | const char* comment, 208 | int method, 209 | int level, 210 | int raw, 211 | int zip64)); 212 | /* 213 | Same than zipOpenNewFileInZip, except if raw=1, we write raw file 214 | */ 215 | 216 | extern int ZEXPORT zipOpenNewFileInZip3 OF((zipFile file, 217 | const char* filename, 218 | const zip_fileinfo* zipfi, 219 | const void* extrafield_local, 220 | uInt size_extrafield_local, 221 | const void* extrafield_global, 222 | uInt size_extrafield_global, 223 | const char* comment, 224 | int method, 225 | int level, 226 | int raw, 227 | int windowBits, 228 | int memLevel, 229 | int strategy, 230 | const char* password, 231 | uLong crcForCrypting)); 232 | 233 | extern int ZEXPORT zipOpenNewFileInZip3_64 OF((zipFile file, 234 | const char* filename, 235 | const zip_fileinfo* zipfi, 236 | const void* extrafield_local, 237 | uInt size_extrafield_local, 238 | const void* extrafield_global, 239 | uInt size_extrafield_global, 240 | const char* comment, 241 | int method, 242 | int level, 243 | int raw, 244 | int windowBits, 245 | int memLevel, 246 | int strategy, 247 | const char* password, 248 | uLong crcForCrypting, 249 | int zip64 250 | )); 251 | 252 | /* 253 | Same than zipOpenNewFileInZip2, except 254 | windowBits,memLevel,,strategy : see parameter strategy in deflateInit2 255 | password : crypting password (NULL for no crypting) 256 | crcForCrypting : crc of file to compress (needed for crypting) 257 | */ 258 | 259 | extern int ZEXPORT zipOpenNewFileInZip4 OF((zipFile file, 260 | const char* filename, 261 | const zip_fileinfo* zipfi, 262 | const void* extrafield_local, 263 | uInt size_extrafield_local, 264 | const void* extrafield_global, 265 | uInt size_extrafield_global, 266 | const char* comment, 267 | int method, 268 | int level, 269 | int raw, 270 | int windowBits, 271 | int memLevel, 272 | int strategy, 273 | const char* password, 274 | uLong crcForCrypting, 275 | uLong versionMadeBy, 276 | uLong flagBase 277 | )); 278 | 279 | 280 | extern int ZEXPORT zipOpenNewFileInZip4_64 OF((zipFile file, 281 | const char* filename, 282 | const zip_fileinfo* zipfi, 283 | const void* extrafield_local, 284 | uInt size_extrafield_local, 285 | const void* extrafield_global, 286 | uInt size_extrafield_global, 287 | const char* comment, 288 | int method, 289 | int level, 290 | int raw, 291 | int windowBits, 292 | int memLevel, 293 | int strategy, 294 | const char* password, 295 | uLong crcForCrypting, 296 | uLong versionMadeBy, 297 | uLong flagBase, 298 | int zip64 299 | )); 300 | /* 301 | Same than zipOpenNewFileInZip4, except 302 | versionMadeBy : value for Version made by field 303 | flag : value for flag field (compression level info will be added) 304 | */ 305 | 306 | 307 | extern int ZEXPORT zipWriteInFileInZip OF((zipFile file, 308 | const void* buf, 309 | unsigned len)); 310 | /* 311 | Write data in the zipfile 312 | */ 313 | 314 | extern int ZEXPORT zipCloseFileInZip OF((zipFile file)); 315 | /* 316 | Close the current file in the zipfile 317 | */ 318 | 319 | extern int ZEXPORT zipCloseFileInZipRaw OF((zipFile file, 320 | uLong uncompressed_size, 321 | uLong crc32)); 322 | 323 | extern int ZEXPORT zipCloseFileInZipRaw64 OF((zipFile file, 324 | ZPOS64_T uncompressed_size, 325 | uLong crc32)); 326 | 327 | /* 328 | Close the current file in the zipfile, for file opened with 329 | parameter raw=1 in zipOpenNewFileInZip2 330 | uncompressed_size and crc32 are value for the uncompressed size 331 | */ 332 | 333 | extern int ZEXPORT zipClose OF((zipFile file, 334 | const char* global_comment)); 335 | /* 336 | Close the zipfile 337 | */ 338 | 339 | 340 | extern int ZEXPORT zipRemoveExtraInfoBlock OF((char* pData, int* dataLen, short sHeader)); 341 | /* 342 | zipRemoveExtraInfoBlock - Added by Mathias Svensson 343 | 344 | Remove extra information block from a extra information data for the local file header or central directory header 345 | 346 | It is needed to remove ZIP64 extra information blocks when before data is written if using RAW mode. 347 | 348 | 0x0001 is the signature header for the ZIP64 extra information blocks 349 | 350 | usage. 351 | Remove ZIP64 Extra information from a central director extra field data 352 | zipRemoveExtraInfoBlock(pCenDirExtraFieldData, &nCenDirExtraFieldDataLen, 0x0001); 353 | 354 | Remove ZIP64 Extra information from a Local File Header extra field data 355 | zipRemoveExtraInfoBlock(pLocalHeaderExtraFieldData, &nLocalHeaderExtraFieldDataLen, 0x0001); 356 | */ 357 | 358 | #ifdef __cplusplus 359 | } 360 | #endif 361 | 362 | #endif /* _zip64_H */ 363 | -------------------------------------------------------------------------------- /src/ios/minizip/unzip.h: -------------------------------------------------------------------------------- 1 | /* unzip.h -- IO for uncompress .zip files using zlib 2 | Version 1.1, February 14h, 2010 3 | part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) 4 | 5 | Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) 6 | 7 | Modifications of Unzip for Zip64 8 | Copyright (C) 2007-2008 Even Rouault 9 | 10 | Modifications for Zip64 support on both zip and unzip 11 | Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) 12 | 13 | For more info read MiniZip_info.txt 14 | 15 | --------------------------------------------------------------------------------- 16 | 17 | Condition of use and distribution are the same than zlib : 18 | 19 | This software is provided 'as-is', without any express or implied 20 | warranty. In no event will the authors be held liable for any damages 21 | arising from the use of this software. 22 | 23 | Permission is granted to anyone to use this software for any purpose, 24 | including commercial applications, and to alter it and redistribute it 25 | freely, subject to the following restrictions: 26 | 27 | 1. The origin of this software must not be misrepresented; you must not 28 | claim that you wrote the original software. If you use this software 29 | in a product, an acknowledgment in the product documentation would be 30 | appreciated but is not required. 31 | 2. Altered source versions must be plainly marked as such, and must not be 32 | misrepresented as being the original software. 33 | 3. This notice may not be removed or altered from any source distribution. 34 | 35 | --------------------------------------------------------------------------------- 36 | 37 | Changes 38 | 39 | See header of unzip64.c 40 | 41 | */ 42 | 43 | #ifndef _unz64_H 44 | #define _unz64_H 45 | 46 | #ifdef __cplusplus 47 | extern "C" { 48 | #endif 49 | 50 | #ifndef _ZLIB_H 51 | #include "zlib.h" 52 | #endif 53 | 54 | #ifndef _ZLIBIOAPI_H 55 | #include "ioapi.h" 56 | #endif 57 | 58 | #ifdef HAVE_BZIP2 59 | #include "bzlib.h" 60 | #endif 61 | 62 | #define Z_BZIP2ED 12 63 | 64 | #if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) 65 | /* like the STRICT of WIN32, we define a pointer that cannot be converted 66 | from (void*) without cast */ 67 | typedef struct TagunzFile__ { int unused; } unzFile__; 68 | typedef unzFile__ *unzFile; 69 | #else 70 | typedef voidp unzFile; 71 | #endif 72 | 73 | 74 | #define UNZ_OK (0) 75 | #define UNZ_END_OF_LIST_OF_FILE (-100) 76 | #define UNZ_ERRNO (Z_ERRNO) 77 | #define UNZ_EOF (0) 78 | #define UNZ_PARAMERROR (-102) 79 | #define UNZ_BADZIPFILE (-103) 80 | #define UNZ_INTERNALERROR (-104) 81 | #define UNZ_CRCERROR (-105) 82 | 83 | /* tm_unz contain date/time info */ 84 | typedef struct tm_unz_s 85 | { 86 | uInt tm_sec; /* seconds after the minute - [0,59] */ 87 | uInt tm_min; /* minutes after the hour - [0,59] */ 88 | uInt tm_hour; /* hours since midnight - [0,23] */ 89 | uInt tm_mday; /* day of the month - [1,31] */ 90 | uInt tm_mon; /* months since January - [0,11] */ 91 | uInt tm_year; /* years - [1980..2044] */ 92 | } tm_unz; 93 | 94 | /* unz_global_info structure contain global data about the ZIPfile 95 | These data comes from the end of central dir */ 96 | typedef struct unz_global_info64_s 97 | { 98 | ZPOS64_T number_entry; /* total number of entries in 99 | the central dir on this disk */ 100 | uLong size_comment; /* size of the global comment of the zipfile */ 101 | } unz_global_info64; 102 | 103 | typedef struct unz_global_info_s 104 | { 105 | uLong number_entry; /* total number of entries in 106 | the central dir on this disk */ 107 | uLong size_comment; /* size of the global comment of the zipfile */ 108 | } unz_global_info; 109 | 110 | /* unz_file_info contain information about a file in the zipfile */ 111 | typedef struct unz_file_info64_s 112 | { 113 | uLong version; /* version made by 2 bytes */ 114 | uLong version_needed; /* version needed to extract 2 bytes */ 115 | uLong flag; /* general purpose bit flag 2 bytes */ 116 | uLong compression_method; /* compression method 2 bytes */ 117 | uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ 118 | uLong crc; /* crc-32 4 bytes */ 119 | ZPOS64_T compressed_size; /* compressed size 8 bytes */ 120 | ZPOS64_T uncompressed_size; /* uncompressed size 8 bytes */ 121 | uLong size_filename; /* filename length 2 bytes */ 122 | uLong size_file_extra; /* extra field length 2 bytes */ 123 | uLong size_file_comment; /* file comment length 2 bytes */ 124 | 125 | uLong disk_num_start; /* disk number start 2 bytes */ 126 | uLong internal_fa; /* internal file attributes 2 bytes */ 127 | uLong external_fa; /* external file attributes 4 bytes */ 128 | 129 | tm_unz tmu_date; 130 | } unz_file_info64; 131 | 132 | typedef struct unz_file_info_s 133 | { 134 | uLong version; /* version made by 2 bytes */ 135 | uLong version_needed; /* version needed to extract 2 bytes */ 136 | uLong flag; /* general purpose bit flag 2 bytes */ 137 | uLong compression_method; /* compression method 2 bytes */ 138 | uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ 139 | uLong crc; /* crc-32 4 bytes */ 140 | uLong compressed_size; /* compressed size 4 bytes */ 141 | uLong uncompressed_size; /* uncompressed size 4 bytes */ 142 | uLong size_filename; /* filename length 2 bytes */ 143 | uLong size_file_extra; /* extra field length 2 bytes */ 144 | uLong size_file_comment; /* file comment length 2 bytes */ 145 | 146 | uLong disk_num_start; /* disk number start 2 bytes */ 147 | uLong internal_fa; /* internal file attributes 2 bytes */ 148 | uLong external_fa; /* external file attributes 4 bytes */ 149 | 150 | tm_unz tmu_date; 151 | } unz_file_info; 152 | 153 | extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1, 154 | const char* fileName2, 155 | int iCaseSensitivity)); 156 | /* 157 | Compare two filename (fileName1,fileName2). 158 | If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) 159 | If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi 160 | or strcasecmp) 161 | If iCaseSenisivity = 0, case sensitivity is defaut of your operating system 162 | (like 1 on Unix, 2 on Windows) 163 | */ 164 | 165 | 166 | extern unzFile ZEXPORT unzOpen OF((const char *path)); 167 | extern unzFile ZEXPORT unzOpen64 OF((const void *path)); 168 | /* 169 | Open a Zip file. path contain the full pathname (by example, 170 | on a Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer 171 | "zlib/zlib113.zip". 172 | If the zipfile cannot be opened (file don't exist or in not valid), the 173 | return value is NULL. 174 | Else, the return value is a unzFile Handle, usable with other function 175 | of this unzip package. 176 | the "64" function take a const void* pointer, because the path is just the 177 | value passed to the open64_file_func callback. 178 | Under Windows, if UNICODE is defined, using fill_fopen64_filefunc, the path 179 | is a pointer to a wide unicode string (LPCTSTR is LPCWSTR), so const char* 180 | does not describe the reality 181 | */ 182 | 183 | 184 | extern unzFile ZEXPORT unzOpen2 OF((const char *path, 185 | zlib_filefunc_def* pzlib_filefunc_def)); 186 | /* 187 | Open a Zip file, like unzOpen, but provide a set of file low level API 188 | for read/write the zip file (see ioapi.h) 189 | */ 190 | 191 | extern unzFile ZEXPORT unzOpen2_64 OF((const void *path, 192 | zlib_filefunc64_def* pzlib_filefunc_def)); 193 | /* 194 | Open a Zip file, like unz64Open, but provide a set of file low level API 195 | for read/write the zip file (see ioapi.h) 196 | */ 197 | 198 | extern int ZEXPORT unzClose OF((unzFile file)); 199 | /* 200 | Close a ZipFile opened with unzipOpen. 201 | If there is files inside the .Zip opened with unzOpenCurrentFile (see later), 202 | these files MUST be closed with unzipCloseCurrentFile before call unzipClose. 203 | return UNZ_OK if there is no problem. */ 204 | 205 | extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, 206 | unz_global_info *pglobal_info)); 207 | 208 | extern int ZEXPORT unzGetGlobalInfo64 OF((unzFile file, 209 | unz_global_info64 *pglobal_info)); 210 | /* 211 | Write info about the ZipFile in the *pglobal_info structure. 212 | No preparation of the structure is needed 213 | return UNZ_OK if there is no problem. */ 214 | 215 | 216 | extern int ZEXPORT unzGetGlobalComment OF((unzFile file, 217 | char *szComment, 218 | uLong uSizeBuf)); 219 | /* 220 | Get the global comment string of the ZipFile, in the szComment buffer. 221 | uSizeBuf is the size of the szComment buffer. 222 | return the number of byte copied or an error code <0 223 | */ 224 | 225 | 226 | /***************************************************************************/ 227 | /* Unzip package allow you browse the directory of the zipfile */ 228 | 229 | extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); 230 | /* 231 | Set the current file of the zipfile to the first file. 232 | return UNZ_OK if there is no problem 233 | */ 234 | 235 | extern int ZEXPORT unzGoToNextFile OF((unzFile file)); 236 | /* 237 | Set the current file of the zipfile to the next file. 238 | return UNZ_OK if there is no problem 239 | return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. 240 | */ 241 | 242 | extern int ZEXPORT unzLocateFile OF((unzFile file, 243 | const char *szFileName, 244 | int iCaseSensitivity)); 245 | /* 246 | Try locate the file szFileName in the zipfile. 247 | For the iCaseSensitivity signification, see unzStringFileNameCompare 248 | 249 | return value : 250 | UNZ_OK if the file is found. It becomes the current file. 251 | UNZ_END_OF_LIST_OF_FILE if the file is not found 252 | */ 253 | 254 | 255 | /* ****************************************** */ 256 | /* Ryan supplied functions */ 257 | /* unz_file_info contain information about a file in the zipfile */ 258 | typedef struct unz_file_pos_s 259 | { 260 | uLong pos_in_zip_directory; /* offset in zip file directory */ 261 | uLong num_of_file; /* # of file */ 262 | } unz_file_pos; 263 | 264 | extern int ZEXPORT unzGetFilePos( 265 | unzFile file, 266 | unz_file_pos* file_pos); 267 | 268 | extern int ZEXPORT unzGoToFilePos( 269 | unzFile file, 270 | unz_file_pos* file_pos); 271 | 272 | typedef struct unz64_file_pos_s 273 | { 274 | ZPOS64_T pos_in_zip_directory; /* offset in zip file directory */ 275 | ZPOS64_T num_of_file; /* # of file */ 276 | } unz64_file_pos; 277 | 278 | extern int ZEXPORT unzGetFilePos64( 279 | unzFile file, 280 | unz64_file_pos* file_pos); 281 | 282 | extern int ZEXPORT unzGoToFilePos64( 283 | unzFile file, 284 | const unz64_file_pos* file_pos); 285 | 286 | /* ****************************************** */ 287 | 288 | extern int ZEXPORT unzGetCurrentFileInfo64 OF((unzFile file, 289 | unz_file_info64 *pfile_info, 290 | char *szFileName, 291 | uLong fileNameBufferSize, 292 | void *extraField, 293 | uLong extraFieldBufferSize, 294 | char *szComment, 295 | uLong commentBufferSize)); 296 | 297 | extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, 298 | unz_file_info *pfile_info, 299 | char *szFileName, 300 | uLong fileNameBufferSize, 301 | void *extraField, 302 | uLong extraFieldBufferSize, 303 | char *szComment, 304 | uLong commentBufferSize)); 305 | /* 306 | Get Info about the current file 307 | if pfile_info!=NULL, the *pfile_info structure will contain somes info about 308 | the current file 309 | if szFileName!=NULL, the filemane string will be copied in szFileName 310 | (fileNameBufferSize is the size of the buffer) 311 | if extraField!=NULL, the extra field information will be copied in extraField 312 | (extraFieldBufferSize is the size of the buffer). 313 | This is the Central-header version of the extra field 314 | if szComment!=NULL, the comment string of the file will be copied in szComment 315 | (commentBufferSize is the size of the buffer) 316 | */ 317 | 318 | 319 | /** Addition for GDAL : START */ 320 | 321 | extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64 OF((unzFile file)); 322 | 323 | /** Addition for GDAL : END */ 324 | 325 | 326 | /***************************************************************************/ 327 | /* for reading the content of the current zipfile, you can open it, read data 328 | from it, and close it (you can close it before reading all the file) 329 | */ 330 | 331 | extern int ZEXPORT unzOpenCurrentFile OF((unzFile file)); 332 | /* 333 | Open for reading data the current file in the zipfile. 334 | If there is no error, the return value is UNZ_OK. 335 | */ 336 | 337 | extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file, 338 | const char* password)); 339 | /* 340 | Open for reading data the current file in the zipfile. 341 | password is a crypting password 342 | If there is no error, the return value is UNZ_OK. 343 | */ 344 | 345 | extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file, 346 | int* method, 347 | int* level, 348 | int raw)); 349 | /* 350 | Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) 351 | if raw==1 352 | *method will receive method of compression, *level will receive level of 353 | compression 354 | note : you can set level parameter as NULL (if you did not want known level, 355 | but you CANNOT set method parameter as NULL 356 | */ 357 | 358 | extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file, 359 | int* method, 360 | int* level, 361 | int raw, 362 | const char* password)); 363 | /* 364 | Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) 365 | if raw==1 366 | *method will receive method of compression, *level will receive level of 367 | compression 368 | note : you can set level parameter as NULL (if you did not want known level, 369 | but you CANNOT set method parameter as NULL 370 | */ 371 | 372 | 373 | extern int ZEXPORT unzCloseCurrentFile OF((unzFile file)); 374 | /* 375 | Close the file in zip opened with unzOpenCurrentFile 376 | Return UNZ_CRCERROR if all the file was read but the CRC is not good 377 | */ 378 | 379 | extern int ZEXPORT unzReadCurrentFile OF((unzFile file, 380 | voidp buf, 381 | unsigned len)); 382 | /* 383 | Read bytes from the current file (opened by unzOpenCurrentFile) 384 | buf contain buffer where data must be copied 385 | len the size of buf. 386 | 387 | return the number of byte copied if somes bytes are copied 388 | return 0 if the end of file was reached 389 | return <0 with error code if there is an error 390 | (UNZ_ERRNO for IO error, or zLib error for uncompress error) 391 | */ 392 | 393 | extern z_off_t ZEXPORT unztell OF((unzFile file)); 394 | 395 | extern ZPOS64_T ZEXPORT unztell64 OF((unzFile file)); 396 | /* 397 | Give the current position in uncompressed data 398 | */ 399 | 400 | extern int ZEXPORT unzeof OF((unzFile file)); 401 | /* 402 | return 1 if the end of file was reached, 0 elsewhere 403 | */ 404 | 405 | extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, 406 | voidp buf, 407 | unsigned len)); 408 | /* 409 | Read extra field from the current file (opened by unzOpenCurrentFile) 410 | This is the local-header version of the extra field (sometimes, there is 411 | more info in the local-header version than in the central-header) 412 | 413 | if buf==NULL, it return the size of the local extra field 414 | 415 | if buf!=NULL, len is the size of the buffer, the extra header is copied in 416 | buf. 417 | the return value is the number of bytes copied in buf, or (if <0) 418 | the error code 419 | */ 420 | 421 | /***************************************************************************/ 422 | 423 | /* Get the current file offset */ 424 | extern ZPOS64_T ZEXPORT unzGetOffset64 (unzFile file); 425 | extern uLong ZEXPORT unzGetOffset (unzFile file); 426 | 427 | /* Set the current file offset */ 428 | extern int ZEXPORT unzSetOffset64 (unzFile file, ZPOS64_T pos); 429 | extern int ZEXPORT unzSetOffset (unzFile file, uLong pos); 430 | 431 | 432 | 433 | #ifdef __cplusplus 434 | } 435 | #endif 436 | 437 | #endif /* _unz64_H */ 438 | -------------------------------------------------------------------------------- /src/wp8/Unzip.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.IO.IsolatedStorage; 6 | using System.Linq; 7 | using System.Runtime.Serialization; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using System.Windows; 11 | using System.Windows.Resources; 12 | 13 | namespace WPCordovaClassLib.Cordova.Commands 14 | { 15 | public class UnZip : BaseCommand 16 | { 17 | // Sync strategy codes 18 | public const int Replace = 1; 19 | public const int Merge = 2; 20 | 21 | /// 22 | /// Represents a singular progress event to be passed back to javascript 23 | /// 24 | [DataContract] 25 | public class FileUnzipProgress 26 | { 27 | /// 28 | /// amount loaded 29 | /// 30 | [DataMember(Name = "loaded", IsRequired = true)] 31 | public long Loaded { get; set; } 32 | /// 33 | /// Total 34 | /// 35 | [DataMember(Name = "total", IsRequired = false)] 36 | public long Total { get; set; } 37 | 38 | public FileUnzipProgress(long total = 0, long loaded = 0) 39 | { 40 | Loaded = loaded; 41 | Total = total; 42 | } 43 | } 44 | 45 | public void unzip(string srcFilePath, string destPath, int type) 46 | { 47 | using (IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication()) 48 | { 49 | // DEBUG here to copy file from dll to isostore ... 50 | // this is only really needed if you want to test with a file in your package/project 51 | StreamResourceInfo fileResourceStreamInfo = Application.GetResourceStream(new Uri(srcFilePath, UriKind.Relative)); 52 | if (fileResourceStreamInfo != null) 53 | { 54 | using (BinaryReader br = new BinaryReader(fileResourceStreamInfo.Stream)) 55 | { 56 | byte[] data = br.ReadBytes((int)fileResourceStreamInfo.Stream.Length); 57 | // This will truncate/overwrite an existing file, or 58 | using (IsolatedStorageFileStream outFile = appStorage.OpenFile(srcFilePath, FileMode.Create)) 59 | { 60 | using (var writer = new BinaryWriter(outFile)) 61 | { 62 | writer.Write(data); 63 | } 64 | } 65 | } 66 | } 67 | 68 | if(type == Replace) 69 | { 70 | DeleteDirectoryRecursively(appStorage, destPath); 71 | } 72 | 73 | IsolatedStorageFileStream zipStream = null; 74 | ZipArchive zipArch = null; 75 | 76 | try 77 | { 78 | zipStream = new IsolatedStorageFileStream(srcFilePath, FileMode.Open, FileAccess.Read, appStorage); 79 | } 80 | catch (Exception) 81 | { 82 | Debug.WriteLine("File not found :: " + srcFilePath); 83 | return; 84 | } 85 | 86 | if (zipStream != null) 87 | { 88 | zipArch = new ZipArchive(zipStream); 89 | } 90 | 91 | if (zipArch != null) 92 | { 93 | int totalFiles = zipArch.FileNames.Count(); 94 | int current = 0; 95 | try 96 | { 97 | foreach (string filename in zipArch.FileNames) 98 | { 99 | string destFilePath = destPath + "/" + filename; 100 | 101 | string directoryName = getDirectoryName(destFilePath); 102 | 103 | //Debug.WriteLine("upacking file : " + filename + " to : " + destFilePath); 104 | 105 | if (!appStorage.DirectoryExists(directoryName)) 106 | { 107 | appStorage.CreateDirectory(directoryName); 108 | } 109 | 110 | 111 | 112 | using (Stream readStream = zipArch.GetFileStream(filename)) 113 | { 114 | if (readStream != null) 115 | { 116 | using (FileStream outStream = new IsolatedStorageFileStream(destFilePath, FileMode.Create, FileAccess.Write, appStorage)) 117 | { 118 | WriteStreamToPath(readStream, outStream); 119 | FileUnzipProgress progEvt = new FileUnzipProgress(totalFiles, current++); 120 | } 121 | } 122 | } 123 | } 124 | zipStream.Close(); 125 | 126 | } 127 | catch (Exception) 128 | { 129 | Debug.WriteLine("File not found :: " + srcFilePath); 130 | } 131 | } 132 | } 133 | } 134 | 135 | private void WriteStreamToPath(Stream readStream, Stream outStream) 136 | { 137 | 138 | long totalBytes = readStream.Length; 139 | int bytesRead = 0; 140 | 141 | using (BinaryReader reader = new BinaryReader(readStream)) 142 | { 143 | using (BinaryWriter writer = new BinaryWriter(outStream)) 144 | { 145 | int BUFFER_SIZE = 1024; 146 | byte[] buffer; 147 | 148 | while (true) 149 | { 150 | buffer = reader.ReadBytes(BUFFER_SIZE); 151 | bytesRead += buffer.Length; 152 | if (buffer.Length > 0) 153 | { 154 | writer.Write(buffer); 155 | } 156 | else 157 | { 158 | writer.Close(); 159 | reader.Close(); 160 | outStream.Close(); 161 | break; 162 | } 163 | } 164 | } 165 | } 166 | 167 | } 168 | 169 | // Gets the full path without the filename 170 | private string getDirectoryName(String filePath) 171 | { 172 | string directoryName; 173 | try 174 | { 175 | directoryName = filePath.Substring(0, filePath.LastIndexOf('/')); 176 | } 177 | catch 178 | { 179 | directoryName = ""; 180 | } 181 | return directoryName; 182 | } 183 | 184 | // helper function from: http://stackoverflow.com/questions/18422331/easy-way-to-recursively-delete-directories-in-isolatedstorage-on-wp7-8 185 | private void DeleteDirectoryRecursively(IsolatedStorageFile storageFile, String dirName) 186 | { 187 | try 188 | { 189 | String pattern = dirName + @"\*"; 190 | String[] files = storageFile.GetFileNames(pattern); 191 | foreach (var fName in files) 192 | { 193 | storageFile.DeleteFile(Path.Combine(dirName, fName)); 194 | } 195 | String[] dirs = storageFile.GetDirectoryNames(pattern); 196 | foreach (var dName in dirs) 197 | { 198 | DeleteDirectoryRecursively(storageFile, Path.Combine(dirName, dName)); 199 | } 200 | if (storageFile.DirectoryExists(dirName)) 201 | { 202 | storageFile.DeleteDirectory(dirName); 203 | } 204 | } 205 | catch(Exception e) 206 | { 207 | Debug.WriteLine("Unable to delete directory : " + dirName); 208 | } 209 | } 210 | 211 | /// 212 | /// Class used for storing file entry information when 213 | /// parsing the central file directory. 214 | /// 215 | private class ZipArchiveEntry 216 | { 217 | public string Filename; 218 | public int FileStart; 219 | public int CompressedLength; 220 | public int Length; 221 | public int CRC32; 222 | } 223 | 224 | private class ZipArchive : IDisposable 225 | { 226 | private const int CENTRAL_FILE_HDR_SIG = 0x02014b50; 227 | private const int END_CENTRAL_DIR_SIG = 0x06054b50; 228 | 229 | private Stream ZipStream; 230 | private List _fileEntries; 231 | 232 | public ZipArchive(Stream zipFileStream) 233 | { 234 | if (!zipFileStream.CanSeek) 235 | { 236 | throw new NotSupportedException("zipFileStream must support seeking"); 237 | } 238 | else 239 | { 240 | ZipStream = zipFileStream; 241 | } 242 | } 243 | 244 | public List FileEntries 245 | { 246 | get 247 | { 248 | if (_fileEntries == null) 249 | { 250 | InflateDirectory(); 251 | } 252 | return _fileEntries; 253 | } 254 | } 255 | 256 | 257 | /// 258 | /// Gets the file stream for the specified file. Returns null if the file could not be found. 259 | /// 260 | /// The filename. 261 | /// Stream to file inside zip stream 262 | public Stream GetFileStream(string filename) 263 | { 264 | long position = ZipStream.Position; 265 | ZipStream.Seek(0, SeekOrigin.Begin); 266 | Uri fileUri = new Uri(filename, UriKind.Relative); 267 | StreamResourceInfo info = new StreamResourceInfo(ZipStream, null); 268 | StreamResourceInfo stream = System.Windows.Application.GetResourceStream(info, fileUri); 269 | ZipStream.Position = position; 270 | if (stream != null) 271 | { 272 | return stream.Stream; 273 | } 274 | return null; 275 | } 276 | 277 | /// 278 | /// Gets a list of file names embedded in the zip file. 279 | /// 280 | /// The stream for a zip file. 281 | /// List of file names 282 | public IEnumerable FileNames 283 | { 284 | get 285 | { 286 | return (from entry in FileEntries 287 | where (!entry.Filename.EndsWith("/") && 288 | !entry.Filename.StartsWith("__MACOSX/")) 289 | select entry.Filename); 290 | } 291 | } 292 | 293 | /// 294 | /// Gets a list of directories embedded in the zip file 295 | /// 296 | public IEnumerable DirectoriesName 297 | { 298 | get 299 | { 300 | return (from entry in FileEntries 301 | where (entry.Filename.EndsWith("/") 302 | && !entry.Filename.StartsWith("__MACOSX/")) 303 | select entry.Filename); 304 | } 305 | } 306 | 307 | /*************************************************** 308 | 4.3.16 End of central directory record: 309 | end of central dir signature 4 bytes (0x06054b50) 310 | number of this disk 2 bytes 311 | number of the disk with the 312 | start of the central directory 2 bytes 313 | total number of entries in the 314 | central directory on this disk 2 bytes 315 | total number of entries in 316 | the central directory 2 bytes 317 | size of the central directory 4 bytes 318 | offset of start of central 319 | directory with respect to 320 | the starting disk number 4 bytes 321 | .ZIP file comment length 2 bytes 322 | .ZIP file comment (variable size) 323 | */ 324 | 325 | /*************************************************** 326 | File header: 327 | central file header signature 4 bytes (0x02014b50) 328 | version made by 2 bytes 329 | version needed to extract 2 bytes 330 | general purpose bit flag 2 bytes 331 | compression method 2 bytes 332 | last mod file time 2 bytes 333 | last mod file date 2 bytes 334 | crc-32 4 bytes 335 | compressed size 4 bytes 336 | uncompressed size 4 bytes 337 | file name length 2 bytes 338 | extra field length 2 bytes 339 | file comment length 2 bytes 340 | disk number start 2 bytes 341 | internal file attributes 2 bytes 342 | external file attributes 4 bytes 343 | relative offset of local header 4 bytes 344 | file name (variable size) 345 | extra field (variable size) 346 | file comment (variable size) 347 | */ 348 | 349 | 350 | private List InflateDirectory() 351 | { 352 | _fileEntries = new List(); 353 | 354 | BinaryReader reader = new BinaryReader(ZipStream); 355 | 356 | reader.BaseStream.Seek(-4, SeekOrigin.End); 357 | // skip back 358 | while (reader.ReadInt32() != END_CENTRAL_DIR_SIG) 359 | { 360 | reader.BaseStream.Seek(-5, SeekOrigin.Current); 361 | } 362 | // skip over number of this disk, number of the disk with dir start, total number of entries on this disk 363 | reader.BaseStream.Seek(6, SeekOrigin.Current); 364 | short entryCount = reader.ReadInt16(); 365 | int directorySize = reader.ReadInt32(); 366 | int directoryStart = reader.ReadInt32(); 367 | reader.BaseStream.Seek(directoryStart, SeekOrigin.Begin); 368 | bool doRebuild = false; 369 | 370 | for (int i = 0; i < entryCount; i++) 371 | { 372 | if (reader.ReadInt32() == CENTRAL_FILE_HDR_SIG) 373 | { 374 | ZipArchiveEntry zipEntry = new ZipArchiveEntry(); 375 | 376 | reader.BaseStream.Seek(4, SeekOrigin.Current); 377 | short flags = reader.ReadInt16(); // read general purpose bit flag 378 | 379 | if ((flags & 8) > 0) //Silverlight doesn't like this format. We'll "fix it" further below 380 | { 381 | doRebuild = true; 382 | } 383 | // skip: compression method, last mod file time, last mod file date 384 | reader.BaseStream.Seek(6, SeekOrigin.Current); 385 | 386 | zipEntry.CRC32 = reader.ReadInt32(); 387 | zipEntry.CompressedLength = reader.ReadInt32(); 388 | zipEntry.Length = reader.ReadInt32(); 389 | 390 | short fileNameLength = reader.ReadInt16(); 391 | short extraFieldLength = reader.ReadInt16(); 392 | short fileCommentLength = reader.ReadInt16(); 393 | 394 | // skip disk number start, internal file attr, ext file attr 395 | reader.BaseStream.Seek(8, SeekOrigin.Current); 396 | 397 | zipEntry.FileStart = reader.ReadInt32(); 398 | zipEntry.Filename = new string(reader.ReadChars(fileNameLength)); 399 | _fileEntries.Add(zipEntry); 400 | 401 | reader.BaseStream.Seek(extraFieldLength + fileCommentLength, SeekOrigin.Current); 402 | } 403 | } 404 | if (doRebuild) 405 | { 406 | // if file size is reported after the compressed data the filestream is unsupported by silverlight 407 | MemoryStream newZipStream = new MemoryStream(); 408 | BinaryWriter writer = new BinaryWriter(newZipStream); 409 | 410 | RebuildEntries(ref reader, ref writer); 411 | 412 | // rewind 413 | reader.BaseStream.Seek(directoryStart, SeekOrigin.Begin); 414 | //Rebuild directory 415 | RebuildDirectory(ref reader, ref writer); 416 | 417 | writer.Write(reader.ReadBytes((int)(reader.BaseStream.Length - reader.BaseStream.Position))); 418 | ZipStream = newZipStream; //Swap to use our newly cleaned stream 419 | } 420 | return _fileEntries; 421 | } 422 | 423 | private void RebuildDirectory(ref BinaryReader reader, ref BinaryWriter writer) 424 | { 425 | for (int i = 0; i < _fileEntries.Count; i++) 426 | { 427 | writer.Write(reader.ReadBytes(8)); 428 | byte flag = reader.ReadByte(); 429 | writer.Write((byte)(0xF7 & flag)); //set 3rd hobbit to 0 for new format 430 | writer.Write(reader.ReadBytes(19)); 431 | short filenamelength = reader.ReadInt16(); 432 | writer.Write(filenamelength); 433 | short extrafieldlength = reader.ReadInt16(); 434 | writer.Write(extrafieldlength); 435 | short filecommentlength = reader.ReadInt16(); 436 | writer.Write(filecommentlength); 437 | writer.Write(reader.ReadBytes(8)); 438 | writer.Write(_fileEntries[i].FileStart); 439 | reader.BaseStream.Seek(4, SeekOrigin.Current); 440 | writer.Write(reader.ReadBytes(filenamelength + extrafieldlength + filecommentlength)); 441 | } 442 | } 443 | 444 | private void RebuildEntries(ref BinaryReader reader, ref BinaryWriter writer) 445 | { 446 | //Rebuild file entries 447 | foreach (ZipArchiveEntry entry in _fileEntries) 448 | { 449 | ZipArchiveEntry e = entry; 450 | reader.BaseStream.Seek(entry.FileStart, SeekOrigin.Begin); 451 | e.FileStart = (int)writer.BaseStream.Position; 452 | writer.Write(reader.ReadBytes(6)); 453 | 454 | short flag = reader.ReadInt16(); 455 | writer.Write((short)(0xF7 & flag)); //set 3rd hobbit to 0 for new format 456 | writer.Write(reader.ReadBytes(6)); 457 | writer.Write(entry.CRC32); 458 | writer.Write(entry.CompressedLength); 459 | writer.Write(entry.Length); 460 | writer.Write((short)entry.Filename.Length); 461 | reader.BaseStream.Seek(14, SeekOrigin.Current); 462 | short fieldLength = reader.ReadInt16(); 463 | writer.Write(fieldLength); 464 | writer.Write(reader.ReadBytes(entry.Filename.Length + fieldLength + entry.CompressedLength)); 465 | } 466 | } 467 | 468 | #region IDisposable Members 469 | 470 | public void Dispose() 471 | { 472 | if (ZipStream != null) 473 | { 474 | ZipStream.Dispose(); 475 | } 476 | } 477 | 478 | #endregion 479 | } 480 | } 481 | } --------------------------------------------------------------------------------