├── .github └── ISSUE_TEMPLATE.md ├── .gitignore ├── .npmignore ├── .vscode └── settings.json ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── SUPPORT.md ├── bin └── www │ ├── codePush.js │ ├── codePushUtil.js │ ├── fileUtil.js │ ├── httpRequester.js │ ├── installMode.js │ ├── localPackage.js │ ├── nativeAppInfo.js │ ├── package.js │ ├── remotePackage.js │ ├── sdk.js │ └── syncStatus.js ├── gulpfile.js ├── package-lock.json ├── package.json ├── plugin.xml ├── samples ├── .gitignore ├── advanced │ ├── README.md │ ├── config.xml │ └── www │ │ ├── css │ │ └── index.css │ │ ├── img │ │ └── logo.png │ │ ├── index.html │ │ └── js │ │ └── index.js └── basic │ ├── README.md │ ├── config.xml │ └── www │ ├── css │ └── index.css │ ├── img │ └── logo.png │ ├── index.html │ └── js │ └── index.js ├── src ├── android │ ├── CodePush.java │ ├── CodePushException.java │ ├── CodePushPackageManager.java │ ├── CodePushPackageMetadata.java │ ├── CodePushPreferences.java │ ├── CodePushReportingManager.java │ ├── InstallMode.java │ ├── InstallOptions.java │ ├── ReportingStatus.java │ ├── StatusReport.java │ ├── UpdateHashUtils.java │ ├── Utilities.java │ └── build-extras.gradle └── ios │ ├── Base64 │ ├── CodePushMF_Base64Additions.h │ └── CodePushMF_Base64Additions.m │ ├── CDVWKWebViewEngine+CodePush.m │ ├── CodePush.h │ ├── CodePush.m │ ├── CodePushPackageManager.h │ ├── CodePushPackageManager.m │ ├── CodePushPackageMetadata.h │ ├── CodePushPackageMetadata.m │ ├── CodePushReportingManager.h │ ├── CodePushReportingManager.m │ ├── InstallMode.h │ ├── InstallOptions.h │ ├── InstallOptions.m │ ├── JWT │ ├── Core │ │ ├── Algorithms │ │ │ ├── Base │ │ │ │ ├── CodePushJWTAlgorithm.h │ │ │ │ ├── CodePushJWTAlgorithmFactory.h │ │ │ │ ├── CodePushJWTAlgorithmFactory.m │ │ │ │ ├── CodePushJWTAlgorithmNone.h │ │ │ │ └── CodePushJWTAlgorithmNone.m │ │ │ ├── ESFamily │ │ │ │ ├── CodePushJWTAlgorithmESBase.h │ │ │ │ └── CodePushJWTAlgorithmESBase.m │ │ │ ├── HSFamily │ │ │ │ ├── CodePushJWTAlgorithmHSBase.h │ │ │ │ └── CodePushJWTAlgorithmHSBase.m │ │ │ ├── Holders │ │ │ │ ├── CodePushJWTAlgorithmDataHolder.h │ │ │ │ ├── CodePushJWTAlgorithmDataHolder.m │ │ │ │ ├── CodePushJWTAlgorithmDataHolderChain.h │ │ │ │ └── CodePushJWTAlgorithmDataHolderChain.m │ │ │ └── RSFamily │ │ │ │ ├── CodePushJWTAlgorithmRSBase.h │ │ │ │ ├── CodePushJWTAlgorithmRSBase.m │ │ │ │ ├── CodePushJWTRSAlgorithm.h │ │ │ │ └── RSKeys │ │ │ │ ├── CodePushJWTCryptoKey.h │ │ │ │ ├── CodePushJWTCryptoKey.m │ │ │ │ ├── CodePushJWTCryptoKeyExtractor.h │ │ │ │ ├── CodePushJWTCryptoKeyExtractor.m │ │ │ │ ├── CodePushJWTCryptoSecurity.h │ │ │ │ └── CodePushJWTCryptoSecurity.m │ │ ├── ClaimSet │ │ │ ├── CodePushJWTClaim.h │ │ │ ├── CodePushJWTClaim.m │ │ │ ├── CodePushJWTClaimsSet.h │ │ │ ├── CodePushJWTClaimsSet.m │ │ │ ├── CodePushJWTClaimsSetSerializer.h │ │ │ ├── CodePushJWTClaimsSetSerializer.m │ │ │ ├── CodePushJWTClaimsSetVerifier.h │ │ │ └── CodePushJWTClaimsSetVerifier.m │ │ ├── Coding │ │ │ ├── CodePushJWTCoding+ResultTypes.h │ │ │ ├── CodePushJWTCoding+ResultTypes.m │ │ │ ├── CodePushJWTCoding+VersionOne.h │ │ │ ├── CodePushJWTCoding+VersionOne.m │ │ │ ├── CodePushJWTCoding+VersionThree.h │ │ │ ├── CodePushJWTCoding+VersionThree.m │ │ │ ├── CodePushJWTCoding+VersionTwo.h │ │ │ ├── CodePushJWTCoding+VersionTwo.m │ │ │ ├── CodePushJWTCoding.h │ │ │ └── CodePushJWTCoding.m │ │ ├── FrameworkSupplement │ │ │ └── CodePushJWT.h │ │ └── Supplement │ │ │ ├── CodePushJWTBase64Coder.h │ │ │ ├── CodePushJWTBase64Coder.m │ │ │ ├── CodePushJWTDeprecations.h │ │ │ ├── CodePushJWTErrorDescription.h │ │ │ └── CodePushJWTErrorDescription.m │ └── LICENSE │ ├── StatusReport.h │ ├── StatusReport.m │ ├── UpdateHashUtils.h │ ├── UpdateHashUtils.m │ ├── Utilities.h │ └── Utilities.m ├── test ├── platform.ts ├── projectManager.ts ├── serverUtil.ts ├── template │ ├── build.json │ ├── config.xml │ ├── package.json │ └── www │ │ ├── index.html │ │ └── js │ │ ├── scenarioCheckForUpdate.js │ │ ├── scenarioCheckForUpdateCustomKey.js │ │ ├── scenarioDownloadUpdate.js │ │ ├── scenarioInstall.js │ │ ├── scenarioInstallOnRestart2xWithRevert.js │ │ ├── scenarioInstallOnRestartWithRevert.js │ │ ├── scenarioInstallOnResumeWithRevert.js │ │ ├── scenarioInstallWithRevert.js │ │ ├── scenarioRestart.js │ │ ├── scenarioSetup.js │ │ ├── scenarioSync.js │ │ ├── scenarioSync2x.js │ │ ├── scenarioSyncMandatoryDefault.js │ │ ├── scenarioSyncMandatoryRestart.js │ │ ├── scenarioSyncMandatoryResume.js │ │ ├── scenarioSyncRestartDelay.js │ │ ├── scenarioSyncResume.js │ │ ├── scenarioSyncResumeDelay.js │ │ ├── updateDeviceReady.js │ │ ├── updateNARConditional.js │ │ ├── updateNotifyApplicationReady.js │ │ ├── updateSync.js │ │ └── updateSync2x.js ├── test.ts └── testUtil.ts ├── tslint.json ├── typings ├── codePush.d.ts └── replace.d.ts └── www ├── codePush.ts ├── codePushUtil.ts ├── fileUtil.ts ├── httpRequester.ts ├── installMode.ts ├── localPackage.ts ├── nativeAppInfo.ts ├── package.ts ├── remotePackage.ts ├── sdk.ts └── syncStatus.ts /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Thanks so much for filing an issue or feature request! Please fill out the following (wherever relevant): 2 | 3 | ### Description 4 | 5 | [FILL THIS OUT: Explain what you did, what you expected to happen, and what actually happens. Also exact reproduction steps and stack trace will be much appreciated.] 6 | 7 | ### Reproduction 8 | 9 | [FILL THIS OUT: If possible try to reproduce your bug on our basic sample: https://github.com/Microsoft/cordova-plugin-code-push/tree/master/samples/basic. If you can't reproduce the bug on it, provide us as much info as possible about your project.] 10 | 11 | ### Additional Information 12 | 13 | * cordova-plugin-code-push version: 14 | * List of installed plugins: 15 | * Cordova version: 16 | * iOS/Android/Windows version: 17 | * Does this reproduce on a debug build or release build? 18 | * Does this reproduce on a simulator, or only on a physical device? 19 | 20 | (The more info the faster we will be able to address it!) 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | #ignore the files built by npm install 3 | node_modules/* 4 | 5 | #ignore the compiled test files 6 | bin/test/* 7 | 8 | #ignore the test output files 9 | test-* -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .npmignore 2 | .gitignore 3 | gulpfile.js 4 | node_modules/* 5 | samples/* 6 | test/* 7 | www/* 8 | bin/test/* -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib" 3 | } 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## TypeScript 4 | 5 | All the JS code in this plugin is compiled from TypeScript sources. Please do not submit pull requests with direct changes to the JS files in `bin` directory. 6 | Instead, modify the sources in the `www` folder and compile a new version of the plugin. Read on for more details. 7 | 8 | ## Building the plugin 9 | 10 | ### Environment setup 11 | 12 | `node.js` and `npm` are needed for building this project. `npm` comes bundled with the `node.js` installer. You can download the `node.js` installer here: https://nodejs.org/download/. 13 | 14 | Once you have installed `node.js` and `npm`, install the dev dependencies for the project. 15 | 16 | ``` 17 | npm install 18 | ``` 19 | 20 | ### Compile 21 | 22 | Follow these steps to build a new version of the plugin: 23 | - clone this repository 24 | - install the dependencies 25 | 26 | Navigate to the root folder from your command line console and run: 27 | ``` 28 | npm install 29 | ``` 30 | - compile 31 | 32 | From the same root folder location, run: 33 | ``` 34 | gulp 35 | ``` 36 | This will compile the sources and place them in the `bin` folder. Any compilation errors will be displayed in the console. 37 | 38 | ## Test 39 | 40 | ### Environment setup 41 | 42 | First, make sure you can build the plugin by following the steps above. 43 | 44 | Then, make sure you have installed `gulp`. 45 | 46 | ``` 47 | npm install -g gulp 48 | ``` 49 | 50 | To run Android tests, make sure you have `sdk\tools` and `sdk\platform-tools` in your PATH. 51 | 52 | ### Supported platforms 53 | 54 | The plugin has end to end tests for Android and iOS. Depending on your development machine OS, you can run some or all the tests. 55 | 56 | OS | Supported tests 57 | ------------- | ------------- 58 | OS X | Android, iOS 59 | Windows | Android 60 | 61 | ### Test descriptions 62 | 63 | The tests first build the app. 64 | 65 | They then check if the required emulators are currently running. 66 | 67 | If an Android emulator is not running, it attempts to boot an Android emulator named `emulator`. You can specify an emulator by adding `--androidemu yourEmulatorNameHere` as a command line option to the gulp task. 68 | 69 | If an iOS simulator is not running, it attempts to boot the latest iOS iPhone simulator. You can specify a simulator by adding `--iosemu yourSimulatorNameHere` as a command line option to the gulp task. 70 | 71 | If all the required emulators are not running and the tests fail to boot them, the tests will fail. 72 | 73 | If you would like the tests to always restart the necessary emulators (killing them if they are currently running), add a `--clean` flag to the command. 74 | 75 | The desired unit tests are then run. 76 | 77 | If you would like to skip building, add a `-fast` to the end of the command you'd like to run. For example, `gulp test-ios` becomes `gulp test-ios-fast`. 78 | 79 | There is a both a full unit test suite and a "core" set of unit tests that you may run. If you would like to run only the core tests, add a `--core` flag to the command. 80 | 81 | If you would like to pull the plugin from NPM rather than running the tests on the local version, add a `--npm` flag to the command. 82 | 83 | The mocha reporter outputs individual results files for each platform. These are `./test_android.xml`, `./test-ios-ui.xml`, and `./test-ios-wk.xml`. 84 | 85 | #### Default 86 | 87 | To run all of the unit tests on Android and iOS with both UIWebView and WkWebView: 88 | ``` 89 | gulp test 90 | ``` 91 | 92 | #### iOS 93 | 94 | To run all of the unit tests on iOS with both the UIWebView and WkWebView: 95 | ``` 96 | gulp test-ios 97 | ``` 98 | 99 | To run all of the unit tests on iOS with the UIWebView: 100 | ``` 101 | gulp test-ios-ui 102 | ``` 103 | 104 | To run all of the unit tests on iOS with the WkWebView: 105 | ``` 106 | gulp test-ios-wk 107 | ``` 108 | 109 | #### Android 110 | 111 | To run all of the unit tests on Android: 112 | ``` 113 | gulp test-android 114 | ``` 115 | 116 | #### More examples 117 | 118 | All possible testing configurations have tasks! 119 | 120 | The platforms are ordered as follows, and ran in that order: 121 | android, ios-ui, ios-wk 122 | 123 | To run the core unit tests on Android: 124 | ``` 125 | gulp test-android --core 126 | ``` 127 | 128 | To run all of the unit tests on iOS with the UIWebView and pull the plugin from NPM: 129 | ``` 130 | gulp test-ios-ui --npm 131 | ``` 132 | 133 | To run all of the unit tests on Android and iOS with the UIWebView without building first: 134 | ``` 135 | gulp test-android-ios-ui-fast 136 | ``` 137 | 138 | To run all of the unit tests on iOS with the WkWebView and restart the emulators: 139 | ``` 140 | gulp test-ios-wk --clean 141 | ``` 142 | 143 | To run the core unit tests on Android and pull the plugin from NPM: 144 | ``` 145 | gulp test-android --core --npm 146 | ``` 147 | 148 | ...and so on! -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Microsoft CodePush Plugin for Apache Cordova 2 | 3 | Copyright (c) Microsoft Corporation 4 | 5 | All rights reserved. 6 | 7 | MIT License 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software", to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | -------------------------------------------------------------------------------- /SUPPORT.md: -------------------------------------------------------------------------------- 1 | # Support 2 | 3 | ## How to file issues and get help 4 | This project uses GitHub Issues to track bugs and feature requests. Please search the existing issues before filing new issues to avoid duplicates. For new issues, file your bug or feature request as a new issue. 5 | For help and questions about using this project, please consider reaching out to the App Center support team using the support feature in your App Center account if you have one - otherwise please create an issue in this repository. 6 | 7 | ## Microsoft Support Policy 8 | Support for this project is limited to the resources listed above. 9 | 10 | -------------------------------------------------------------------------------- /bin/www/codePushUtil.js: -------------------------------------------------------------------------------- 1 | 2 | /******************************************************************************************** 3 | THIS FILE HAS BEEN COMPILED FROM TYPESCRIPT SOURCES. 4 | PLEASE DO NOT MODIFY THIS FILE DIRECTLY AS YOU WILL LOSE YOUR CHANGES WHEN RECOMPILING. 5 | INSTEAD, EDIT THE TYPESCRIPT SOURCES UNDER THE WWW FOLDER, AND THEN RUN GULP. 6 | FOR MORE INFORMATION, PLEASE SEE CONTRIBUTING.md. 7 | *********************************************************************************************/ 8 | 9 | 10 | "use strict"; 11 | var CodePushUtil = (function () { 12 | function CodePushUtil() { 13 | } 14 | CodePushUtil.copyUnassignedMembers = function (fromParameter, toParameter) { 15 | for (var key in fromParameter) { 16 | if (toParameter[key] === undefined || toParameter[key] === null) { 17 | toParameter[key] = fromParameter[key]; 18 | } 19 | } 20 | }; 21 | CodePushUtil.getNodeStyleCallbackFor = function (successCallback, errorCallback) { 22 | return function (error, result) { 23 | if (error) { 24 | errorCallback && errorCallback(error); 25 | } 26 | else { 27 | successCallback && successCallback(result); 28 | } 29 | }; 30 | }; 31 | CodePushUtil.getErrorMessage = function (e) { 32 | return e && e.message || e && e.toString() || ""; 33 | }; 34 | CodePushUtil.logMessage = function (msg) { 35 | console.log(CodePushUtil.TAG + " " + msg); 36 | }; 37 | CodePushUtil.logError = function (message, error) { 38 | var errorMessage = (message || "") + " " + CodePushUtil.getErrorMessage(error); 39 | var stackTrace = error && error.stack ? ". StackTrace: " + error.stack : ""; 40 | console.error(CodePushUtil.TAG + " " + errorMessage + stackTrace); 41 | }; 42 | CodePushUtil.TAG = "[CodePush]"; 43 | CodePushUtil.invokeErrorCallback = function (error, errorCallback) { 44 | CodePushUtil.logError(null, error); 45 | errorCallback && errorCallback(error); 46 | }; 47 | return CodePushUtil; 48 | }()); 49 | module.exports = CodePushUtil; 50 | -------------------------------------------------------------------------------- /bin/www/httpRequester.js: -------------------------------------------------------------------------------- 1 | 2 | /******************************************************************************************** 3 | THIS FILE HAS BEEN COMPILED FROM TYPESCRIPT SOURCES. 4 | PLEASE DO NOT MODIFY THIS FILE DIRECTLY AS YOU WILL LOSE YOUR CHANGES WHEN RECOMPILING. 5 | INSTEAD, EDIT THE TYPESCRIPT SOURCES UNDER THE WWW FOLDER, AND THEN RUN GULP. 6 | FOR MORE INFORMATION, PLEASE SEE CONTRIBUTING.md. 7 | *********************************************************************************************/ 8 | 9 | 10 | "use strict"; 11 | var __extends = (this && this.__extends) || (function () { 12 | var extendStatics = function (d, b) { 13 | extendStatics = Object.setPrototypeOf || 14 | ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || 15 | function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; 16 | return extendStatics(d, b); 17 | }; 18 | return function (d, b) { 19 | extendStatics(d, b); 20 | function __() { this.constructor = d; } 21 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 22 | }; 23 | })(); 24 | var CodePushUtil = require("./codePushUtil"); 25 | var HttpRequester = (function () { 26 | function HttpRequester(contentType) { 27 | cordova.plugin.http.setHeader("X-CodePush-Plugin-Name", "cordova-plugin-code-push"); 28 | cordova.plugin.http.setHeader("X-CodePush-Plugin-Version", cordova.require("cordova/plugin_list").metadata["cordova-plugin-code-push"]); 29 | cordova.plugin.http.setHeader("X-CodePush-SDK-Version", cordova.require("cordova/plugin_list").metadata["code-push"]); 30 | if (contentType) { 31 | cordova.plugin.http.setHeader("Content-Type", contentType); 32 | } 33 | } 34 | HttpRequester.prototype.request = function (verb, url, callbackOrRequestBody, callback) { 35 | var requestCallback = callback; 36 | var options = HttpRequester.getInitialOptionsForVerb(verb); 37 | if (options instanceof Error) { 38 | CodePushUtil.logError("Could not make the HTTP request", options); 39 | requestCallback && requestCallback(options, undefined); 40 | return; 41 | } 42 | if (!requestCallback && typeof callbackOrRequestBody === "function") { 43 | requestCallback = callbackOrRequestBody; 44 | } 45 | if (typeof callbackOrRequestBody === "string") { 46 | options.serializer = "utf8"; 47 | options.data = callbackOrRequestBody; 48 | } 49 | options.responseType = "text"; 50 | cordova.plugin.http.sendRequest(url, options, function (success) { 51 | requestCallback && requestCallback(null, { 52 | body: success.data, 53 | statusCode: success.status, 54 | }); 55 | }, function (failure) { 56 | requestCallback && requestCallback(new Error(failure.error), null); 57 | }); 58 | }; 59 | HttpRequester.getInitialOptionsForVerb = function (verb) { 60 | switch (verb) { 61 | case 0: 62 | return { method: "get" }; 63 | case 4: 64 | return { method: "delete" }; 65 | case 1: 66 | return { method: "head" }; 67 | case 8: 68 | return { method: "patch" }; 69 | case 2: 70 | return { method: "post" }; 71 | case 3: 72 | return { method: "put" }; 73 | case 5: 74 | case 6: 75 | case 7: 76 | default: 77 | return new ((function (_super) { 78 | __extends(UnsupportedMethodError, _super); 79 | function UnsupportedMethodError() { 80 | return _super !== null && _super.apply(this, arguments) || this; 81 | } 82 | return UnsupportedMethodError; 83 | }(Error)))("Unsupported HTTP method code [" + verb + "]"); 84 | } 85 | }; 86 | return HttpRequester; 87 | }()); 88 | module.exports = HttpRequester; 89 | -------------------------------------------------------------------------------- /bin/www/installMode.js: -------------------------------------------------------------------------------- 1 | 2 | /******************************************************************************************** 3 | THIS FILE HAS BEEN COMPILED FROM TYPESCRIPT SOURCES. 4 | PLEASE DO NOT MODIFY THIS FILE DIRECTLY AS YOU WILL LOSE YOUR CHANGES WHEN RECOMPILING. 5 | INSTEAD, EDIT THE TYPESCRIPT SOURCES UNDER THE WWW FOLDER, AND THEN RUN GULP. 6 | FOR MORE INFORMATION, PLEASE SEE CONTRIBUTING.md. 7 | *********************************************************************************************/ 8 | 9 | 10 | "use strict"; 11 | var InstallMode; 12 | (function (InstallMode) { 13 | InstallMode[InstallMode["IMMEDIATE"] = 0] = "IMMEDIATE"; 14 | InstallMode[InstallMode["ON_NEXT_RESTART"] = 1] = "ON_NEXT_RESTART"; 15 | InstallMode[InstallMode["ON_NEXT_RESUME"] = 2] = "ON_NEXT_RESUME"; 16 | })(InstallMode || (InstallMode = {})); 17 | module.exports = InstallMode; 18 | -------------------------------------------------------------------------------- /bin/www/nativeAppInfo.js: -------------------------------------------------------------------------------- 1 | 2 | /******************************************************************************************** 3 | THIS FILE HAS BEEN COMPILED FROM TYPESCRIPT SOURCES. 4 | PLEASE DO NOT MODIFY THIS FILE DIRECTLY AS YOU WILL LOSE YOUR CHANGES WHEN RECOMPILING. 5 | INSTEAD, EDIT THE TYPESCRIPT SOURCES UNDER THE WWW FOLDER, AND THEN RUN GULP. 6 | FOR MORE INFORMATION, PLEASE SEE CONTRIBUTING.md. 7 | *********************************************************************************************/ 8 | 9 | 10 | "use strict"; 11 | var DefaultServerUrl = "https://codepush.appcenter.ms/"; 12 | var NativeAppInfo = (function () { 13 | function NativeAppInfo() { 14 | } 15 | NativeAppInfo.getApplicationBuildTime = function (callback) { 16 | var timestampSuccess = function (timestamp) { callback(null, timestamp); }; 17 | var timestampError = function () { callback(new Error("Could not get application timestamp."), null); }; 18 | cordova.exec(timestampSuccess, timestampError, "CodePush", "getNativeBuildTime", []); 19 | }; 20 | NativeAppInfo.getApplicationVersion = function (callback) { 21 | var versionSuccess = function (version) { callback(null, version); }; 22 | var versionError = function () { callback(new Error("Could not get application version."), null); }; 23 | cordova.exec(versionSuccess, versionError, "CodePush", "getAppVersion", []); 24 | }; 25 | NativeAppInfo.getBinaryHash = function (callback) { 26 | var binaryHashSuccess = function (binaryHash) { callback(null, binaryHash); }; 27 | var binaryHashError = function () { callback(new Error("Could not get binary hash."), null); }; 28 | cordova.exec(binaryHashSuccess, binaryHashError, "CodePush", "getBinaryHash", []); 29 | }; 30 | NativeAppInfo.getServerURL = function (serverCallback) { 31 | var serverSuccess = function (serverURL) { serverCallback(null, serverURL); }; 32 | var serverError = function () { serverCallback(null, DefaultServerUrl); }; 33 | cordova.exec(serverSuccess, serverError, "CodePush", "getServerURL", []); 34 | }; 35 | NativeAppInfo.getDeploymentKey = function (deploymentKeyCallback) { 36 | var deploymentSuccess = function (deploymentKey) { deploymentKeyCallback(null, deploymentKey); }; 37 | var deploymentError = function () { deploymentKeyCallback(new Error("Deployment key not found."), null); }; 38 | cordova.exec(deploymentSuccess, deploymentError, "CodePush", "getDeploymentKey", []); 39 | }; 40 | NativeAppInfo.isFailedUpdate = function (packageHash, checkCallback) { 41 | var win = function (failed) { 42 | checkCallback && checkCallback(!!failed); 43 | }; 44 | var fail = function (e) { 45 | win(0); 46 | }; 47 | cordova.exec(win, fail, "CodePush", "isFailedUpdate", [packageHash]); 48 | }; 49 | NativeAppInfo.isFirstRun = function (packageHash, firstRunCallback) { 50 | var win = function (firstRun) { 51 | firstRunCallback(!!firstRun); 52 | }; 53 | var fail = function () { 54 | firstRunCallback(false); 55 | }; 56 | cordova.exec(win, fail, "CodePush", "isFirstRun", [packageHash]); 57 | }; 58 | NativeAppInfo.isPendingUpdate = function (callback) { 59 | var win = function (firstRun) { 60 | callback(!!firstRun); 61 | }; 62 | var fail = function () { 63 | callback(false); 64 | }; 65 | cordova.exec(win, fail, "CodePush", "isPendingUpdate", []); 66 | }; 67 | return NativeAppInfo; 68 | }()); 69 | module.exports = NativeAppInfo; 70 | -------------------------------------------------------------------------------- /bin/www/package.js: -------------------------------------------------------------------------------- 1 | 2 | /******************************************************************************************** 3 | THIS FILE HAS BEEN COMPILED FROM TYPESCRIPT SOURCES. 4 | PLEASE DO NOT MODIFY THIS FILE DIRECTLY AS YOU WILL LOSE YOUR CHANGES WHEN RECOMPILING. 5 | INSTEAD, EDIT THE TYPESCRIPT SOURCES UNDER THE WWW FOLDER, AND THEN RUN GULP. 6 | FOR MORE INFORMATION, PLEASE SEE CONTRIBUTING.md. 7 | *********************************************************************************************/ 8 | 9 | 10 | "use strict"; 11 | var Package = (function () { 12 | function Package() { 13 | } 14 | return Package; 15 | }()); 16 | module.exports = Package; 17 | -------------------------------------------------------------------------------- /bin/www/syncStatus.js: -------------------------------------------------------------------------------- 1 | 2 | /******************************************************************************************** 3 | THIS FILE HAS BEEN COMPILED FROM TYPESCRIPT SOURCES. 4 | PLEASE DO NOT MODIFY THIS FILE DIRECTLY AS YOU WILL LOSE YOUR CHANGES WHEN RECOMPILING. 5 | INSTEAD, EDIT THE TYPESCRIPT SOURCES UNDER THE WWW FOLDER, AND THEN RUN GULP. 6 | FOR MORE INFORMATION, PLEASE SEE CONTRIBUTING.md. 7 | *********************************************************************************************/ 8 | 9 | 10 | "use strict"; 11 | var SyncStatus; 12 | (function (SyncStatus) { 13 | SyncStatus[SyncStatus["UP_TO_DATE"] = 0] = "UP_TO_DATE"; 14 | SyncStatus[SyncStatus["UPDATE_INSTALLED"] = 1] = "UPDATE_INSTALLED"; 15 | SyncStatus[SyncStatus["UPDATE_IGNORED"] = 2] = "UPDATE_IGNORED"; 16 | SyncStatus[SyncStatus["ERROR"] = 3] = "ERROR"; 17 | SyncStatus[SyncStatus["IN_PROGRESS"] = 4] = "IN_PROGRESS"; 18 | SyncStatus[SyncStatus["CHECKING_FOR_UPDATE"] = 5] = "CHECKING_FOR_UPDATE"; 19 | SyncStatus[SyncStatus["AWAITING_USER_ACTION"] = 6] = "AWAITING_USER_ACTION"; 20 | SyncStatus[SyncStatus["DOWNLOADING_PACKAGE"] = 7] = "DOWNLOADING_PACKAGE"; 21 | SyncStatus[SyncStatus["INSTALLING_UPDATE"] = 8] = "INSTALLING_UPDATE"; 22 | })(SyncStatus || (SyncStatus = {})); 23 | module.exports = SyncStatus; 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cordova-plugin-code-push", 3 | "version": "2.0.0", 4 | "description": "CodePush Plugin for Apache Cordova", 5 | "cordova": { 6 | "id": "cordova-plugin-code-push", 7 | "platforms": [ 8 | "android", 9 | "ios" 10 | ] 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/Microsoft/cordova-plugin-code-push" 15 | }, 16 | "keywords": [ 17 | "cordova", 18 | "code", 19 | "push", 20 | "ecosystem:cordova", 21 | "cordova-android", 22 | "cordova-ios" 23 | ], 24 | "author": "Microsoft Corporation", 25 | "license": "MIT", 26 | "types": "./typings/codePush.d.ts", 27 | "devDependencies": { 28 | "@types/assert": "^1.4.2", 29 | "@types/cordova": "0.0.34", 30 | "@types/cordova-plugin-device": "^1.1.5", 31 | "@types/cordova-plugin-dialogs": "^1.3.2", 32 | "@types/cordova-plugin-file": "^0.0.3", 33 | "@types/mkdirp": "^0.5.2", 34 | "@types/mocha": "^5.2.7", 35 | "@types/node": "^12.6.3", 36 | "@types/power-assert": "^1.5.0", 37 | "@types/q": "^1.5.2", 38 | "archiver": "^3.0.0", 39 | "body-parser": "^1.19.0", 40 | "del": "^5.0.0", 41 | "express": "^4.17.1", 42 | "gulp": "^4.0.2", 43 | "gulp-insert": "^0.5.0", 44 | "gulp-tslint": "^8.1.4", 45 | "gulp-typescript": "^5.0.1", 46 | "mkdirp": "^0.5.1", 47 | "mocha": "^6.1.4", 48 | "mocha-junit-reporter": "^1.23.1", 49 | "q": "^1.5.1", 50 | "replace": "^1.2.0", 51 | "run-sequence": "^2.2.1", 52 | "tslint": "^5.18.0", 53 | "typescript": "^3.5.3" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /samples/.gitignore: -------------------------------------------------------------------------------- 1 | */platforms 2 | */plugins 3 | -------------------------------------------------------------------------------- /samples/advanced/README.md: -------------------------------------------------------------------------------- 1 | # Cordova CodePush Sample App - Advanced 2 | 3 | This is a sample application demonstrating a more advanced way you could integrate CodePush in your Cordova application. All the CodePush specific code is found in [index.js](/samples/advanced/www/js/index.js). The CodePush configuration is found in [config.xml](/samples/advanced/config.xml). 4 | 5 | When the application loads, on the `deviceready` event, we poll the CodePush server for an update. If an update is available, we prompt the user to install it. If the user approves it, the update is installed and the application is reloaded. 6 | 7 | For more information on how to get started see our [Getting Started](/README.md#getting-started) section. 8 | -------------------------------------------------------------------------------- /samples/advanced/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | CodePushAdvanced 4 | 5 | A sample Apache Cordova application that uses the CodePush service. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /samples/advanced/www/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 | -------------------------------------------------------------------------------- /samples/advanced/www/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/cordova-plugin-code-push/05958131e9c50630cc8591c74d7d0c34d5ffa3dd/samples/advanced/www/img/logo.png -------------------------------------------------------------------------------- /samples/advanced/www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 20 | 21 | 22 | 32 | 33 | 34 | 35 | 36 | 37 | Hello World 38 | 39 | 40 |
41 |

Hello, CodePush (Version 1)

42 | 46 |
47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /samples/basic/README.md: -------------------------------------------------------------------------------- 1 | # Cordova CodePush Sample App - Basic 2 | 3 | This is a sample application demonstrating the CodePush sync operation. This operation offers a very easy way of integrating CodePush in your application, by invoking only one function. All the CodePush specific code is found in [index.js](/samples/basic/www/js/index.js). The CodePush configuration is found in [config.xml](/samples/basic/config.xml). 4 | 5 | When the application loads, on the `deviceready` event, we invoke sync. This checks for an update, and if one is available, the user will be prompted to install it. Once the user accepts it, the update is installed and the application reloaded. See [SyncOptions](/README.md#syncoptions) in our documentation for customizing the sync behavior. 6 | 7 | For more information on how to get started see our [Getting Started](/README.md#getting-started) section. 8 | -------------------------------------------------------------------------------- /samples/basic/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | CodePushBasic 4 | 5 | A sample Apache Cordova application that uses the CodePush service. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /samples/basic/www/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 | -------------------------------------------------------------------------------- /samples/basic/www/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/cordova-plugin-code-push/05958131e9c50630cc8591c74d7d0c34d5ffa3dd/samples/basic/www/img/logo.png -------------------------------------------------------------------------------- /samples/basic/www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 20 | 21 | 22 | 32 | 33 | 34 | 35 | 36 | 37 | Hello World 38 | 39 | 40 |
41 |

Hello, CodePush (Version 1)

42 | 46 |
47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /samples/basic/www/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 | 37 | /* Invoke sync with the custom options, which enables user interaction. 38 | For customizing the sync behavior, see SyncOptions in the CodePush documentation. */ 39 | window.codePush.sync( 40 | function (syncStatus) { 41 | switch (syncStatus) { 42 | // Result (final) statuses 43 | case SyncStatus.UPDATE_INSTALLED: 44 | app.displayMessage("The update was installed successfully. For InstallMode.ON_NEXT_RESTART, the changes will be visible after application restart. "); 45 | break; 46 | case SyncStatus.UP_TO_DATE: 47 | app.displayMessage("The application is up to date."); 48 | break; 49 | case SyncStatus.UPDATE_IGNORED: 50 | app.displayMessage("The user decided not to install the optional update."); 51 | break; 52 | case SyncStatus.ERROR: 53 | app.displayMessage("An error occured while checking for updates"); 54 | break; 55 | 56 | // Intermediate (non final) statuses 57 | case SyncStatus.CHECKING_FOR_UPDATE: 58 | console.log("Checking for update."); 59 | break; 60 | case SyncStatus.AWAITING_USER_ACTION: 61 | console.log("Alerting user."); 62 | break; 63 | case SyncStatus.DOWNLOADING_PACKAGE: 64 | console.log("Downloading package."); 65 | break; 66 | case SyncStatus.INSTALLING_UPDATE: 67 | console.log("Installing update"); 68 | break; 69 | } 70 | }, 71 | { 72 | installMode: InstallMode.ON_NEXT_RESTART, updateDialog: true 73 | }, 74 | function (downloadProgress) { 75 | console.log("Downloading " + downloadProgress.receivedBytes + " of " + downloadProgress.totalBytes + " bytes."); 76 | }); 77 | 78 | // continue application initialization 79 | app.receivedEvent('deviceready'); 80 | }, 81 | // Update DOM on a Received Event 82 | receivedEvent: function (id) { 83 | var parentElement = document.getElementById(id); 84 | var listeningElement = parentElement.querySelector('.listening'); 85 | var receivedElement = parentElement.querySelector('.received'); 86 | 87 | listeningElement.setAttribute('style', 'display:none;'); 88 | receivedElement.setAttribute('style', 'display:block;'); 89 | 90 | console.log('Received Event: ' + id); 91 | }, 92 | // Displays an alert dialog containing a message. 93 | displayMessage: function (message) { 94 | navigator.notification.alert( 95 | message, 96 | null, 97 | 'CodePush', 98 | 'OK'); 99 | } 100 | }; 101 | 102 | app.initialize(); -------------------------------------------------------------------------------- /src/android/CodePushException.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.cordova; 2 | 3 | public class CodePushException extends Exception { 4 | public CodePushException() { 5 | } 6 | 7 | public CodePushException(String message) { 8 | super(message); 9 | } 10 | 11 | public CodePushException(String message, Throwable cause) { 12 | super(message, cause); 13 | } 14 | 15 | public CodePushException(Throwable cause) { 16 | super(cause); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/android/CodePushPackageManager.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.cordova; 2 | 3 | import android.content.Context; 4 | 5 | import org.json.JSONException; 6 | 7 | import java.io.File; 8 | import java.io.IOException; 9 | 10 | /** 11 | * Handles update package management. 12 | */ 13 | public class CodePushPackageManager { 14 | 15 | public static final String CODEPUSH_OLD_PACKAGE_PATH = "/codepush/oldPackage.json"; 16 | public static final String CODEPUSH_CURRENT_PACKAGE_PATH = "/codepush/currentPackage.json"; 17 | 18 | private Context context; 19 | private CodePushPreferences codePushPreferences; 20 | 21 | public CodePushPackageManager(Context context, CodePushPreferences codePushPreferences) { 22 | this.context = context; 23 | this.codePushPreferences = codePushPreferences; 24 | } 25 | 26 | public void revertToPreviousVersion() { 27 | /* delete the failed update package */ 28 | CodePushPackageMetadata failedUpdateMetadata = this.getCurrentPackageMetadata(); 29 | if (failedUpdateMetadata != null) { 30 | if (failedUpdateMetadata.packageHash != null) { 31 | this.codePushPreferences.saveFailedUpdate(failedUpdateMetadata.packageHash); 32 | } 33 | File failedUpdateDir = new File(this.context.getFilesDir() + failedUpdateMetadata.localPath); 34 | if (failedUpdateDir.exists()) { 35 | Utilities.deleteEntryRecursively(failedUpdateDir); 36 | } 37 | } 38 | 39 | /* replace the current file with the old one */ 40 | File currentFile = new File(this.context.getFilesDir() + CodePushPackageManager.CODEPUSH_CURRENT_PACKAGE_PATH); 41 | File oldFile = new File(this.context.getFilesDir() + CodePushPackageManager.CODEPUSH_OLD_PACKAGE_PATH); 42 | 43 | if (currentFile.exists()) { 44 | currentFile.delete(); 45 | } 46 | 47 | if (oldFile.exists()) { 48 | oldFile.renameTo(currentFile); 49 | } 50 | } 51 | 52 | public void cleanDeployments() { 53 | File file = new File(this.context.getFilesDir() + "/codepush"); 54 | if (file.exists()) { 55 | Utilities.deleteEntryRecursively(file); 56 | } 57 | } 58 | 59 | public void cleanOldPackage() throws IOException, JSONException { 60 | CodePushPackageMetadata oldPackageMetadata = this.getOldPackageMetadata(); 61 | if (oldPackageMetadata != null) { 62 | File file = new File(this.context.getFilesDir() + oldPackageMetadata.localPath); 63 | if (file.exists()) { 64 | Utilities.deleteEntryRecursively(file); 65 | } 66 | } 67 | } 68 | 69 | public CodePushPackageMetadata getOldPackageMetadata() { 70 | String currentPackageFilePath = this.context.getFilesDir() + CODEPUSH_OLD_PACKAGE_PATH; 71 | return CodePushPackageMetadata.getPackageMetadata(currentPackageFilePath); 72 | } 73 | 74 | public CodePushPackageMetadata getCurrentPackageMetadata() { 75 | String currentPackageFilePath = this.context.getFilesDir() + CODEPUSH_CURRENT_PACKAGE_PATH; 76 | return CodePushPackageMetadata.getPackageMetadata(currentPackageFilePath); 77 | } 78 | 79 | public String getCachedBinaryHash() { 80 | return this.codePushPreferences.getCachedBinaryHash(); 81 | } 82 | 83 | public void saveBinaryHash(String binaryHash) { 84 | this.codePushPreferences.saveBinaryHash(binaryHash); 85 | } 86 | 87 | public boolean isFailedUpdate(String packageHash) { 88 | return this.codePushPreferences.isFailedUpdate(packageHash); 89 | } 90 | 91 | public void clearFailedUpdates() { 92 | this.codePushPreferences.clearFailedUpdates(); 93 | } 94 | 95 | public void savePendingInstall(InstallOptions options) { 96 | this.codePushPreferences.savePendingInstall(options); 97 | } 98 | 99 | public InstallOptions getPendingInstall() { 100 | return this.codePushPreferences.getPendingInstall(); 101 | } 102 | 103 | public void clearPendingInstall() { 104 | this.codePushPreferences.clearPendingInstall(); 105 | } 106 | 107 | public void markInstallNeedsConfirmation() { 108 | this.codePushPreferences.markInstallNeedsConfirmation(); 109 | } 110 | 111 | public void clearInstallNeedsConfirmation() { 112 | this.codePushPreferences.clearInstallNeedsConfirmation(); 113 | } 114 | 115 | public boolean installNeedsConfirmation() { 116 | return this.codePushPreferences.installNeedsConfirmation(); 117 | } 118 | 119 | public boolean isBinaryFirstRun() { 120 | return this.codePushPreferences.isBinaryFirstRun(); 121 | } 122 | 123 | public void clearBinaryFirstRunFlag() { 124 | this.codePushPreferences.clearBinaryFirstRunFlag(); 125 | } 126 | 127 | public void saveBinaryFirstRunFlag() { 128 | this.codePushPreferences.saveBinaryFirstRunFlag(); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/android/CodePushPackageMetadata.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.cordova; 2 | 3 | import org.json.JSONObject; 4 | 5 | import java.io.File; 6 | 7 | /** 8 | * Model class for the CodePush metadata stored alongside a package deployment. 9 | */ 10 | public class CodePushPackageMetadata { 11 | public String deploymentKey; 12 | public String packageDescription; 13 | public String label; 14 | public String appVersion; 15 | public boolean isMandatory; 16 | public String packageHash; 17 | public long packageSize; 18 | public String nativeBuildTime; 19 | public String localPath; 20 | 21 | final static class JsonField { 22 | public static final String DeploymentKey = "deploymentKey"; 23 | public static final String Description = "description"; 24 | public static final String Label = "label"; 25 | public static final String AppVersion = "appVersion"; 26 | public static final String IsMandatory = "isMandatory"; 27 | public static final String PackageHash = "packageHash"; 28 | public static final String PackageSize = "packageSize"; 29 | public static final String NativeBuildTime = "nativeBuildTime"; 30 | public static final String LocalPath = "localPath"; 31 | } 32 | 33 | public static CodePushPackageMetadata getPackageMetadata(String filePath) { 34 | CodePushPackageMetadata result = null; 35 | 36 | try { 37 | File file = new File(filePath); 38 | if (file.exists()) { 39 | String content = Utilities.readFileContents(file); 40 | result = new CodePushPackageMetadata(); 41 | JSONObject jsonObject = new JSONObject(content); 42 | 43 | if (jsonObject.has(CodePushPackageMetadata.JsonField.DeploymentKey)) { 44 | result.deploymentKey = jsonObject.getString(CodePushPackageMetadata.JsonField.DeploymentKey); 45 | } 46 | 47 | if (jsonObject.has(CodePushPackageMetadata.JsonField.Description)) { 48 | result.packageDescription = jsonObject.getString(CodePushPackageMetadata.JsonField.Description); 49 | } 50 | 51 | if (jsonObject.has(CodePushPackageMetadata.JsonField.Label)) { 52 | result.label = jsonObject.getString(CodePushPackageMetadata.JsonField.Label); 53 | } 54 | 55 | if (jsonObject.has(CodePushPackageMetadata.JsonField.AppVersion)) { 56 | result.appVersion = jsonObject.getString(CodePushPackageMetadata.JsonField.AppVersion); 57 | } 58 | 59 | if (jsonObject.has(CodePushPackageMetadata.JsonField.IsMandatory)) { 60 | result.isMandatory = jsonObject.getBoolean(CodePushPackageMetadata.JsonField.IsMandatory); 61 | } 62 | 63 | if (jsonObject.has(CodePushPackageMetadata.JsonField.PackageHash)) { 64 | result.packageHash = jsonObject.getString(CodePushPackageMetadata.JsonField.PackageHash); 65 | } 66 | 67 | if (jsonObject.has(CodePushPackageMetadata.JsonField.PackageSize)) { 68 | result.packageSize = jsonObject.getLong(CodePushPackageMetadata.JsonField.PackageSize); 69 | } 70 | 71 | if (jsonObject.has(CodePushPackageMetadata.JsonField.NativeBuildTime)) { 72 | result.nativeBuildTime = jsonObject.getString(CodePushPackageMetadata.JsonField.NativeBuildTime); 73 | } 74 | 75 | if (jsonObject.has(CodePushPackageMetadata.JsonField.LocalPath)) { 76 | result.localPath = jsonObject.getString(CodePushPackageMetadata.JsonField.LocalPath); 77 | } 78 | } 79 | } catch (Exception e) { 80 | Utilities.logException(e); 81 | } 82 | 83 | return result; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/android/CodePushReportingManager.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.cordova; 2 | 3 | import org.apache.cordova.CordovaWebView; 4 | 5 | import java.util.Locale; 6 | import android.app.Activity; 7 | 8 | /** 9 | * Handles the native -> JS reporting mechanism. 10 | */ 11 | public class CodePushReportingManager { 12 | 13 | private Activity cordovaActivity; 14 | private CodePushPreferences codePushPreferences; 15 | private Boolean hasFailedReport = null; 16 | 17 | public CodePushReportingManager(Activity cordovaActivity, CodePushPreferences codePushPreferences) { 18 | this.cordovaActivity = cordovaActivity; 19 | this.codePushPreferences = codePushPreferences; 20 | } 21 | 22 | /** 23 | * Invokes the window.codePush.reportStatus JS function for the given webView. 24 | */ 25 | public void reportStatus(StatusReport statusReport, final CordovaWebView webView) { 26 | /* JS function to call: window.codePush.reportStatus(status: number, label: String, appVersion: String, currentDeploymentKey: String, previousLabelOrAppVersion?: string, previousDeploymentKey?: string) */ 27 | if (statusReport.deploymentKey == null || statusReport.deploymentKey.isEmpty()) { 28 | return; 29 | } 30 | 31 | final String script = String.format( 32 | Locale.US, 33 | "javascript:document.addEventListener(\"deviceready\", function () { window.codePush.reportStatus(%d, %s, %s, %s, %s, %s); });", 34 | statusReport.status.getValue(), 35 | convertStringParameter(statusReport.label), 36 | convertStringParameter(statusReport.appVersion), 37 | convertStringParameter(statusReport.deploymentKey), 38 | statusReport.lastVersionLabelOrAppVersion == null ? convertStringParameter(codePushPreferences.getLastVersionLabelOrAppVersion()) : convertStringParameter(statusReport.lastVersionLabelOrAppVersion), 39 | statusReport.lastVersionDeploymentKey == null ? convertStringParameter(codePushPreferences.getLastVersionDeploymentKey()) : convertStringParameter(statusReport.lastVersionDeploymentKey) 40 | ); 41 | 42 | cordovaActivity.runOnUiThread(new Runnable() { 43 | public void run() { 44 | webView.loadUrl(script); 45 | } 46 | }); 47 | } 48 | 49 | public boolean hasFailedReport() { 50 | if (hasFailedReport == null) { 51 | hasFailedReport = codePushPreferences.getFailedReport() != null; 52 | } 53 | 54 | return hasFailedReport; 55 | } 56 | 57 | public StatusReport getAndClearFailedReport() { 58 | StatusReport failedReport = codePushPreferences.getFailedReport(); 59 | codePushPreferences.clearFailedReport(); 60 | this.hasFailedReport = false; 61 | return failedReport; 62 | } 63 | 64 | public void saveFailedReport(StatusReport statusReport) { 65 | codePushPreferences.saveFailedReport(statusReport); 66 | this.hasFailedReport = true; 67 | } 68 | 69 | public void saveSuccessfulReport(StatusReport statusReport) { 70 | if (statusReport.status == ReportingStatus.STORE_VERSION || statusReport.status == ReportingStatus.UPDATE_CONFIRMED) { 71 | codePushPreferences.saveLastVersion(statusReport.label == null ? statusReport.appVersion : statusReport.label, statusReport.deploymentKey); 72 | codePushPreferences.clearFailedReport(); 73 | this.hasFailedReport = false; 74 | } 75 | } 76 | 77 | private String convertStringParameter(String input) { 78 | if (null == input) { 79 | return "undefined"; 80 | } else { 81 | return "'" + input + "'"; 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/android/InstallMode.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.cordova; 2 | 3 | /** 4 | * Defines the available InstallModes. 5 | */ 6 | public enum InstallMode { 7 | IMMEDIATE(0), 8 | ON_NEXT_RESTART(1), 9 | ON_NEXT_RESUME(2); 10 | 11 | private int value; 12 | 13 | InstallMode(int i) { 14 | this.value = i; 15 | } 16 | 17 | /** 18 | * Returns the InstallMode associated with a given value. 19 | * If no InstallMode is associated with the provided value, null is returned. 20 | */ 21 | public static InstallMode fromValue(int i) { 22 | for (InstallMode mode : InstallMode.values()) { 23 | if (i == mode.value) { 24 | return mode; 25 | } 26 | } 27 | 28 | return null; 29 | } 30 | 31 | /** 32 | * Returns the value associated with this enum. 33 | */ 34 | public int getValue() { 35 | return this.value; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/android/InstallOptions.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.cordova; 2 | 3 | /** 4 | * Defines the update installation options. 5 | */ 6 | public class InstallOptions { 7 | public InstallMode installMode; 8 | public int minimumBackgroundDuration; 9 | 10 | public InstallOptions(InstallMode installMode, int minimumBackgroundDuration) { 11 | this.installMode = installMode; 12 | this.minimumBackgroundDuration = minimumBackgroundDuration; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/android/ReportingStatus.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.cordova; 2 | 3 | /** 4 | * Defines application statuses we use in reporting events from the native to the JS layer. 5 | */ 6 | public enum ReportingStatus { 7 | STORE_VERSION(0), 8 | UPDATE_CONFIRMED(1), 9 | UPDATE_ROLLED_BACK(2); 10 | 11 | private int value; 12 | 13 | ReportingStatus(int i) { 14 | this.value = i; 15 | } 16 | 17 | /** 18 | * Returns the value associated with this enum. 19 | */ 20 | public int getValue() { 21 | return this.value; 22 | } 23 | } -------------------------------------------------------------------------------- /src/android/StatusReport.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.cordova; 2 | 3 | import org.json.JSONException; 4 | import org.json.JSONObject; 5 | 6 | /** 7 | * Represents a status report to be sent to metrics. 8 | */ 9 | public class StatusReport { 10 | 11 | private static final String STATUS_KEY = "status"; 12 | private static final String LABEL_KEY = "label"; 13 | private static final String APP_VERSION_KEY = "appVersion"; 14 | private static final String DEPLOYMENT_KEY_KEY = "deploymentKey"; 15 | private static final String LAST_VERSION_LABEL_OR_APP_VERSION_KEY = "lastVersionLabelOrAppVersion"; 16 | private static final String LAST_VERSION_DEPLOYMENT_KEY_KEY = "lastVersionDeploymentKey"; 17 | 18 | ReportingStatus status; 19 | String label; 20 | String appVersion; 21 | String deploymentKey; 22 | 23 | // Optional fields. 24 | String lastVersionLabelOrAppVersion; 25 | String lastVersionDeploymentKey; 26 | 27 | public StatusReport(int status, String label, String appVersion, String deploymentKey, String lastVersionLabelOrAppVersion, String lastVersionDeploymentKey) { 28 | this(ReportingStatus.values()[status], label, appVersion, deploymentKey, lastVersionLabelOrAppVersion, lastVersionDeploymentKey); 29 | } 30 | 31 | public StatusReport(ReportingStatus status, String label, String appVersion, String deploymentKey) { 32 | this(status, label, appVersion, deploymentKey, null, null); 33 | } 34 | 35 | public StatusReport(ReportingStatus status, String label, String appVersion, String deploymentKey, String lastVersionLabelOrAppVersion, String lastVersionDeploymentKey) { 36 | this.status = status; 37 | this.label = label; 38 | this.appVersion = appVersion; 39 | this.deploymentKey = deploymentKey; 40 | this.lastVersionLabelOrAppVersion = lastVersionLabelOrAppVersion; 41 | this.lastVersionDeploymentKey = lastVersionDeploymentKey; 42 | } 43 | 44 | public String serialize() { 45 | try { 46 | JSONObject jsonObject = new JSONObject(); 47 | jsonObject.put(STATUS_KEY, status.getValue()); 48 | jsonObject.put(LABEL_KEY, label); 49 | jsonObject.put(APP_VERSION_KEY, appVersion); 50 | if (deploymentKey != null) { 51 | jsonObject.put(DEPLOYMENT_KEY_KEY, deploymentKey); 52 | } 53 | 54 | if (lastVersionLabelOrAppVersion != null) { 55 | jsonObject.put(LAST_VERSION_LABEL_OR_APP_VERSION_KEY, lastVersionLabelOrAppVersion); 56 | } 57 | 58 | if (lastVersionDeploymentKey != null) { 59 | jsonObject.put(LAST_VERSION_DEPLOYMENT_KEY_KEY, lastVersionDeploymentKey); 60 | } 61 | 62 | return jsonObject.toString(); 63 | } catch (JSONException e) { 64 | // Should not happen 65 | e.printStackTrace(); 66 | return null; 67 | } 68 | } 69 | 70 | public static StatusReport deserialize(JSONObject jsonObject) throws JSONException { 71 | return new StatusReport( 72 | jsonObject.optInt(STATUS_KEY), 73 | jsonObject.optString(LABEL_KEY, null), 74 | jsonObject.optString(APP_VERSION_KEY, null), 75 | jsonObject.optString(DEPLOYMENT_KEY_KEY, null), 76 | jsonObject.optString(LAST_VERSION_LABEL_OR_APP_VERSION_KEY, null), 77 | jsonObject.optString(LAST_VERSION_DEPLOYMENT_KEY_KEY, null) 78 | ); 79 | } 80 | 81 | public static StatusReport deserialize(String jsonString) throws JSONException { 82 | JSONObject jsonObject = new JSONObject(jsonString); 83 | return deserialize(jsonObject); 84 | } 85 | } -------------------------------------------------------------------------------- /src/android/build-extras.gradle: -------------------------------------------------------------------------------- 1 | // Needed to speed up asset listing: refer to https://github.com/apache/cordova-plugin-file/blob/76c8fd5e43598ea660c2db64eb08e2659b45a575/src/android/AssetFilesystem.java#L41 2 | // Taken from https://raw.githubusercontent.com/apache/cordova-plugin-file/ed4cb4b999373eaa80b2fe1f38e3fc24db1b00ae/src/android/build-extras.gradle 3 | 4 | /* 5 | Licensed to the Apache Software Foundation (ASF) under one 6 | or more contributor license agreements. See the NOTICE file 7 | distributed with this work for additional information 8 | regarding copyright ownership. The ASF licenses this file 9 | to you under the Apache License, Version 2.0 (the 10 | "License"); you may not use this file except in compliance 11 | with the License. You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, 16 | software distributed under the License is distributed on an 17 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 | KIND, either express or implied. See the License for the 19 | specific language governing permissions and limitations 20 | under the License. 21 | */ 22 | ext.postBuildExtras = { 23 | 24 | def inAssetsDir = file("src/main/assets") 25 | if (!inAssetsDir.exists()) { // Add support for cordova-android < 7.0.0 26 | inAssetsDir = file("assets") 27 | } 28 | 29 | def outAssetsDir = inAssetsDir 30 | def outFile = new File(outAssetsDir, "cdvasset.manifest") 31 | 32 | def newTask = task("cdvCreateAssetManifest") { 33 | doLast { 34 | def contents = new HashMap() 35 | def sizes = new HashMap() 36 | contents[""] = inAssetsDir.list() 37 | def tree = fileTree(dir: inAssetsDir) 38 | tree.visit { fileDetails -> 39 | if (fileDetails.isDirectory()) { 40 | contents[fileDetails.relativePath.toString()] = fileDetails.file.list() 41 | } else { 42 | sizes[fileDetails.relativePath.toString()] = fileDetails.file.length() 43 | } 44 | } 45 | 46 | outAssetsDir.mkdirs() 47 | outFile.withObjectOutputStream { oos -> 48 | oos.writeObject(contents) 49 | oos.writeObject(sizes) 50 | } 51 | } 52 | } 53 | newTask.inputs.dir inAssetsDir 54 | newTask.outputs.file outFile 55 | def preBuildTask = tasks["preBuild"] 56 | preBuildTask.dependsOn(newTask) 57 | } 58 | 59 | android.buildTypes.each { 60 | // to prevent incorrect long value restoration from strings.xml we need to wrap it with double quotes 61 | // https://github.com/Microsoft/cordova-plugin-code-push/issues/264 62 | it.resValue "string", "CODE_PUSH_APK_BUILD_TIME", String.format("\"%d\"", System.currentTimeMillis()) 63 | } 64 | 65 | dependencies { 66 | compile('com.nimbusds:nimbus-jose-jwt:5.1') { exclude group: "net.minidev", module: "json-smart" } 67 | } 68 | -------------------------------------------------------------------------------- /src/ios/Base64/CodePushMF_Base64Additions.h: -------------------------------------------------------------------------------- 1 | // 2 | // MF_Base64Additions.h 3 | // Base64 -- RFC 4648 compatible implementation 4 | // see http://www.ietf.org/rfc/rfc4648.txt for more details 5 | // 6 | // Designed to be compiled with Automatic Reference Counting 7 | // 8 | // Created by Dave Poirier on 2012-06-14. 9 | // Public Domain 10 | // Hosted at https://github.com/ekscrypto/Base64 11 | // 12 | 13 | #import 14 | 15 | @interface NSString (Base64Addition) 16 | +(NSString *)stringFromBase64String:(NSString *)base64String; 17 | +(NSString *)stringFromBase64UrlEncodedString:(NSString *)base64UrlEncodedString; 18 | -(NSString *)base64String; 19 | -(NSString *)base64UrlEncodedString; 20 | @end 21 | 22 | @interface NSData (Base64Addition) 23 | +(NSData *)dataWithBase64String:(NSString *)base64String; 24 | +(NSData *)dataWithBase64UrlEncodedString:(NSString *)base64UrlEncodedString; 25 | -(NSString *)base64String; 26 | -(NSString *)base64UrlEncodedString; 27 | @end 28 | 29 | @interface MF_Base64Codec : NSObject 30 | +(NSData *)dataFromBase64String:(NSString *)base64String; 31 | +(NSString *)base64StringFromData:(NSData *)data; 32 | +(NSString *)base64UrlEncodedStringFromBase64String:(NSString *)base64String; 33 | +(NSString *)base64StringFromBase64UrlEncodedString:(NSString *)base64UrlEncodedString; 34 | @end 35 | -------------------------------------------------------------------------------- /src/ios/CodePush.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface CodePush : CDVPlugin 4 | 5 | - (void)getServerURL:(CDVInvokedUrlCommand*)command; 6 | - (void)getDeploymentKey:(CDVInvokedUrlCommand*)command; 7 | - (void)getNativeBuildTime:(CDVInvokedUrlCommand*)command; 8 | - (void)getAppVersion:(CDVInvokedUrlCommand*)command; 9 | - (void)install:(CDVInvokedUrlCommand *)command; 10 | - (void)preInstall:(CDVInvokedUrlCommand *)command; 11 | - (void)isFailedUpdate:(CDVInvokedUrlCommand *)command; 12 | - (void)isFirstRun:(CDVInvokedUrlCommand *)command; 13 | - (void)isPendingUpdate:(CDVInvokedUrlCommand *)command; 14 | - (void)restartApplication:(CDVInvokedUrlCommand *)command; 15 | - (void)getBinaryHash:(CDVInvokedUrlCommand *)command; 16 | - (void)getPackageHash:(CDVInvokedUrlCommand *)command; 17 | - (void)decodeSignature:(CDVInvokedUrlCommand *)command; 18 | - (void)getPublicKey:(CDVInvokedUrlCommand *)command; 19 | - (void)pluginInitialize; 20 | + (Boolean)hasIonicWebViewEngine:(id) webViewEngine; 21 | + (void) setServerBasePath:(NSString*)serverPath webView:(id) webViewEngine; 22 | + (NSString*) getCurrentServerBasePath; 23 | 24 | void CPLog(NSString *formatString, ...); 25 | @end 26 | -------------------------------------------------------------------------------- /src/ios/CodePushPackageManager.h: -------------------------------------------------------------------------------- 1 | #import "CodePushPackageMetadata.h" 2 | #import "InstallOptions.h" 3 | 4 | @interface CodePushPackageManager : NSObject 5 | 6 | + (void)cleanOldPackage; 7 | + (void)revertToPreviousVersion; 8 | + (CodePushPackageMetadata*)getCurrentPackageMetadata; 9 | + (void)clearFailedUpdates; 10 | + (void)cleanDeployments; 11 | + (NSString*)getCachedBinaryHash; 12 | + (void)saveBinaryHash:(NSString*)binaryHash; 13 | + (BOOL)isFailedHash:(NSString*)packageHash; 14 | + (void)savePendingInstall:(InstallOptions *)installOptions; 15 | + (InstallOptions*)getPendingInstall; 16 | + (void)clearPendingInstall; 17 | + (void)markInstallNeedsConfirmation; 18 | + (BOOL)installNeedsConfirmation; 19 | + (void)clearInstallNeedsConfirmation; 20 | + (void)clearBinaryFirstRunFlag; 21 | + (void)markBinaryFirstRunFlag; 22 | + (BOOL)isBinaryFirstRun; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /src/ios/CodePushPackageMetadata.h: -------------------------------------------------------------------------------- 1 | @interface CodePushPackageMetadata : NSObject 2 | 3 | @property NSString* deploymentKey; 4 | @property NSString* packageDescription; 5 | @property NSString* label; 6 | @property NSString* appVersion; 7 | @property bool isMandatory; 8 | @property NSString* packageHash; 9 | @property NSNumber* packageSize; 10 | @property NSString* localPath; 11 | @property NSString* nativeBuildTime; 12 | 13 | + (CodePushPackageMetadata*)parsePackageManifest:(NSString*)content; 14 | 15 | @end -------------------------------------------------------------------------------- /src/ios/CodePushPackageMetadata.m: -------------------------------------------------------------------------------- 1 | #import "CodePushPackageMetadata.h" 2 | 3 | @implementation CodePushPackageMetadata 4 | 5 | + (CodePushPackageMetadata*)parsePackageManifest:(NSString*)content { 6 | if (content) { 7 | NSData* manifestData = [content dataUsingEncoding:NSUTF8StringEncoding]; 8 | NSMutableDictionary *manifest = [NSJSONSerialization JSONObjectWithData:manifestData options:NSJSONReadingMutableContainers | NSJSONReadingMutableLeaves error:NULL]; 9 | 10 | if (manifestData) { 11 | CodePushPackageMetadata* packageMetadata = [[CodePushPackageMetadata alloc] init]; 12 | packageMetadata.deploymentKey = manifest[@"deploymentKey"]; 13 | packageMetadata.packageDescription = manifest[@"description"]; 14 | packageMetadata.label = manifest[@"label"]; 15 | packageMetadata.appVersion = manifest[@"appVersion"]; 16 | packageMetadata.isMandatory = manifest[@"isMandatory"]; 17 | packageMetadata.packageHash = manifest[@"packageHash"]; 18 | packageMetadata.packageSize = manifest[@"packageSize"]; 19 | packageMetadata.nativeBuildTime = manifest[@"nativeBuildTime"]; 20 | packageMetadata.localPath = manifest[@"localPath"]; 21 | return packageMetadata; 22 | } 23 | } 24 | 25 | return nil; 26 | } 27 | 28 | @end -------------------------------------------------------------------------------- /src/ios/CodePushReportingManager.h: -------------------------------------------------------------------------------- 1 | #import "StatusReport.h" 2 | 3 | @interface CodePushReportingManager : NSObject 4 | 5 | + (void)reportStatus:(StatusReport*)statusReport withWebView:(UIView*)webView; 6 | + (BOOL)hasFailedReport; 7 | + (StatusReport*)getFailedReport; 8 | + (StatusReport*)getAndClearFailedReport; 9 | + (void)saveFailedReport:(StatusReport*)statusReport; 10 | + (void)saveSuccessfulReport:(StatusReport*)statusReport; 11 | 12 | @end -------------------------------------------------------------------------------- /src/ios/InstallMode.h: -------------------------------------------------------------------------------- 1 | enum { 2 | IMMEDIATE = 0, 3 | ON_NEXT_RESTART = 1, 4 | ON_NEXT_RESUME = 2 5 | }; 6 | typedef NSInteger InstallMode; 7 | 8 | -------------------------------------------------------------------------------- /src/ios/InstallOptions.h: -------------------------------------------------------------------------------- 1 | #import "InstallMode.h" 2 | 3 | @interface InstallOptions : NSObject 4 | 5 | @property InstallMode installMode; 6 | @property int minimumBackgroundDuration; 7 | 8 | -(void)encodeWithCoder:(NSCoder*)encoder; 9 | -(id)initWithCoder:(NSCoder*)decoder; 10 | 11 | @end -------------------------------------------------------------------------------- /src/ios/InstallOptions.m: -------------------------------------------------------------------------------- 1 | #import "InstallOptions.h" 2 | 3 | @implementation InstallOptions 4 | 5 | NSString* const InstallModeKey = @"installMode"; 6 | NSString* const MinimumBackgroundDurationKey = @"minimumBackgroundDuration"; 7 | 8 | -(void)encodeWithCoder:(NSCoder*)encoder { 9 | [encoder encodeInteger:self.installMode forKey:InstallModeKey]; 10 | [encoder encodeInteger:self.minimumBackgroundDuration forKey:MinimumBackgroundDurationKey]; 11 | } 12 | 13 | -(id)initWithCoder:(NSCoder*)decoder { 14 | self.installMode = [decoder decodeIntegerForKey:InstallModeKey]; 15 | self.minimumBackgroundDuration = (int)[decoder decodeIntegerForKey:MinimumBackgroundDurationKey]; 16 | return self; 17 | } 18 | 19 | @end -------------------------------------------------------------------------------- /src/ios/JWT/Core/Algorithms/Base/CodePushJWTAlgorithm.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTAlgorithm.h 3 | // JWT 4 | // 5 | // Created by Klaas Pieter Annema on 31-05-13. 6 | // Copyright (c) 2013 Karma. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "CodePushJWTDeprecations.h" 11 | 12 | @protocol JWTAlgorithm 13 | 14 | @required 15 | /** 16 | Signs data using provided secret data. 17 | @param hash The data to sign. 18 | @param key The secret to use for signing. 19 | @param error The inout error. 20 | */ 21 | - (NSData *)signHash:(NSData *)hash key:(NSData *)key error:(NSError *__autoreleasing*)error; 22 | /** 23 | Verifies data using. 24 | @param hash The data to sign. 25 | @param signature The secret to use for signing. 26 | @param error The inout error. 27 | */ 28 | - (BOOL)verifyHash:(NSData *)hash signature:(NSData *)signature key:(NSData *)key error:(NSError *__autoreleasing*)error; 29 | 30 | //@required 31 | 32 | @property (nonatomic, readonly, copy) NSString *name; 33 | 34 | /** 35 | Encodes and encrypts the provided payload using the provided secret key 36 | @param theString The string to encode 37 | @param theSecret The secret to use for encryption 38 | @return An NSData object containing the encrypted payload, or nil if something went wrong. 39 | */ 40 | - (NSData *)encodePayload:(NSString *)theString withSecret:(NSString *)theSecret __deprecated_and_will_be_removed_in_release_version(JWTVersion_3_0_0); 41 | 42 | /** 43 | Verifies the provided signature using the signed input and verification key 44 | @param input The header and payload encoded string 45 | @param signature The JWT provided signature 46 | @param verificationKey The key to use for verifying the signature 47 | @return YES if the provided signature is valid, NO otherwise 48 | */ 49 | - (BOOL)verifySignedInput:(NSString *)input withSignature:(NSString *)signature verificationKey:(NSString *)verificationKey __deprecated_and_will_be_removed_in_release_version(JWTVersion_3_0_0); 50 | 51 | @optional 52 | 53 | /** 54 | Encodes and encrypts the provided payload using the provided secret key 55 | @param theStringData The data to encode 56 | @param theSecretData The secret data to use for encryption 57 | @return An NSData object containing the encrypted payload, or nil if something went wrong. 58 | */ 59 | - (NSData *)encodePayloadData:(NSData *)theStringData withSecret:(NSData *)theSecretData; 60 | 61 | /** 62 | Verifies the provided signature using the signed input and verification key (as data) 63 | @param input The header and payload encoded string 64 | @param signature The JWT provided signature 65 | @param verificationKeyData The key data to use for verifying the signature 66 | @return YES if the provided signature is valid, NO otherwise 67 | */ 68 | - (BOOL)verifySignedInput:(NSString *)input withSignature:(NSString *)signature verificationKeyData:(NSData *)verificationKeyData; 69 | @end 70 | -------------------------------------------------------------------------------- /src/ios/JWT/Core/Algorithms/Base/CodePushJWTAlgorithmFactory.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTAlgorithmFactory.h 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 07.10.15. 6 | // Copyright © 2015 Karma. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "CodePushJWTAlgorithm.h" 11 | @interface JWTAlgorithmFactory : NSObject 12 | 13 | + (NSArray *)algorithms; 14 | + (id)algorithmByName:(NSString *)name; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /src/ios/JWT/Core/Algorithms/Base/CodePushJWTAlgorithmFactory.m: -------------------------------------------------------------------------------- 1 | // 2 | // JWTAlgorithmFactory.m 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 07.10.15. 6 | // Copyright © 2015 Karma. All rights reserved. 7 | // 8 | 9 | #import "CodePushJWTAlgorithmFactory.h" 10 | #import "CodePushJWTAlgorithmHSBase.h" 11 | #import "CodePushJWTAlgorithmRSBase.h" 12 | #import "CodePushJWTAlgorithmNone.h" 13 | 14 | // not implemented. 15 | NSString *const JWTAlgorithmNameES256 = @"ES256"; 16 | NSString *const JWTAlgorithmNameES384 = @"ES384"; 17 | NSString *const JWTAlgorithmNameES512 = @"ES512"; 18 | 19 | @implementation JWTAlgorithmFactory 20 | 21 | + (NSArray *)algorithms { 22 | return @[ 23 | [JWTAlgorithmNone new], 24 | [JWTAlgorithmHSBase algorithm256], 25 | [JWTAlgorithmHSBase algorithm384], 26 | [JWTAlgorithmHSBase algorithm512], 27 | [JWTAlgorithmRSBase algorithm256], 28 | [JWTAlgorithmRSBase algorithm384], 29 | [JWTAlgorithmRSBase algorithm512] 30 | ]; 31 | 32 | } 33 | 34 | + (id)algorithmByName:(NSString *)name { 35 | id algorithm = nil; 36 | 37 | NSString *algName = [name copy]; 38 | 39 | NSUInteger index = [[self algorithms] indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) { 40 | // lowercase comparison 41 | return [obj.name.lowercaseString isEqualToString:algName.lowercaseString]; 42 | }]; 43 | 44 | if (index != NSNotFound) { 45 | algorithm = [self algorithms][index]; 46 | } 47 | 48 | return algorithm; 49 | } 50 | 51 | @end -------------------------------------------------------------------------------- /src/ios/JWT/Core/Algorithms/Base/CodePushJWTAlgorithmNone.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTAlgorithmNone.h 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 16.10.15. 6 | // Copyright © 2015 Karma. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "CodePushJWTAlgorithm.h" 11 | extern NSString *const JWTAlgorithmNameNone; 12 | 13 | @interface JWTAlgorithmNone : NSObject 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /src/ios/JWT/Core/Algorithms/Base/CodePushJWTAlgorithmNone.m: -------------------------------------------------------------------------------- 1 | // 2 | // JWTAlgorithmNone.m 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 16.10.15. 6 | // Copyright © 2015 Karma. All rights reserved. 7 | // 8 | 9 | #import "CodePushJWTAlgorithmNone.h" 10 | NSString *const JWTAlgorithmNameNone = @"none"; 11 | 12 | @implementation JWTAlgorithmNone 13 | 14 | - (NSString *)name { 15 | return JWTAlgorithmNameNone; 16 | } 17 | 18 | - (NSData *)signHash:(NSData *)hash key:(NSData *)key error:(NSError *__autoreleasing *)error { 19 | return [NSData data]; 20 | } 21 | 22 | - (BOOL)verifyHash:(NSData *)hash signature:(NSData *)signature key:(NSData *)key error:(NSError *__autoreleasing *)error { 23 | //if a secret is provided, this isn't the None algorithm 24 | if (key && key.length > 0) { 25 | return NO; 26 | } 27 | 28 | //If the signature isn't blank, this isn't the None algorithm 29 | if (signature && signature.length > 0) { 30 | return NO; 31 | } 32 | 33 | return YES; 34 | } 35 | 36 | - (NSData *)encodePayload:(NSString *)theString withSecret:(NSString *)theSecret { 37 | return [self encodePayloadData:[theSecret dataUsingEncoding:NSUTF8StringEncoding] withSecret:[theSecret dataUsingEncoding:NSUTF8StringEncoding]]; 38 | } 39 | 40 | - (NSData *)encodePayloadData:(NSData *)theStringData withSecret:(NSData *)theSecretData 41 | { 42 | return [self signHash:theStringData key:theSecretData error:nil]; 43 | } 44 | 45 | - (BOOL)verifySignedInput:(NSString *)input withSignature:(NSString *)signature verificationKey:(NSString *)verificationKey 46 | { 47 | return [self verifySignedInput:input withSignature:signature verificationKeyData:verificationKey]; 48 | } 49 | 50 | - (BOOL)verifySignedInput:(NSString *)input withSignature:(NSString *)signature verificationKeyData:(NSData *)verificationKeyData 51 | { 52 | return [self verifyHash:[input dataUsingEncoding:NSUTF8StringEncoding] signature:[signature dataUsingEncoding:NSUTF8StringEncoding] key:verificationKeyData error:nil]; 53 | } 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /src/ios/JWT/Core/Algorithms/ESFamily/CodePushJWTAlgorithmESBase.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTAlgorithmESBase.h 3 | // Pods 4 | // 5 | // Created by Lobanov Dmitry on 12.02.17. 6 | // 7 | // 8 | 9 | #import 10 | #import "CodePushJWTRSAlgorithm.h" 11 | extern NSString *const JWTAlgorithmNameES256; 12 | extern NSString *const JWTAlgorithmNameES384; 13 | extern NSString *const JWTAlgorithmNameES512; 14 | @interface JWTAlgorithmESBase : NSObject @end 15 | 16 | @interface JWTAlgorithmESBase (JWTAsymmetricKeysAlgorithm) @end 17 | 18 | @interface JWTAlgorithmESBase (Create) 19 | 20 | + (instancetype)algorithm256; 21 | + (instancetype)algorithm384; 22 | + (instancetype)algorithm512; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /src/ios/JWT/Core/Algorithms/ESFamily/CodePushJWTAlgorithmESBase.m: -------------------------------------------------------------------------------- 1 | // 2 | // JWTAlgorithmESBase.m 3 | // Pods 4 | // 5 | // Created by Lobanov Dmitry on 12.02.17. 6 | // 7 | // 8 | 9 | #import "CodePushJWTAlgorithmESBase.h" 10 | #import 11 | @interface JWTAlgorithmESBase () 12 | 13 | @end 14 | @implementation JWTAlgorithmESBase 15 | @synthesize keyExtractorType; 16 | @synthesize signKey; 17 | @synthesize verifyKey; 18 | @end 19 | 20 | // thanks! https://github.com/soyersoyer/SwCrypt 21 | @interface JWTAlgorithmESBase (ImportKeys) 22 | - (void)importKey; 23 | //importKey(publicKey, format: .importKeyBinary, keyType: .keyPublic) 24 | @end 25 | @implementation JWTAlgorithmESBase (ImportKeys) 26 | - (void)importKey { 27 | return; 28 | } 29 | @end 30 | 31 | @implementation JWTAlgorithmESBase (JWTAsymmetricKeysAlgorithm) 32 | - (NSString *)name { 33 | return @"ESBase"; 34 | } 35 | - (NSData *)signHash:(NSData *)hash key:(NSData *)key error:(NSError *__autoreleasing *)error { 36 | return nil; 37 | } 38 | - (BOOL)verifyHash:(NSData *)hash signature:(NSData *)signature key:(NSData *)key error:(NSError *__autoreleasing *)error { 39 | return NO; 40 | } 41 | @end 42 | -------------------------------------------------------------------------------- /src/ios/JWT/Core/Algorithms/HSFamily/CodePushJWTAlgorithmHSBase.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTAlgorithmHSBase.h 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 13.03.16. 6 | // Copyright © 2016 Karma. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "CodePushJWTAlgorithm.h" 11 | extern NSString *const JWTAlgorithmNameHS256; 12 | extern NSString *const JWTAlgorithmNameHS384; 13 | extern NSString *const JWTAlgorithmNameHS512; 14 | 15 | @interface JWTAlgorithmHSBase : NSObject 16 | 17 | @property (assign, nonatomic, readonly) size_t ccSHANumberDigestLength; 18 | @property (assign, nonatomic, readonly) uint32_t ccHmacAlgSHANumber; 19 | 20 | @end 21 | 22 | @interface JWTAlgorithmHSBase (Create) 23 | 24 | + (instancetype)algorithm256; 25 | + (instancetype)algorithm384; 26 | + (instancetype)algorithm512; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /src/ios/JWT/Core/Algorithms/Holders/CodePushJWTAlgorithmDataHolder.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTAlgorithmDataHolder.h 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 31.08.16. 6 | // Copyright © 2016 Karma. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "CodePushJWTAlgorithm.h" 11 | #import "CodePushJWTDeprecations.h" 12 | #import "CodePushJWTBase64Coder.h" 13 | 14 | // TODO: available in 3.0 15 | // All methods with secret as NSString in algorithms will be deprecated or removed. 16 | 17 | @protocol JWTAlgorithmDataHolderProtocol 18 | /** 19 | The verification key to use when encoding/decoding a JWT in data form 20 | */ 21 | @property (copy, nonatomic, readwrite) NSData *internalSecretData; 22 | 23 | /** 24 | The to use for encoding a JWT 25 | */ 26 | @property (strong, nonatomic, readwrite) id internalAlgorithm; 27 | 28 | /** 29 | The string coder. It converts data to string and vice versa. 30 | */ 31 | @property (strong, nonatomic, readwrite) id internalStringCoder; 32 | @end 33 | 34 | @interface JWTAlgorithmBaseDataHolder : NSObject 35 | 36 | #pragma mark - Getters 37 | /** 38 | The verification key to use when encoding/decoding a JWT 39 | */ 40 | @property (copy, nonatomic, readonly) NSString *internalSecret; 41 | 42 | /** 43 | The algorithm name to use for decoding the JWT. Required unless force decode is true 44 | */ 45 | @property (copy, nonatomic, readonly) NSString *internalAlgorithmName; 46 | 47 | #pragma mark - Setters 48 | /** 49 | Sets jwtSecret and returns the JWTAlgorithmBaseDataHolder to allow for method chaining 50 | */ 51 | @property (copy, nonatomic, readonly) JWTAlgorithmBaseDataHolder *(^secret)(NSString *secret); 52 | 53 | /** 54 | Sets jwtSecretData and returns the JWTAlgorithmBaseDataHolder to allow for method chaining 55 | */ 56 | @property (copy, nonatomic, readonly) JWTAlgorithmBaseDataHolder *(^secretData)(NSData *secretData); 57 | 58 | /** 59 | Sets jwtAlgorithm and returns the JWTAlgorithmBaseDataHolder to allow for method chaining 60 | */ 61 | @property (copy, nonatomic, readonly) JWTAlgorithmBaseDataHolder *(^algorithm)(idalgorithm); 62 | 63 | /** 64 | Sets jwtAlgorithmName and returns the JWTAlgorithmBaseDataHolder to allow for method chaining. See list of names in appropriate headers. 65 | */ 66 | @property (copy, nonatomic, readonly) JWTAlgorithmBaseDataHolder *(^algorithmName)(NSString *algorithmName); 67 | 68 | /** 69 | Sets stringCoder and returns the JWTAlgorithmBaseDataHolder to allow for method chaining. See list of names in appropriate headers. 70 | */ 71 | @property (copy, nonatomic, readonly) JWTAlgorithmBaseDataHolder *(^stringCoder)(id stringCoder); 72 | @end 73 | 74 | @protocol JWTAlgorithmDataHolderCreateProtocol 75 | 76 | + (instancetype)createWithAlgorithm256; 77 | + (instancetype)createWithAlgorithm384; 78 | + (instancetype)createWithAlgorithm512; 79 | 80 | @end 81 | 82 | @interface JWTAlgorithmNoneDataHolder : JWTAlgorithmBaseDataHolder @end 83 | @interface JWTAlgorithmHSFamilyDataHolder : JWTAlgorithmBaseDataHolder 84 | @end 85 | @protocol JWTCryptoKeyProtocol; 86 | @interface JWTAlgorithmRSFamilyDataHolder : JWTAlgorithmBaseDataHolder 87 | #pragma mark - Getters 88 | /** 89 | The passphrase for the PKCS12 blob, which represents the certificate containing the private key for the RS algorithms. 90 | */ 91 | @property (copy, nonatomic, readonly) NSString *internalPrivateKeyCertificatePassphrase; 92 | @property (copy, nonatomic, readonly) NSString *internalKeyExtractorType; 93 | @property (strong, nonatomic, readonly) id internalSignKey; 94 | @property (strong, nonatomic, readonly) id internalVerifyKey; 95 | #pragma mark - Setters 96 | /** 97 | Sets jwtPrivateKeyCertificatePassphrase and returns the JWTAlgorithmRSFamilyDataHolder to allow for method chaining 98 | */ 99 | @property (copy, nonatomic, readonly) JWTAlgorithmRSFamilyDataHolder *(^privateKeyCertificatePassphrase)(NSString *privateKeyCertificatePassphrase); 100 | @property (copy, nonatomic, readonly) JWTAlgorithmRSFamilyDataHolder *(^keyExtractorType)(NSString *keyExtractorType); 101 | @property (copy, nonatomic, readonly) JWTAlgorithmRSFamilyDataHolder *(^signKey)(id signKey); 102 | @property (copy, nonatomic, readonly) JWTAlgorithmRSFamilyDataHolder *(^verifyKey)(id verifyKey); 103 | @end 104 | -------------------------------------------------------------------------------- /src/ios/JWT/Core/Algorithms/Holders/CodePushJWTAlgorithmDataHolderChain.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTAlgorithmDataHolderChain.h 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 02.10.16. 6 | // Copyright © 2016 Karma. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "CodePushJWTAlgorithmDataHolder.h" 11 | 12 | @interface JWTAlgorithmDataHolderChain : NSObject 13 | 14 | @property (strong, nonatomic, readonly) NSArray *holders; 15 | 16 | #pragma mark - Initialization 17 | - (instancetype)initWithHolders:(NSArray *)holders; 18 | - (instancetype)initWithHolder:(id)holder; 19 | 20 | #pragma mark - Appending 21 | - (instancetype)chainByAppendingChain:(JWTAlgorithmDataHolderChain *)chain; 22 | - (instancetype)chainByAppendingHolders:(NSArray *)holders; 23 | - (instancetype)chainByAppendingHolder:(id)holder; 24 | 25 | #pragma mark - Create 26 | + (instancetype)chainWithHolders:(NSArray *)holders; 27 | + (instancetype)chainWithHolder:(id)holder; 28 | @end 29 | 30 | @interface JWTAlgorithmDataHolderChain (HoldersPopulation) 31 | - (NSArray *)singleAlgorithm:(id)algorithm withManySecretData:(NSArray *)secretsData; 32 | - (NSArray *)singleSecretData:(NSData *)secretData withManyAlgorithms:(NSArray *)algorithms; 33 | 34 | - (instancetype)chainByPopulatingAlgorithm:(id)algorithm withManySecretData:(NSArray *)secretsData; 35 | - (instancetype)chainByPopulatingSecretData:(NSData *)secretData withManyAlgorithms:(NSArray *)algorithms; 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /src/ios/JWT/Core/Algorithms/RSFamily/CodePushJWTAlgorithmRSBase.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTAlgorithmRSBase.h 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 13.03.16. 6 | // Copyright © 2016 Karma. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "CodePushJWTRSAlgorithm.h" 11 | extern NSString *const JWTAlgorithmNameRS256; 12 | extern NSString *const JWTAlgorithmNameRS384; 13 | extern NSString *const JWTAlgorithmNameRS512; 14 | 15 | @interface JWTAlgorithmRSBase : NSObject 16 | 17 | @property (assign, nonatomic, readonly) size_t ccSHANumberDigestLength; 18 | @property (assign, nonatomic, readonly) uint32_t secPaddingPKCS1SHANumber; 19 | - (unsigned char *)CC_SHANumberWithData:(const void *)data withLength:(uint32_t)len withHashBytes:(unsigned char *)hashBytes; 20 | 21 | @end 22 | 23 | @interface JWTAlgorithmRSBase (Create) 24 | 25 | + (instancetype)algorithm256; 26 | + (instancetype)algorithm384; 27 | + (instancetype)algorithm512; 28 | + (instancetype)mutableAlgorithm __deprecated; 29 | 30 | @end 31 | 32 | /* 33 | // when you can't live without mutability, uncomment. 34 | @class JWTAlgorithmRSFamilyMemberMutable; 35 | */ -------------------------------------------------------------------------------- /src/ios/JWT/Core/Algorithms/RSFamily/CodePushJWTRSAlgorithm.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Marcelo Schroeder on 12/03/2016. 3 | // Copyright (c) 2016 Karma. All rights reserved. 4 | // 5 | 6 | #import 7 | #import "CodePushJWTAlgorithm.h" 8 | @protocol JWTCryptoKeyProtocol; 9 | 10 | @protocol JWTAsymmetricKeysAlgorithm 11 | 12 | @optional 13 | @property(nonatomic, readwrite, copy) NSString *keyExtractorType; 14 | @property(nonatomic, readwrite, strong) id signKey; 15 | @property(nonatomic, readwrite, strong) id verifyKey; 16 | 17 | @end 18 | 19 | @protocol JWTRSAlgorithm 20 | 21 | @required 22 | @property(nonatomic, readwrite, copy) NSString *privateKeyCertificatePassphrase; 23 | @end 24 | -------------------------------------------------------------------------------- /src/ios/JWT/Core/Algorithms/RSFamily/RSKeys/CodePushJWTCryptoKey.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTCryptoKey.h 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 04.02.17. 6 | // Copyright © 2017 JWTIO. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @protocol JWTCryptoKeyProtocol 13 | @property (copy, nonatomic, readonly) NSString *tag; 14 | @property (assign, nonatomic, readonly) SecKeyRef key; 15 | @property (copy, nonatomic, readonly) NSData *rawKey; 16 | @end 17 | 18 | @protocol JWTCryptoKey__Generator__Protocol 19 | - (instancetype)initWithData:(NSData *)data parameters:(NSDictionary *)parameters error:(NSError *__autoreleasing*)error; //NS_DESIGNATED_INITIALIZER 20 | - (instancetype)initWithBase64String:(NSString *)base64String parameters:(NSDictionary *)parameters error:(NSError *__autoreleasing*)error; 21 | - (instancetype)initWithPemEncoded:(NSString *)encoded parameters:(NSDictionary *)parameters error:(NSError *__autoreleasing*)error; 22 | - (instancetype)initWithPemAtURL:(NSURL *)url parameters:(NSDictionary *)parameters error:(NSError *__autoreleasing*)error; 23 | @end 24 | 25 | @interface JWTCryptoKeyBuilder : NSObject 26 | @property (assign, nonatomic, readonly) NSString *keyType; 27 | - (instancetype)keyTypeRSA; 28 | - (instancetype)keyTypeEC; 29 | @end 30 | 31 | /* 32 | Don't use it directly, use subclasses instead! 33 | */ 34 | @interface JWTCryptoKey : NSObject @end 35 | 36 | // Check that Security key is retrieved. 37 | // Could be used as additional step in key data verification. 38 | @interface JWTCryptoKey (Check) 39 | - (instancetype)checkedWithError:(NSError *__autoreleasing*)error; 40 | @end 41 | 42 | @interface JWTCryptoKey (Parameters) 43 | + (NSString *)parametersKeyBuilder; 44 | @end 45 | 46 | @interface JWTCryptoKeyPublic : JWTCryptoKey 47 | - (instancetype)initWithCertificateData:(NSData *)certificateData parameters:(NSDictionary *)parameters error:(NSError *__autoreleasing*)error; //NS_DESIGNATED_INITIALIZER; 48 | - (instancetype)initWithCertificateBase64String:(NSString *)certificateString parameters:(NSDictionary *)parameters error:(NSError *__autoreleasing*)error; 49 | @end 50 | 51 | @interface JWTCryptoKeyPrivate : JWTCryptoKey 52 | - (instancetype)initWithP12Data:(NSData *)p12Data withPassphrase:(NSString *)passphrase parameters:(NSDictionary *)parameters error:(NSError *__autoreleasing*)error; //NS_DESIGNATED_INITIALIZER; 53 | - (instancetype)initWithP12AtURL:(NSURL *)url withPassphrase:(NSString *)passphrase parameters:(NSDictionary *)parameters error:(NSError *__autoreleasing*)error; 54 | @end 55 | -------------------------------------------------------------------------------- /src/ios/JWT/Core/Algorithms/RSFamily/RSKeys/CodePushJWTCryptoKeyExtractor.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTCryptoKeyExtractor.h 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 04.02.17. 6 | // Copyright © 2017 JWTIO. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @protocol JWTCryptoKeyProtocol; 13 | @protocol JWTCryptoKeyExtractorProtocol 14 | @optional 15 | - (id)keyFromString:(NSString *)string parameters:(NSDictionary *)parameters error:(NSError *__autoreleasing*)error; 16 | - (id)keyFromData:(NSData *)data parameters:(NSDictionary *)parameters error:(NSError *__autoreleasing*)error; 17 | @end 18 | 19 | @interface JWTCryptoKeyExtractor : NSObject 20 | @property (copy, nonatomic, readonly) NSString *type; 21 | + (NSString *)type; 22 | + (NSString *)parametersKeyCertificatePassphrase; 23 | @end 24 | 25 | @interface JWTCryptoKeyExtractor (ClassCluster) 26 | + (instancetype)publicKeyWithCertificate; 27 | + (instancetype)privateKeyInP12; 28 | + (instancetype)publicKeyWithPEMBase64; 29 | + (instancetype)privateKeyWithPEMBase64; 30 | + (instancetype)createWithType:(NSString *)type; 31 | @end 32 | -------------------------------------------------------------------------------- /src/ios/JWT/Core/Algorithms/RSFamily/RSKeys/CodePushJWTCryptoSecurity.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTCryptoSecurity.h 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 04.02.17. 6 | // Copyright © 2017 JWTIO. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | // Thanks for https://github.com/TakeScoop/SwiftyRSA! 12 | @interface JWTCryptoSecurity : NSObject 13 | + (NSString *)keyTypeRSA; 14 | + (NSString *)keyTypeEC; 15 | + (SecKeyRef)addKeyWithData:(NSData *)data asPublic:(BOOL)public tag:(NSString *)tag type:(NSString *)type error:(NSError *__autoreleasing*)error; 16 | + (SecKeyRef)addKeyWithData:(NSData *)data asPublic:(BOOL)public tag:(NSString *)tag error:(NSError *__autoreleasing*)error; 17 | + (SecKeyRef)keyByTag:(NSString *)tag error:(NSError *__autoreleasing*)error; 18 | + (void)removeKeyByTag:(NSString *)tag error:(NSError *__autoreleasing*)error; 19 | @end 20 | 21 | @interface JWTCryptoSecurity (Certificates) 22 | + (OSStatus)extractIdentityAndTrustFromPKCS12:(CFDataRef)inPKCS12Data password:(CFStringRef)password identity:(SecIdentityRef *)outIdentity trust:(SecTrustRef *)outTrust; 23 | + (SecKeyRef)publicKeyFromCertificate:(NSData *)certificateData; 24 | @end 25 | 26 | @interface JWTCryptoSecurity (Pem) 27 | + (NSString *)certificateFromPemFileContent:(NSString *)content; 28 | + (NSString *)keyFromPemFileContent:(NSString *)content; 29 | + (NSArray *)itemsFromPemFileContent:(NSString *)content byRegex:(NSRegularExpression *)expression; 30 | + (NSString *)certificateFromPemFileWithName:(NSString *)name; 31 | + (NSString *)keyFromPemFileWithName:(NSString *)name; 32 | + (NSArray *)itemsFromPemFileWithName:(NSString *)name byRegex:(NSRegularExpression *)expression; 33 | + (NSString *)stringByRemovingPemHeadersFromString:(NSString *)string; 34 | @end 35 | 36 | @interface JWTCryptoSecurity (PublicKey) 37 | + (NSData *)dataByRemovingPublicKeyHeader:(NSData *)data error:(NSError *__autoreleasing*)error; 38 | @end 39 | -------------------------------------------------------------------------------- /src/ios/JWT/Core/ClaimSet/CodePushJWTClaim.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTClaim.h 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 13.02.16. 6 | // Copyright © 2016 Karma. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface JWTClaim : NSObject 12 | 13 | + (NSString *)name; 14 | + (instancetype)claimByName:(NSString *)name; 15 | + (BOOL)verifyValue:(NSObject *)value withTrustedValue:(NSObject *)trustedValue; 16 | - (BOOL)verifyValue:(NSObject *)value withTrustedValue:(NSObject *)trustedValue; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /src/ios/JWT/Core/ClaimSet/CodePushJWTClaimsSet.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTClaimsSet.h 3 | // JWT 4 | // 5 | // Created by Klaas Pieter Annema on 31-05-13. 6 | // Copyright (c) 2013 Karma. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface JWTClaimsSet : NSObject 12 | 13 | @property (nonatomic, readwrite, copy) NSString *issuer; 14 | @property (nonatomic, readwrite, copy) NSString *subject; 15 | @property (nonatomic, readwrite, copy) NSString *audience; 16 | @property (nonatomic, readwrite, copy) NSDate *expirationDate; 17 | @property (nonatomic, readwrite, copy) NSDate *notBeforeDate; 18 | @property (nonatomic, readwrite, copy) NSDate *issuedAt; 19 | @property (nonatomic, readwrite, copy) NSString *identifier; 20 | @property (nonatomic, readwrite, copy) NSString *type; 21 | @property (nonatomic, readwrite, copy) NSString *scope; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /src/ios/JWT/Core/ClaimSet/CodePushJWTClaimsSet.m: -------------------------------------------------------------------------------- 1 | // 2 | // JWTClaimsSet.m 3 | // JWT 4 | // 5 | // Created by Klaas Pieter Annema on 31-05-13. 6 | // Copyright (c) 2013 Karma. All rights reserved. 7 | // 8 | 9 | #import "CodePushJWTClaimsSet.h" 10 | 11 | @implementation JWTClaimsSet 12 | 13 | - (id)copyWithZone:(NSZone *)zone { 14 | JWTClaimsSet *newClaimsSet = [[self.class alloc] init]; 15 | 16 | newClaimsSet.issuer = self.issuer; 17 | newClaimsSet.subject = self.subject; 18 | newClaimsSet.audience = self.audience; 19 | newClaimsSet.expirationDate = self.expirationDate; 20 | newClaimsSet.notBeforeDate = self.notBeforeDate; 21 | newClaimsSet.issuedAt = self.issuedAt; 22 | newClaimsSet.identifier = self.identifier; 23 | newClaimsSet.type = self.type; 24 | newClaimsSet.scope = self.scope; 25 | 26 | return newClaimsSet; 27 | } 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /src/ios/JWT/Core/ClaimSet/CodePushJWTClaimsSetSerializer.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTClaimsSetSerializer.h 3 | // JWT 4 | // 5 | // Created by Klaas Pieter Annema on 31-05-13. 6 | // Copyright (c) 2013 Karma. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "CodePushJWTClaimsSet.h" 12 | 13 | @interface JWTClaimsSetSerializer : NSObject 14 | 15 | + (NSArray *)claimsSetKeys; 16 | + (NSDictionary *)dictionaryWithClaimsSet:(JWTClaimsSet *)theClaimsSet; 17 | + (JWTClaimsSet *)claimsSetWithDictionary:(NSDictionary *)theDictionary; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /src/ios/JWT/Core/ClaimSet/CodePushJWTClaimsSetSerializer.m: -------------------------------------------------------------------------------- 1 | // 2 | // JWTClaimsSetSerializer.m 3 | // JWT 4 | // 5 | // Created by Klaas Pieter Annema on 31-05-13. 6 | // Copyright (c) 2013 Karma. All rights reserved. 7 | // 8 | 9 | #import "CodePushJWTClaimsSetSerializer.h" 10 | 11 | @implementation JWTClaimsSetSerializer 12 | 13 | + (NSArray *)claimsSetKeys 14 | { 15 | return @[@"iss", @"sub", @"aud", @"exp", @"nbf", @"iat", @"jti", @"typ", @"scope"]; 16 | } 17 | 18 | + (NSDictionary *)dictionaryWithClaimsSet:(JWTClaimsSet *)theClaimsSet; 19 | { 20 | NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; 21 | [self dictionary:dictionary setObjectIfNotNil:theClaimsSet.issuer forKey:@"iss"]; 22 | [self dictionary:dictionary setObjectIfNotNil:theClaimsSet.subject forKey:@"sub"]; 23 | [self dictionary:dictionary setObjectIfNotNil:theClaimsSet.audience forKey:@"aud"]; 24 | [self dictionary:dictionary setDateIfNotNil:theClaimsSet.expirationDate forKey:@"exp"]; 25 | [self dictionary:dictionary setDateIfNotNil:theClaimsSet.notBeforeDate forKey:@"nbf"]; 26 | [self dictionary:dictionary setDateIfNotNil:theClaimsSet.issuedAt forKey:@"iat"]; 27 | [self dictionary:dictionary setObjectIfNotNil:theClaimsSet.identifier forKey:@"jti"]; 28 | [self dictionary:dictionary setObjectIfNotNil:theClaimsSet.type forKey:@"typ"]; 29 | [self dictionary:dictionary setObjectIfNotNil:theClaimsSet.scope forKey:@"scope"]; 30 | 31 | return [dictionary copy]; 32 | } 33 | 34 | + (JWTClaimsSet *)claimsSetWithDictionary:(NSDictionary *)theDictionary; 35 | { 36 | JWTClaimsSet *claimsSet = [[JWTClaimsSet alloc] init]; 37 | claimsSet.issuer = [theDictionary objectForKey:@"iss"]; 38 | claimsSet.subject = [theDictionary objectForKey:@"sub"]; 39 | claimsSet.audience = [theDictionary objectForKey:@"aud"]; 40 | claimsSet.expirationDate = [NSDate dateWithTimeIntervalSince1970:[[theDictionary objectForKey:@"exp"] doubleValue]]; 41 | claimsSet.notBeforeDate = [NSDate dateWithTimeIntervalSince1970:[[theDictionary objectForKey:@"nbf"] doubleValue]]; 42 | claimsSet.issuedAt = [NSDate dateWithTimeIntervalSince1970:[[theDictionary objectForKey:@"iat"] doubleValue]]; 43 | claimsSet.identifier = [theDictionary objectForKey:@"jti"]; 44 | claimsSet.type = [theDictionary objectForKey:@"typ"]; 45 | claimsSet.scope = [theDictionary objectForKey:@"scope"]; 46 | 47 | return claimsSet; 48 | } 49 | 50 | + (void)dictionary:(NSMutableDictionary *)theDictionary setObjectIfNotNil:(id)theObject forKey:(id)theKey; 51 | { 52 | if (!theObject) 53 | return; 54 | 55 | [theDictionary setObject:theObject forKey:theKey]; 56 | } 57 | 58 | + (void)dictionary:(NSMutableDictionary *)theDictionary setDateIfNotNil:(NSDate*)date forKey:(id)theKey; 59 | { 60 | if (!date) 61 | return; 62 | NSNumber* value = @((unsigned long)[date timeIntervalSince1970]); 63 | 64 | [theDictionary setObject:value forKey:theKey]; 65 | } 66 | 67 | 68 | @end 69 | -------------------------------------------------------------------------------- /src/ios/JWT/Core/ClaimSet/CodePushJWTClaimsSetVerifier.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTClaimsSetVerifier.h 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 13.02.16. 6 | // Copyright © 2016 Karma. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "CodePushJWTClaimsSet.h" 11 | 12 | @interface JWTClaimsSetVerifier : NSObject 13 | 14 | + (BOOL)verifyClaimsSet:(JWTClaimsSet *)theClaimsSet withTrustedClaimsSet:(JWTClaimsSet *)trustedClaimsSet; 15 | 16 | + (BOOL)verifyClaimsSetDictionary:(NSDictionary *)theClaimsSetDictionary withTrustedClaimsSet:(JWTClaimsSet *)trustedClaimsSet; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /src/ios/JWT/Core/ClaimSet/CodePushJWTClaimsSetVerifier.m: -------------------------------------------------------------------------------- 1 | // 2 | // JWTClaimsSetVerifier.m 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 13.02.16. 6 | // Copyright © 2016 Karma. All rights reserved. 7 | // 8 | 9 | #import "CodePushJWTClaimsSetVerifier.h" 10 | #import "CodePushJWTClaimsSetSerializer.h" 11 | #import "CodePushJWTClaim.h" 12 | 13 | @implementation JWTClaimsSetVerifier 14 | 15 | + (BOOL)verifyDictionary:(NSDictionary *)dictionary withTrustedDictionary:(NSDictionary *)trustedDictionary byKey:(NSString *)key { 16 | NSObject *value = dictionary[key]; 17 | NSObject *trustedValue = trustedDictionary[key]; 18 | 19 | BOOL result = YES; 20 | 21 | if (trustedValue) { 22 | result = [[JWTClaim claimByName:key] verifyValue:value withTrustedValue:trustedValue]; 23 | } 24 | 25 | return result; 26 | } 27 | 28 | + (BOOL)verifyClaimsSetDictionary:(NSDictionary *)theClaimsSetDictionary withTrustedClaimsSetDictionary:(NSDictionary *)trustedClaimsSetDictionary { 29 | 30 | NSArray *claimsSets = [JWTClaimsSetSerializer claimsSetKeys]; 31 | 32 | if (!trustedClaimsSetDictionary) { 33 | return YES; 34 | } 35 | 36 | if (!theClaimsSetDictionary) { 37 | return NO; 38 | } 39 | 40 | BOOL result = YES; 41 | 42 | for (NSString *key in claimsSets) { 43 | result = result && [self verifyDictionary:theClaimsSetDictionary withTrustedDictionary:trustedClaimsSetDictionary byKey:key]; 44 | } 45 | 46 | return result; 47 | } 48 | 49 | 50 | + (BOOL)verifyClaimsSet:(JWTClaimsSet *)theClaimsSet withTrustedClaimsSet:(JWTClaimsSet *)trustedClaimsSet { 51 | 52 | NSDictionary *dictionary = [JWTClaimsSetSerializer dictionaryWithClaimsSet:theClaimsSet]; 53 | 54 | NSDictionary *trustedDictionary = [JWTClaimsSetSerializer dictionaryWithClaimsSet:trustedClaimsSet]; 55 | 56 | NSArray *claimsSets = [JWTClaimsSetSerializer claimsSetKeys]; 57 | 58 | BOOL result = YES; 59 | for (NSString *key in claimsSets) { 60 | result = result && [self verifyDictionary:dictionary withTrustedDictionary:trustedDictionary byKey:key]; 61 | } 62 | 63 | return result; 64 | } 65 | 66 | + (BOOL)verifyClaimsSetDictionary:(NSDictionary *)theClaimsSetDictionary withTrustedClaimsSet:(JWTClaimsSet *)trustedClaimsSet { 67 | NSDictionary *trustedDictionary = [JWTClaimsSetSerializer dictionaryWithClaimsSet:trustedClaimsSet]; 68 | 69 | return [self verifyClaimsSetDictionary:theClaimsSetDictionary withTrustedClaimsSetDictionary:trustedDictionary]; 70 | } 71 | 72 | @end -------------------------------------------------------------------------------- /src/ios/JWT/Core/Coding/CodePushJWTCoding+ResultTypes.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTCoding+ResultTypes.h 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 30.11.16. 6 | // Copyright © 2016 JWTIO. All rights reserved. 7 | // 8 | 9 | #import "CodePushJWTCoding.h" 10 | @class JWTClaimsSet; 11 | 12 | extern NSString *JWTCodingResultHeaders; 13 | extern NSString *JWTCodingResultPayload; 14 | 15 | @interface JWT (ResultTypes) @end 16 | 17 | /* 18 | ResultType 19 | /\ 20 | / \ 21 | / \ 22 | Success Error 23 | 24 | Protocols: Mutable and Immutable (?!?) 25 | */ 26 | 27 | // Public 28 | @protocol JWTCodingResultTypeSuccessEncodedProtocol 29 | @property (copy, nonatomic, readonly) NSString *encoded; 30 | - (instancetype)initWithEncoded:(NSString *)encoded; 31 | @property (copy, nonatomic, readonly) NSString *token; 32 | - (instancetype)initWithToken:(NSString *)token; 33 | @end 34 | 35 | // Public 36 | @protocol JWTCodingResultTypeSuccessDecodedProtocol 37 | @property (copy, nonatomic, readonly) NSDictionary *headers; 38 | @property (copy, nonatomic, readonly) NSDictionary *payload; 39 | 40 | // dictionary @{ 41 | // JWTCodingResultHeaders : self.headers, 42 | // JWTCodingResultPayload : self.payload 43 | //} 44 | @property (copy, nonatomic, readonly) NSDictionary *headerAndPayloadDictionary; 45 | 46 | @property (nonatomic, readonly) JWTClaimsSet *claimsSet; 47 | - (instancetype)initWithHeadersAndPayload:(NSDictionary *)headersAndPayloadDictionary; 48 | - (instancetype)initWithHeaders:(NSDictionary *)headers withPayload:(NSDictionary *)payload; 49 | - (instancetype)initWithClaimsSet:(JWTClaimsSet *)claimsSet; 50 | @end 51 | 52 | // Public 53 | @interface JWTCodingResultTypeSuccess : NSObject @end 54 | 55 | // Public 56 | @protocol JWTCodingResultTypeErrorProtocol 57 | @property (copy, nonatomic, readonly) NSError *error; 58 | - (instancetype)initWithError:(NSError *)error; 59 | @end 60 | 61 | @interface JWTCodingResultTypeError : NSObject @end 62 | 63 | @interface JWTCodingResultType : NSObject 64 | - (instancetype)initWithSuccessResult:(JWTCodingResultTypeSuccess *)success; 65 | - (instancetype)initWithErrorResult:(JWTCodingResultTypeError *)error; 66 | @property (strong, nonatomic, readonly) JWTCodingResultTypeSuccess *successResult; 67 | @property (strong, nonatomic, readonly) JWTCodingResultTypeError *errorResult; 68 | @end 69 | -------------------------------------------------------------------------------- /src/ios/JWT/Core/Coding/CodePushJWTCoding+ResultTypes.m: -------------------------------------------------------------------------------- 1 | // 2 | // JWTCoding+ResultTypes.m 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 30.11.16. 6 | // Copyright © 2016 JWTIO. All rights reserved. 7 | // 8 | 9 | #import "CodePushJWTCoding+ResultTypes.h" 10 | 11 | NSString *JWTCodingResultHeaders = @"header"; 12 | NSString *JWTCodingResultPayload = @"payload"; 13 | 14 | @implementation JWT (ResultTypes) @end 15 | 16 | // Protected? 17 | @protocol JWTMutableCodingResultTypeSuccessEncodedProtocol 18 | @property (copy, nonatomic, readwrite) NSString *encoded; 19 | @property (copy, nonatomic, readwrite) NSString *token; 20 | @end 21 | 22 | // Protected? 23 | @protocol JWTMutableCodingResultTypeSuccessDecodedProtocol 24 | @property (copy, nonatomic, readwrite) NSDictionary *headers; 25 | @property (copy, nonatomic, readwrite) NSDictionary *payload; 26 | @property (nonatomic, readwrite) JWTClaimsSet *claimsSet; 27 | @end 28 | 29 | // Protected? 30 | @protocol JWTMutableCodingResultTypeErrorProtocol 31 | @property (copy, nonatomic, readwrite) NSError *error; 32 | @end 33 | 34 | @interface JWTCodingResultTypeSuccess () @end 35 | 36 | @implementation JWTCodingResultTypeSuccess 37 | @synthesize encoded = _encoded; 38 | @synthesize headers = _headers; 39 | @synthesize payload = _payload; 40 | @synthesize claimsSet = _claimsSet; 41 | //Not used yet. Could be replacement for _encoded. 42 | @synthesize token = _token; 43 | 44 | - (NSDictionary *)headerAndPayloadDictionary { 45 | if (self.headers && self.payload) { 46 | return @{ 47 | JWTCodingResultHeaders: self.headers, 48 | JWTCodingResultPayload: self.payload 49 | }; 50 | } 51 | return nil; 52 | } 53 | - (instancetype)initWithEncoded:(NSString *)encoded { 54 | if (self = [super init]) { 55 | self.encoded = encoded; 56 | } 57 | return self; 58 | } 59 | - (instancetype)initWithToken:(NSString *)token { 60 | if (self = [super init]) { 61 | self.token = token; 62 | } 63 | return self; 64 | } 65 | - (instancetype)initWithHeaders:(NSDictionary *)headers withPayload:(NSDictionary *)payload { 66 | if (self = [super init]) { 67 | self.headers = headers; 68 | self.payload = payload; 69 | } 70 | return self; 71 | } 72 | 73 | - (instancetype)initWithHeadersAndPayload:(NSDictionary *)headersAndPayloadDictionary { 74 | NSDictionary *headers = headersAndPayloadDictionary[JWTCodingResultHeaders]; 75 | NSDictionary *payload = headersAndPayloadDictionary[JWTCodingResultPayload]; 76 | return [self initWithHeaders:headers withPayload:payload]; 77 | } 78 | 79 | - (instancetype)initWithClaimsSet:(JWTClaimsSet *)claimsSet { 80 | if (self = [super init]) { 81 | self.claimsSet = claimsSet; 82 | } 83 | return self; 84 | } 85 | @end 86 | 87 | @interface JWTCodingResultTypeError () @end 88 | 89 | @implementation JWTCodingResultTypeError 90 | @synthesize error = _error; 91 | - (instancetype)initWithError:(NSError *)error { 92 | if (self = [super init]) { 93 | self.error = error; 94 | } 95 | return self; 96 | } 97 | @end 98 | 99 | @interface JWTCodingResultType () 100 | @property (strong, nonatomic, readwrite) JWTCodingResultTypeSuccess *successResult; 101 | @property (strong, nonatomic, readwrite) JWTCodingResultTypeError *errorResult; 102 | @end 103 | 104 | @implementation JWTCodingResultType 105 | - (instancetype)initWithSuccessResult:(JWTCodingResultTypeSuccess *)success { 106 | if (self = [super init]) { 107 | self.successResult = success; 108 | } 109 | return self; 110 | } 111 | - (instancetype)initWithErrorResult:(JWTCodingResultTypeError *)error { 112 | if (self = [super init]) { 113 | self.errorResult = error; 114 | } 115 | return self; 116 | } 117 | 118 | @end 119 | -------------------------------------------------------------------------------- /src/ios/JWT/Core/Coding/CodePushJWTCoding+VersionThree.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTCoding+VersionThree.h 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 27.11.16. 6 | // Copyright © 2016 JWTIO. All rights reserved. 7 | // 8 | 9 | #import "CodePushJWTCoding.h" 10 | 11 | // encode and decode options 12 | @protocol JWTAlgorithm; 13 | @class JWTClaimsSet; 14 | @class JWTCodingBuilder; 15 | @class JWTEncodingBuilder; 16 | @class JWTDecodingBuilder; 17 | @class JWTAlgorithmDataHolderChain; 18 | @protocol JWTAlgorithmDataHolderProtocol; 19 | @class JWTCodingResultType; 20 | 21 | @interface JWT (VersionThree) 22 | + (JWTEncodingBuilder *)encodeWithHolders:(NSArray *)holders; 23 | + (JWTEncodingBuilder *)encodeWithChain:(JWTAlgorithmDataHolderChain *)chain; 24 | + (JWTDecodingBuilder *)decodeWithHolders:(NSArray *)holders; 25 | + (JWTDecodingBuilder *)decodeWithChain:(JWTAlgorithmDataHolderChain *)chain; 26 | @end 27 | 28 | @interface JWTCodingBuilder : NSObject 29 | #pragma mark - Create 30 | // each element should conform to JWTAlgorithmDataHolderProtocol 31 | + (instancetype)createWithHolders:(NSArray *)holders; 32 | + (instancetype)createWithChain:(JWTAlgorithmDataHolderChain *)chain; 33 | + (instancetype)createWithEmptyChain; 34 | 35 | #pragma mark - Internal 36 | @property (nonatomic, readonly) JWTAlgorithmDataHolderChain *internalChain; 37 | @property (copy, nonatomic, readonly) NSNumber *internalOptions; 38 | 39 | #pragma mark - Fluent 40 | @property (copy, nonatomic, readonly) JWTCodingBuilder *(^chain)(JWTAlgorithmDataHolderChain *chain); 41 | @property (copy, nonatomic, readonly) JWTCodingBuilder *(^constructChain)(JWTAlgorithmDataHolderChain *(^block)()); 42 | @property (copy, nonatomic, readonly) JWTCodingBuilder *(^modifyChain)(JWTAlgorithmDataHolderChain *(^block)(JWTAlgorithmDataHolderChain * chain)); 43 | @property (copy, nonatomic, readonly) JWTCodingBuilder *(^options)(NSNumber *options); 44 | @property (copy, nonatomic, readonly) JWTCodingBuilder *(^addHolder)(id holder); 45 | //@property (copy, nonatomic, readonly) JWTCodingBuilder *(^constructHolder)(id(^block)(id holder)); 46 | @end 47 | 48 | @interface JWTCodingBuilder (Sugar) 49 | - (instancetype)and; 50 | - (instancetype)with; 51 | @end 52 | 53 | @interface JWTCodingBuilder (Coding) 54 | @property (nonatomic, readonly) JWTCodingResultType *result; 55 | @end 56 | 57 | @interface JWTEncodingBuilder : JWTCodingBuilder 58 | #pragma mark - Create 59 | + (instancetype)encodePayload:(NSDictionary *)payload; 60 | + (instancetype)encodeClaimsSet:(JWTClaimsSet *)claimsSet; 61 | 62 | #pragma mark - Internal 63 | @property (copy, nonatomic, readonly) NSDictionary *internalPayload; 64 | @property (copy, nonatomic, readonly) NSDictionary *internalHeaders; 65 | @property (nonatomic, readonly) JWTClaimsSet *internalClaimsSet; 66 | 67 | #pragma mark - Fluent 68 | @property (copy, nonatomic, readonly) JWTEncodingBuilder *(^payload)(NSDictionary *payload); 69 | @property (copy, nonatomic, readonly) JWTEncodingBuilder *(^headers)(NSDictionary *headers); 70 | @property (copy, nonatomic, readonly) JWTEncodingBuilder *(^claimsSet)(JWTClaimsSet *claimsSet); 71 | 72 | @end 73 | 74 | @interface JWTEncodingBuilder (Coding) 75 | @property (nonatomic, readonly) JWTCodingResultType *encode; 76 | @end 77 | 78 | @interface JWTDecodingBuilder : JWTCodingBuilder 79 | #pragma mark - Create 80 | + (instancetype)decodeMessage:(NSString *)message; 81 | 82 | #pragma mark - Internal 83 | @property (copy, nonatomic, readonly) NSString *internalMessage; 84 | @property (nonatomic, readonly) JWTClaimsSet *internalClaimsSet; 85 | 86 | #pragma mark - Fluent 87 | @property (copy, nonatomic, readonly) JWTDecodingBuilder *(^message)(NSString *message); 88 | @property (copy, nonatomic, readonly) JWTDecodingBuilder *(^claimsSet)(JWTClaimsSet *claimsSet); 89 | 90 | @end 91 | 92 | @interface JWTDecodingBuilder (Coding) 93 | @property (nonatomic, readonly) JWTCodingResultType *decode; 94 | @end 95 | -------------------------------------------------------------------------------- /src/ios/JWT/Core/Coding/CodePushJWTCoding.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWT.h 3 | // JWT 4 | // 5 | // Created by Klaas Pieter Annema on 31-05-13. 6 | // Copyright (c) 2013 Karma. All rights reserved. 7 | // 8 | 9 | #import 10 | /** 11 | @discussion JWT is a general interface for decoding and encoding. 12 | Now it is to complex and fat to support. 13 | Possible solution: split interface into several pieces. 14 | 15 | JWT_1_0 -> JWT with plain old functions. 16 | JWT_2_0 -> JWT with builder usage. 17 | JWT_3_0 -> JWT with splitted apart algorithm data and payload data. 18 | */ 19 | @interface JWT : NSObject @end 20 | 21 | typedef NS_OPTIONS(NSInteger, JWTCodingDecodingOptions) { 22 | JWTCodingDecodingOptionsNone = 0, 23 | JWTCodingDecodingOptionsSkipVerification = 1 24 | }; 25 | -------------------------------------------------------------------------------- /src/ios/JWT/Core/Coding/CodePushJWTCoding.m: -------------------------------------------------------------------------------- 1 | // 2 | // JWT.m 3 | // JWT 4 | // 5 | // Created by Klaas Pieter Annema on 31-05-13. 6 | // Copyright (c) 2013 Karma. All rights reserved. 7 | // 8 | 9 | #import "CodePushJWTCoding.h" 10 | 11 | @implementation JWT @end 12 | -------------------------------------------------------------------------------- /src/ios/JWT/Core/FrameworkSupplement/CodePushJWT.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWT.h 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 23.10.16. 6 | // Copyright © 2016 Karma. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for JWT. 12 | FOUNDATION_EXPORT double JWTVersionNumber; 13 | 14 | //! Project version string for JWT. 15 | FOUNDATION_EXPORT const unsigned char JWTVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import "PublicHeader.h" 18 | 19 | // Coding 20 | #import "CodePushJWTCoding.h" 21 | #import "CodePushJWTCoding+ResultTypes.h" 22 | #import "CodePushJWTCoding+VersionOne.h" 23 | #import "CodePushJWTCoding+VersionTwo.h" 24 | #import "CodePushJWTCoding+VersionThree.h" 25 | 26 | // Algorithms 27 | #import "CodePushJWTAlgorithm.h" 28 | #import "CodePushJWTRSAlgorithm.h" 29 | #import "CodePushJWTAlgorithmFactory.h" 30 | #import "CodePushJWTAlgorithmNone.h" 31 | #import "CodePushJWTAlgorithmHSBase.h" 32 | #import "CodePushJWTAlgorithmRSBase.h" 33 | 34 | // Holders 35 | #import "CodePushJWTAlgorithmDataHolder.h" 36 | #import "CodePushJWTAlgorithmDataHolderChain.h" 37 | 38 | // Claims 39 | #import "CodePushJWTClaimsSet.h" 40 | #import "CodePushJWTClaim.h" 41 | #import "CodePushJWTClaimsSetSerializer.h" 42 | #import "CodePushJWTClaimsSetVerifier.h" 43 | 44 | // Supplement 45 | #import "CodePushJWTDeprecations.h" 46 | #import "CodePushJWTBase64Coder.h" 47 | #import "CodePushJWTErrorDescription.h" 48 | 49 | // Crypto 50 | #import "CodePushJWTCryptoKey.h" 51 | #import "CodePushJWTCryptoKeyExtractor.h" 52 | #import "CodePushJWTCryptoSecurity.h" 53 | -------------------------------------------------------------------------------- /src/ios/JWT/Core/Supplement/CodePushJWTBase64Coder.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTBase64Coder.h 3 | // Pods 4 | // 5 | // Created by Lobanov Dmitry on 05.10.16. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @protocol JWTStringCoder__Protocol 12 | - (NSString *)stringWithData:(NSData *)data; 13 | - (NSData *)dataWithString:(NSString *)string; 14 | @end 15 | 16 | @interface JWTBase64Coder : NSObject 17 | + (NSString *)base64UrlEncodedStringWithData:(NSData *)data; 18 | + (NSData *)dataWithBase64UrlEncodedString:(NSString *)urlEncodedString; 19 | @end 20 | 21 | @interface JWTBase64Coder (JWTStringCoder__Protocol) @end 22 | 23 | 24 | @interface JWTStringCoder__For__Encoding : NSObject 25 | @property (assign, nonatomic, readwrite) NSStringEncoding stringEncoding; 26 | + (instancetype)utf8Encoding; 27 | @end 28 | @interface JWTStringCoder__For__Encoding (JWTStringCoder__Protocol) @end 29 | -------------------------------------------------------------------------------- /src/ios/JWT/Core/Supplement/CodePushJWTBase64Coder.m: -------------------------------------------------------------------------------- 1 | // 2 | // JWTBase64Coder.m 3 | // Pods 4 | // 5 | // Created by Lobanov Dmitry on 05.10.16. 6 | // 7 | // 8 | 9 | #import "CodePushJWTBase64Coder.h" 10 | 11 | @interface JWTBase64Coder (ConditionLinking) 12 | + (BOOL)isBase64AddtionsAvailable; 13 | @end 14 | 15 | @implementation JWTBase64Coder (ConditionLinking) 16 | + (BOOL)isBase64AddtionsAvailable { 17 | return [[NSData class] respondsToSelector:@selector(dataWithBase64UrlEncodedString:)]; 18 | } 19 | @end 20 | 21 | #if __has_include() 22 | #import 23 | #elif __has_include("CodePushMF_Base64Additions.h") 24 | #import "CodePushMF_Base64Additions.h" 25 | #endif 26 | 27 | @implementation JWTBase64Coder 28 | 29 | + (NSString *)base64UrlEncodedStringWithData:(NSData *)data { 30 | if ([self isBase64AddtionsAvailable] && [data respondsToSelector:@selector(base64UrlEncodedString)]) { 31 | return [data performSelector:@selector(base64UrlEncodedString)]; 32 | } 33 | else { 34 | return [data base64EncodedStringWithOptions:0]; 35 | } 36 | } 37 | 38 | + (NSData *)dataWithBase64UrlEncodedString:(NSString *)urlEncodedString { 39 | if ([self isBase64AddtionsAvailable] && [[NSData class] respondsToSelector:@selector(dataWithBase64UrlEncodedString:)]) { 40 | return [[NSData class] performSelector:@selector(dataWithBase64UrlEncodedString:) withObject:urlEncodedString]; 41 | } 42 | else { 43 | return [[NSData alloc] initWithBase64EncodedString:urlEncodedString options:0]; 44 | } 45 | } 46 | 47 | + (NSData *)dataWithString:(NSString *)string { 48 | // check if base64. 49 | if (string == nil) { 50 | return nil; 51 | } 52 | 53 | // check that string is base64 encoded 54 | NSData *data = [[NSData alloc] initWithBase64EncodedString:string options:0]; 55 | 56 | NSString *stringToPass = data != nil ? string : [[string dataUsingEncoding:NSUTF8StringEncoding] base64EncodedStringWithOptions:0]; 57 | 58 | NSData *result = [self dataWithBase64UrlEncodedString:stringToPass]; 59 | return result; 60 | } 61 | 62 | + (NSString *)stringWithData:(NSData *)data { 63 | return [self.class base64UrlEncodedStringWithData:data]; 64 | } 65 | 66 | @end 67 | 68 | @implementation JWTBase64Coder (JWTStringCoder__Protocol) 69 | - (NSString *)stringWithData:(NSData *)data { 70 | return [self.class stringWithData:data]; 71 | } 72 | - (NSData *)dataWithString:(NSString *)string { 73 | return [self.class dataWithString:string]; 74 | } 75 | @end 76 | 77 | @implementation JWTStringCoder__For__Encoding 78 | + (instancetype)utf8Encoding { 79 | JWTStringCoder__For__Encoding *coding = [self new]; 80 | coding.stringEncoding = NSUTF8StringEncoding; 81 | return coding; 82 | } 83 | @end 84 | @implementation JWTStringCoder__For__Encoding (JWTStringCoder__Protocol) 85 | - (NSString *)stringWithData:(NSData *)data { 86 | return [[NSString alloc] initWithData:data encoding:self.stringEncoding]; 87 | } 88 | - (NSData *)dataWithString:(NSString *)string { 89 | return [string dataUsingEncoding:self.stringEncoding]; 90 | } 91 | @end 92 | -------------------------------------------------------------------------------- /src/ios/JWT/Core/Supplement/CodePushJWTDeprecations.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTDeprecations.h 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 31.08.16. 6 | // Copyright © 2016 Karma. All rights reserved. 7 | // 8 | 9 | #ifndef JWTDeprecations_h 10 | #define JWTDeprecations_h 11 | 12 | #define STR(str) #str 13 | #define JWTVersion_2_1_0 2.1 14 | #define JWTVersion_2_2_0 2.2 15 | #define JWTVersion_3_0_0 3.0 16 | #define __first_deprecated_in_release_version(version) __deprecated_msg("first deprecated in release version: " STR(version)) 17 | #define __deprecated_and_will_be_removed_in_release_version(version) __deprecated_msg("deprecated. will be removed in release version: "STR(version)) 18 | #define __available_in_release_version(version) __deprecated_msg("will be introduced in release version: " STR(version)) 19 | 20 | #define __jwt_technical_debt(debt) __deprecated_msg("Don't forget to inspect it later." STR(debt)) 21 | 22 | #endif /* JWTDeprecations_h */ 23 | -------------------------------------------------------------------------------- /src/ios/JWT/Core/Supplement/CodePushJWTErrorDescription.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTErrorDescription.h 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 27.11.16. 6 | // Copyright © 2016 JWTIO. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | extern NSString *JWTErrorDomain; 12 | 13 | typedef NS_ENUM(NSInteger, JWTError) { 14 | JWTInvalidFormatError = -100, 15 | JWTUnsupportedAlgorithmError, 16 | JWTAlgorithmNameMismatchError, 17 | JWTInvalidSignatureError, 18 | JWTNoPayloadError, 19 | JWTNoHeaderError, 20 | JWTEncodingHeaderError, 21 | JWTEncodingPayloadError, 22 | JWTEncodingSigningError, 23 | JWTClaimsSetVerificationFailed, 24 | JWTInvalidSegmentSerializationError, 25 | JWTUnspecifiedAlgorithmError, 26 | JWTBlacklistedAlgorithmError, 27 | JWTDecodingHeaderError, 28 | JWTDecodingPayloadError, 29 | JWTDecodingHoldersChainEmptyError 30 | }; 31 | 32 | @interface JWTErrorDescription : NSObject 33 | + (NSError *)errorWithCode:(JWTError)code; 34 | @end 35 | -------------------------------------------------------------------------------- /src/ios/JWT/Core/Supplement/CodePushJWTErrorDescription.m: -------------------------------------------------------------------------------- 1 | // 2 | // JWTErrorDescription.m 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 27.11.16. 6 | // Copyright © 2016 JWTIO. All rights reserved. 7 | // 8 | 9 | #import "CodePushJWTErrorDescription.h" 10 | NSString *JWTErrorDomain = @"io.jwt"; 11 | @implementation JWTErrorDescription 12 | + (NSDictionary *)userDescriptionsAndCodes { 13 | static NSDictionary *userDescriptionsAndCodes = nil; 14 | return userDescriptionsAndCodes ?: (userDescriptionsAndCodes = @{ 15 | @(JWTInvalidFormatError): @"Invalid format! Try to check your encoding algorithm. Maybe you put too many dots as delimiters?", 16 | @(JWTUnsupportedAlgorithmError): @"Unsupported algorithm! You could implement it by yourself", 17 | @(JWTAlgorithmNameMismatchError) : @"Algorithm doesn't match name in header.", 18 | @(JWTInvalidSignatureError): @"Invalid signature! It seems that signed part of jwt mismatch generated part by algorithm provided in header.", 19 | @(JWTNoPayloadError): @"No payload! Hey, forget payload?", 20 | @(JWTNoHeaderError): @"No header! Hmm", 21 | @(JWTEncodingHeaderError): @"It seems that header encoding failed", 22 | @(JWTEncodingPayloadError): @"It seems that payload encoding failed", 23 | @(JWTEncodingSigningError): @"It seems that signing output corrupted. Make sure signing worked (e.g. we may have issues extracting the key from the PKCS12 bundle if passphrase is incorrect).", 24 | @(JWTClaimsSetVerificationFailed): @"It seems that claims verification failed", 25 | @(JWTInvalidSegmentSerializationError): @"It seems that json serialization failed for segment", 26 | @(JWTUnspecifiedAlgorithmError): @"Unspecified algorithm! You must explicitly choose an algorithm to decode with.", 27 | @(JWTBlacklistedAlgorithmError): @"Algorithm in blacklist? Try to check whitelist parameter", 28 | @(JWTDecodingHeaderError): @"Error decoding the JWT Header segment.", 29 | @(JWTDecodingPayloadError): @"Error decoding the JWT Payload segment.", 30 | @(JWTDecodingHoldersChainEmptyError) : @"Error decoding the JWT algorithm and data holders chain is empty!" 31 | }, userDescriptionsAndCodes); 32 | } 33 | 34 | + (NSDictionary *)errorDescriptionsAndCodes { 35 | static NSDictionary *errorDescriptionsAndCodes = nil; 36 | return errorDescriptionsAndCodes ?: (errorDescriptionsAndCodes = @{ 37 | @(JWTInvalidFormatError): @"JWTInvalidFormatError", 38 | @(JWTUnsupportedAlgorithmError): @"JWTUnsupportedAlgorithmError", 39 | @(JWTAlgorithmNameMismatchError) :@"JWTAlgorithmNameMismatchError", 40 | @(JWTInvalidSignatureError): @"JWTInvalidSignatureError", 41 | @(JWTNoPayloadError): @"JWTNoPayloadError", 42 | @(JWTNoHeaderError): @"JWTNoHeaderError", 43 | @(JWTEncodingHeaderError): @"JWTEncodingHeaderError", 44 | @(JWTEncodingPayloadError): @"JWTEncodingPayloadError", 45 | @(JWTEncodingSigningError): @"JWTEncodingSigningError", 46 | @(JWTClaimsSetVerificationFailed): @"JWTClaimsSetVerificationFailed", 47 | @(JWTInvalidSegmentSerializationError): @"JWTInvalidSegmentSerializationError", 48 | @(JWTUnspecifiedAlgorithmError): @"JWTUnspecifiedAlgorithmError", 49 | @(JWTBlacklistedAlgorithmError): @"JWTBlacklistedAlgorithmError", 50 | @(JWTDecodingHeaderError): @"JWTDecodingHeaderError", 51 | @(JWTDecodingPayloadError): @"JWTDecodingPayloadError", 52 | @(JWTDecodingHoldersChainEmptyError) :@"JWTDecodingHoldersChainEmptyError" 53 | }, errorDescriptionsAndCodes); 54 | } 55 | 56 | + (NSString *)userDescriptionForCode:(JWTError)code { 57 | NSString *resultString = [self userDescriptionsAndCodes][@(code)]; 58 | return resultString ?: @"Unexpected error"; 59 | } 60 | 61 | + (NSString *)errorDescriptionForCode:(JWTError)code { 62 | NSString *resultString = [self errorDescriptionsAndCodes][@(code)]; 63 | return resultString ?: @"JWTUnexpectedError"; 64 | } 65 | 66 | + (NSError *)errorWithCode:(JWTError)code { 67 | return [self errorWithCode:code withUserDescription:[self userDescriptionForCode:code] withErrorDescription:[self errorDescriptionForCode:code]]; 68 | } 69 | 70 | + (NSError *)errorWithCode:(NSInteger)code withUserDescription:(NSString *)userDescription withErrorDescription:(NSString *)errorDescription { 71 | return [NSError errorWithDomain:JWTErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey: userDescription, @"errorDescription": errorDescription}]; 72 | } 73 | @end 74 | -------------------------------------------------------------------------------- /src/ios/JWT/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Karma Mobility, Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /src/ios/StatusReport.h: -------------------------------------------------------------------------------- 1 | enum { 2 | STORE_VERSION = 0, 3 | UPDATE_CONFIRMED = 1, 4 | UPDATE_ROLLED_BACK = 2 5 | }; 6 | typedef NSInteger ReportingStatus; 7 | 8 | @interface StatusReport : NSObject 9 | 10 | @property ReportingStatus status; 11 | @property NSString* label; 12 | @property NSString* appVersion; 13 | @property NSString* deploymentKey; 14 | 15 | // Optional properties. 16 | @property NSString* lastVersionLabelOrAppVersion; 17 | @property NSString* lastVersionDeploymentKey; 18 | 19 | - (id)initWithStatus:(ReportingStatus)status andLabel:(NSString*)label andAppVersion:(NSString*)appVersion andDeploymentKey:(NSString*)deploymentKey; 20 | 21 | - (id)initWithStatus:(ReportingStatus)status andLabel:(NSString*)label andAppVersion:(NSString*)appVersion andDeploymentKey:(NSString*)deploymentKey andLastVersionLabelOrAppVersion:(NSString*)lastVersionLabelOrAppVersion andLastVersionDeploymentKey:(NSString*)lastVersionDeploymentKey; 22 | 23 | - (id)initWithDictionary:(NSDictionary*)dict; 24 | 25 | - (NSDictionary*)toDictionary; 26 | 27 | @end -------------------------------------------------------------------------------- /src/ios/StatusReport.m: -------------------------------------------------------------------------------- 1 | #import "StatusReport.h" 2 | 3 | @implementation StatusReport 4 | 5 | const NSString* StatusKey = @"status"; 6 | const NSString* LabelKey = @"label"; 7 | const NSString* AppVersionKey = @"appVersion"; 8 | const NSString* DeploymentKeyKey = @"deploymentKey"; 9 | const NSString* LastVersionLabelOrAppVersionKey = @"lastVersionLabelOrAppVersion"; 10 | const NSString* LastVersionDeploymentKeyKey = @"lastVersionDeploymentKey"; 11 | 12 | - (id)initWithStatus:(ReportingStatus)status andLabel:(NSString*)label andAppVersion:(NSString*)appVersion andDeploymentKey:(NSString*)deploymentKey { 13 | return [self initWithStatus:status andLabel:label andAppVersion:appVersion andDeploymentKey:deploymentKey andLastVersionLabelOrAppVersion:nil andLastVersionDeploymentKey:nil]; 14 | } 15 | 16 | 17 | - (id)initWithStatus:(ReportingStatus)status andLabel:(NSString*)label andAppVersion:(NSString*)appVersion andDeploymentKey:(NSString*)deploymentKey andLastVersionLabelOrAppVersion:(NSString*)lastVersionLabelOrAppVersion andLastVersionDeploymentKey:(NSString*)lastVersionDeploymentKey { 18 | self = [super init]; 19 | if (self) { 20 | _status = status; 21 | _label = label; 22 | _appVersion = appVersion; 23 | _deploymentKey = deploymentKey; 24 | _lastVersionLabelOrAppVersion = lastVersionLabelOrAppVersion; 25 | _lastVersionDeploymentKey = lastVersionDeploymentKey; 26 | } 27 | 28 | return self; 29 | } 30 | 31 | - (id)initWithDictionary:(NSDictionary*)dict { 32 | return [self initWithStatus:[dict[StatusKey] longValue] andLabel:dict[LabelKey] andAppVersion:dict[AppVersionKey] andDeploymentKey:dict[DeploymentKeyKey] andLastVersionLabelOrAppVersion:dict[LastVersionLabelOrAppVersionKey] andLastVersionDeploymentKey:dict[LastVersionDeploymentKeyKey]]; 33 | } 34 | 35 | - (NSDictionary*)toDictionary { 36 | NSMutableDictionary* dict = [[NSMutableDictionary alloc] init]; 37 | dict[StatusKey] = @(_status); 38 | if (_label) dict[LabelKey] = _label; 39 | if (_appVersion) dict[AppVersionKey] = _appVersion; 40 | if (_deploymentKey) dict[DeploymentKeyKey] = _deploymentKey; 41 | if (_lastVersionLabelOrAppVersion) dict[LastVersionLabelOrAppVersionKey] = _lastVersionLabelOrAppVersion; 42 | if (_lastVersionDeploymentKey) dict[LastVersionDeploymentKeyKey] = _lastVersionDeploymentKey; 43 | return dict; 44 | } 45 | 46 | @end -------------------------------------------------------------------------------- /src/ios/UpdateHashUtils.h: -------------------------------------------------------------------------------- 1 | @interface UpdateHashUtils : NSObject 2 | 3 | + (NSString*)getBinaryHash:(NSError**)error; 4 | + (NSString*)getHashForPath:(NSString*)path error:(NSError**)error; 5 | 6 | @end -------------------------------------------------------------------------------- /src/ios/UpdateHashUtils.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import "UpdateHashUtils.h" 3 | 4 | @implementation UpdateHashUtils : NSObject 5 | 6 | + (NSArray *)ignoredFilenames { 7 | return @[ 8 | @".codepushrelease", 9 | @".DS_Store", 10 | @"__MACOSX" 11 | ]; 12 | } 13 | 14 | + (NSString*)binaryAssetsPath 15 | { 16 | return [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"www"]; 17 | } 18 | 19 | + (void)addFolderEntriesToManifest:(NSString*)folderPath 20 | pathPrefix:(NSString*)pathPrefix 21 | manifestEntries:(NSMutableArray*)manifestEntries 22 | error:(NSError**)error 23 | { 24 | NSArray* folderFiles = [[NSFileManager defaultManager] 25 | contentsOfDirectoryAtPath:folderPath 26 | error:error]; 27 | if (*error) { 28 | return; 29 | } 30 | 31 | for (NSString* fileName in folderFiles) { 32 | @autoreleasepool { 33 | if ([[self ignoredFilenames] containsObject:fileName]) { 34 | continue; 35 | } 36 | NSString* fullFilePath = [folderPath stringByAppendingPathComponent:fileName]; 37 | NSString* relativePath = [pathPrefix stringByAppendingPathComponent:fileName]; 38 | BOOL isDir = NO; 39 | if ([[NSFileManager defaultManager] fileExistsAtPath:fullFilePath 40 | isDirectory:&isDir] && isDir) { 41 | [self addFolderEntriesToManifest:fullFilePath 42 | pathPrefix:relativePath 43 | manifestEntries:manifestEntries 44 | error:error]; 45 | if (*error) { 46 | return; 47 | } 48 | } else { 49 | NSData* fileContents = [NSData dataWithContentsOfFile:fullFilePath]; 50 | NSString* fileContentsHash = [self computeHashForData:fileContents]; 51 | [manifestEntries addObject:[[relativePath stringByAppendingString:@":"] stringByAppendingString:fileContentsHash]]; 52 | } 53 | } 54 | } 55 | } 56 | 57 | + (NSString*)computeFinalHashFromManifestEntries:(NSMutableArray*)manifest 58 | error:(NSError**)error 59 | { 60 | NSArray* sortedManifest = [manifest sortedArrayUsingSelector:@selector(compare:)]; 61 | NSData* manifestData = [NSJSONSerialization dataWithJSONObject:sortedManifest 62 | options:kNilOptions 63 | error:error]; 64 | if (*error) { 65 | return nil; 66 | } 67 | 68 | NSString* manifestString = [[NSString alloc] initWithData:manifestData 69 | encoding:NSUTF8StringEncoding]; 70 | // The JSON serialization turns path separators into "\/", e.g. "www\/images\/image.png" 71 | manifestString = [manifestString stringByReplacingOccurrencesOfString:@"\\/" 72 | withString:@"/"]; 73 | return [self computeHashForData:[NSData dataWithBytes:manifestString.UTF8String length:manifestString.length]]; 74 | } 75 | 76 | + (NSString*)computeHashForData:(NSData*)inputData 77 | { 78 | uint8_t digest[CC_SHA256_DIGEST_LENGTH]; 79 | CC_SHA256(inputData.bytes, (CC_LONG)inputData.length, digest); 80 | NSMutableString* inputHash = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2]; 81 | for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) { 82 | [inputHash appendFormat:@"%02x", digest[i]]; 83 | } 84 | 85 | return inputHash; 86 | } 87 | 88 | + (NSString*)getBinaryHash:(NSError**)error 89 | { 90 | return [self getHashForPath:[self binaryAssetsPath] error:error]; 91 | } 92 | 93 | + (NSString*)getHashForPath:(NSString*)path error:(NSError**)error 94 | { 95 | NSMutableArray* manifestEntries = [NSMutableArray array]; 96 | [self addFolderEntriesToManifest:path 97 | pathPrefix:@"www" 98 | manifestEntries:manifestEntries 99 | error:error]; 100 | return [self computeFinalHashFromManifestEntries:manifestEntries error:error]; 101 | } 102 | 103 | @end 104 | -------------------------------------------------------------------------------- /src/ios/Utilities.h: -------------------------------------------------------------------------------- 1 | @interface Utilities : NSObject 2 | 3 | + (NSString*)getApplicationVersion; 4 | + (NSString*)getApplicationTimestamp; 5 | + (NSDate*)getApplicationBuildTime; 6 | + (BOOL)CDVWebViewEngineAvailable; 7 | 8 | @end -------------------------------------------------------------------------------- /src/ios/Utilities.m: -------------------------------------------------------------------------------- 1 | #import "Utilities.h" 2 | 3 | @implementation Utilities 4 | 5 | static NSNumber* CDVWebViewEngineExists = nil; 6 | 7 | + (NSString*)getApplicationVersion{ 8 | return [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; 9 | } 10 | 11 | + (NSString*)getApplicationTimestamp{ 12 | NSDate* applicationBuildTime = [self getApplicationBuildTime]; 13 | if (applicationBuildTime){ 14 | NSNumber* timestamp = [[NSNumber alloc] initWithDouble: floor([applicationBuildTime timeIntervalSince1970] * 1000)]; 15 | return [timestamp stringValue]; 16 | } 17 | 18 | return nil; 19 | } 20 | 21 | + (NSDate*)getApplicationBuildTime{ 22 | //get path for plist file to check modification date because iOS10.2 failed to get modification date of main bundle 23 | NSString *appPlistPath = [[NSBundle mainBundle] pathForResource:nil ofType:@"plist"]; 24 | NSDictionary *executableAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:appPlistPath error:nil]; 25 | NSDate *fileDate = [executableAttributes objectForKey:@"NSFileModificationDate"]; 26 | return fileDate; 27 | } 28 | 29 | + (BOOL)CDVWebViewEngineAvailable{ 30 | if(CDVWebViewEngineExists == nil) { 31 | BOOL value = NSClassFromString(@"CDVWebViewEngine") != nil; 32 | CDVWebViewEngineExists = [NSNumber numberWithBool:value]; 33 | } 34 | return [CDVWebViewEngineExists boolValue]; 35 | } 36 | 37 | void CPLog(NSString *formatString, ...) { 38 | va_list args; 39 | va_start(args, formatString); 40 | NSString *prependedFormatString = [NSString stringWithFormat:@"\n[CodePush] %@", formatString]; 41 | NSLogv(prependedFormatString, args); 42 | va_end(args); 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /test/serverUtil.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * Class used to mock the codePush.checkForUpdate() response from the server. 5 | */ 6 | export class CheckForUpdateResponseMock { 7 | download_url: string; 8 | is_available: boolean; 9 | is_disabled: boolean; 10 | package_size: number; 11 | update_app_version: boolean; 12 | target_binary_range: string; 13 | description: string; 14 | label: string; 15 | package_hash: string; 16 | should_run_binary_version: boolean; 17 | is_mandatory: boolean; 18 | } 19 | 20 | /** 21 | * The model class of the codePush.checkForUpdate() request to the server. 22 | */ 23 | export class UpdateCheckRequestMock { 24 | deploymentKey: string; 25 | target_binary_range: string; 26 | package_hash: string; 27 | isCompanion: boolean; 28 | } 29 | 30 | /** 31 | * Contains all the messages sent from the application to the mock server during tests. 32 | */ 33 | export class TestMessage { 34 | public static CHECK_UP_TO_DATE = "CHECK_UP_TO_DATE"; 35 | public static CHECK_UPDATE_AVAILABLE = "CHECK_UPDATE_AVAILABLE"; 36 | public static CHECK_ERROR = "CHECK_ERROR"; 37 | public static DOWNLOAD_SUCCEEDED = "DOWNLOAD_SUCCEEDED"; 38 | public static DOWNLOAD_ERROR = "DOWNLOAD_ERROR"; 39 | public static UPDATE_INSTALLED = "UPDATE_INSTALLED"; 40 | public static INSTALL_ERROR = "INSTALL_ERROR"; 41 | public static DEVICE_READY_AFTER_UPDATE = "DEVICE_READY_AFTER_UPDATE"; 42 | public static UPDATE_FAILED_PREVIOUSLY = "UPDATE_FAILED_PREVIOUSLY"; 43 | public static NOTIFY_APP_READY_SUCCESS = "NOTIFY_APP_READY_SUCCESS"; 44 | public static NOTIFY_APP_READY_FAILURE = "NOTIFY_APP_READY_FAILURE"; 45 | public static SKIPPED_NOTIFY_APPLICATION_READY = "SKIPPED_NOTIFY_APPLICATION_READY"; 46 | public static SYNC_STATUS = "SYNC_STATUS"; 47 | public static RESTART_SUCCEEDED = "RESTART_SUCCEEDED"; 48 | public static RESTART_FAILED = "RESTART_FAILED"; 49 | public static PENDING_PACKAGE = "PENDING_PACKAGE"; 50 | public static CURRENT_PACKAGE = "CURRENT_PACKAGE"; 51 | 52 | public static SYNC_UP_TO_DATE = 0; 53 | public static SYNC_UPDATE_INSTALLED = 1; 54 | public static SYNC_UPDATE_IGNORED = 2; 55 | public static SYNC_ERROR = 3; 56 | public static SYNC_IN_PROGRESS = 4; 57 | public static SYNC_CHECKING_FOR_UPDATE = 5; 58 | public static SYNC_AWAITING_USER_ACTION = 6; 59 | public static SYNC_DOWNLOADING_PACKAGE = 7; 60 | public static SYNC_INSTALLING_UPDATE = 8; 61 | } 62 | 63 | /** 64 | * Contains all the messages sent from the mock server back to the application during tests. 65 | */ 66 | export class TestMessageResponse { 67 | public static SKIP_NOTIFY_APPLICATION_READY = "SKIP_NOTIFY_APPLICATION_READY"; 68 | } 69 | 70 | /** 71 | * Defines the messages sent from the application to the mock server during tests. 72 | */ 73 | export class AppMessage { 74 | message: string; 75 | args: any[]; 76 | 77 | constructor(message: string, args: any[]) { 78 | this.message = message; 79 | this.args = args; 80 | } 81 | 82 | static fromString(message: string): AppMessage { 83 | return new AppMessage(message, undefined); 84 | } 85 | } 86 | 87 | /** 88 | * Checks if two messages are equal. 89 | */ 90 | export function areEqual(m1: AppMessage, m2: AppMessage): boolean { 91 | /* compare objects */ 92 | if (m1 === m2) { 93 | return true; 94 | } 95 | 96 | /* compare messages */ 97 | if (!m1 || !m2 || m1.message !== m2.message) { 98 | return false; 99 | } 100 | 101 | /* compare arguments */ 102 | if (m1.args === m2.args) { 103 | return true; 104 | } 105 | 106 | if (!m1.args || !m2.args || m1.args.length !== m2.args.length) { 107 | return false; 108 | } 109 | 110 | for (var i = 0; i < m1.args.length; i++) { 111 | if (m1.args[i] !== m2.args[i]) { 112 | return false; 113 | } 114 | } 115 | 116 | return true; 117 | } 118 | -------------------------------------------------------------------------------- /test/template/build.json: -------------------------------------------------------------------------------- 1 | { "ios": { "debug": { "buildFlag": [ "-UseModernBuildSystem=0" ] }, "release": { "buildFlag": [ "-UseModernBuildSystem=0" ] } } } 2 | -------------------------------------------------------------------------------- /test/template/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | CodePushTest 4 | 5 | A sample Apache Cordova application that is used for testing the CodePush plugin. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 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 | -------------------------------------------------------------------------------- /test/template/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.microsoft.codepush.test", 3 | "displayName": "CodePushTest", 4 | "description": "A sample Apache Cordova application that is used for testing the CodePush plugin.", 5 | "keywords": [ 6 | "ecosystem:cordova" 7 | ], 8 | "author": "Microsoft Corporation", 9 | "license": "MIT" 10 | } 11 | -------------------------------------------------------------------------------- /test/template/www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Hello World 9 | 10 | 11 | 12 |
13 |

Hello, CodePush (CODE_PUSH_APP_VERSION)

14 |

Waiting for device...

15 |
16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /test/template/www/js/scenarioCheckForUpdate.js: -------------------------------------------------------------------------------- 1 | var app = { 2 | // Application Constructor 3 | initialize: function () { 4 | this.bindEvents(); 5 | }, 6 | bindEvents: function () { 7 | document.addEventListener('deviceready', this.onDeviceReady, false); 8 | }, 9 | onDeviceReady: function () { 10 | app.receivedDeviceReady(); 11 | app.checkForUpdates(); 12 | }, 13 | // Update DOM on a Received Event 14 | receivedDeviceReady: function () { 15 | document.getElementById("deviceready").innerText = "Device is ready (scenario - check for update)"; 16 | console.log('Received Event: deviceready'); 17 | }, 18 | checkForUpdates: function () { 19 | console.log("Checking for updates..."); 20 | window.codePush.checkForUpdate(app.checkSuccess, app.checkError); 21 | }, 22 | checkSuccess: function (remotePackage) { 23 | if (!remotePackage) { 24 | // A null remotePackage means that the server successfully responded, but there is no update available. 25 | console.log("The application is up to date."); 26 | app.sendTestMessage("CHECK_UP_TO_DATE"); 27 | } 28 | else { 29 | console.log("There is an update available. Remote package:" + JSON.stringify(remotePackage)); 30 | app.sendTestMessage("CHECK_UPDATE_AVAILABLE", [remotePackage]); 31 | } 32 | }, 33 | checkError: function (error) { 34 | console.log("An error occured while checking for updates."); 35 | app.sendTestMessage("CHECK_ERROR"); 36 | }, 37 | sendTestMessage: function (message, args) { 38 | var xhr = new XMLHttpRequest(); 39 | xhr.open("POST", "CODE_PUSH_SERVER_URL/reportTestMessage", false); 40 | var body = JSON.stringify({ message: message, args: args }); 41 | console.log("Sending test message body: " + body); 42 | 43 | xhr.setRequestHeader("Content-type", "application/json"); 44 | 45 | xhr.send(body); 46 | } 47 | }; 48 | 49 | app.initialize(); -------------------------------------------------------------------------------- /test/template/www/js/scenarioCheckForUpdateCustomKey.js: -------------------------------------------------------------------------------- 1 | var app = { 2 | // Application Constructor 3 | initialize: function () { 4 | this.bindEvents(); 5 | }, 6 | bindEvents: function () { 7 | document.addEventListener('deviceready', this.onDeviceReady, false); 8 | }, 9 | onDeviceReady: function () { 10 | app.receivedDeviceReady(); 11 | app.checkForUpdates(); 12 | }, 13 | // Update DOM on a Received Event 14 | receivedDeviceReady: function () { 15 | document.getElementById("deviceready").innerText = "Device is ready (scenario - check for update)"; 16 | console.log('Received Event: deviceready'); 17 | }, 18 | checkForUpdates: function () { 19 | console.log("Checking for updates..."); 20 | window.codePush.checkForUpdate(app.checkSuccess, app.checkError, "CUSTOM-DEPLOYMENT-KEY"); 21 | }, 22 | checkSuccess: function (remotePackage) { 23 | if (!remotePackage) { 24 | // A null remotePackage means that the server successfully responded, but there is no update available. 25 | console.log("The application is up to date."); 26 | app.sendTestMessage("CHECK_UP_TO_DATE"); 27 | } 28 | else { 29 | console.log("There is an update available. Remote package:" + JSON.stringify(remotePackage)); 30 | app.sendTestMessage("CHECK_UPDATE_AVAILABLE", [remotePackage]); 31 | } 32 | }, 33 | checkError: function (error) { 34 | console.log("An error occured while checking for updates."); 35 | app.sendTestMessage("CHECK_ERROR"); 36 | }, 37 | sendTestMessage: function (message, args) { 38 | var xhr = new XMLHttpRequest(); 39 | xhr.open("POST", "CODE_PUSH_SERVER_URL/reportTestMessage", false); 40 | var body = JSON.stringify({ message: message, args: args }); 41 | console.log("Sending test message body: " + body); 42 | 43 | xhr.setRequestHeader("Content-type", "application/json"); 44 | 45 | xhr.send(body); 46 | } 47 | }; 48 | 49 | app.initialize(); -------------------------------------------------------------------------------- /test/template/www/js/scenarioDownloadUpdate.js: -------------------------------------------------------------------------------- 1 | var app = { 2 | // Application Constructor 3 | initialize: function () { 4 | this.bindEvents(); 5 | }, 6 | bindEvents: function () { 7 | document.addEventListener('deviceready', this.onDeviceReady, false); 8 | }, 9 | onDeviceReady: function () { 10 | app.receivedDeviceReady(); 11 | app.checkForUpdates(); 12 | }, 13 | // Update DOM on a Received Event 14 | receivedDeviceReady: function () { 15 | document.getElementById("deviceready").innerText = "Device is ready (scenario - download update)"; 16 | console.log('Received Event: deviceready'); 17 | }, 18 | checkForUpdates: function () { 19 | console.log("Checking for updates..."); 20 | window.codePush.checkForUpdate(app.checkSuccess, app.checkError); 21 | }, 22 | checkSuccess: function (remotePackage) { 23 | if (!remotePackage) { 24 | // A null remotePackage means that the server successfully responded, but there is no update available. 25 | console.log("The application is up to date."); 26 | app.sendTestMessage("CHECK_UP_TO_DATE"); 27 | } 28 | else { 29 | console.log("There is an update available. Remote package:" + JSON.stringify(remotePackage)); 30 | console.log("Downloading package..."); 31 | remotePackage.download(app.downloadSuccess, app.downloadError); 32 | } 33 | }, 34 | checkError: function (error) { 35 | console.log("An error occured while checking for updates."); 36 | app.sendTestMessage("CHECK_ERROR"); 37 | }, 38 | downloadSuccess: function (localPackage) { 39 | console.log("Download succeeded."); 40 | app.sendTestMessage("DOWNLOAD_SUCCEEDED", [localPackage]); 41 | }, 42 | downloadError: function (error) { 43 | console.log("Download error."); 44 | app.sendTestMessage("DOWNLOAD_ERROR"); 45 | }, 46 | sendTestMessage: function (message, args) { 47 | var xhr = new XMLHttpRequest(); 48 | xhr.open("POST", "CODE_PUSH_SERVER_URL/reportTestMessage", false); 49 | var body = JSON.stringify({ message: message, args: args }); 50 | console.log("Sending test message body: " + body); 51 | 52 | xhr.setRequestHeader("Content-type", "application/json"); 53 | 54 | xhr.send(body); 55 | } 56 | }; 57 | 58 | app.initialize(); -------------------------------------------------------------------------------- /test/template/www/js/scenarioInstall.js: -------------------------------------------------------------------------------- 1 | var app = { 2 | // Application Constructor 3 | initialize: function () { 4 | this.bindEvents(); 5 | }, 6 | bindEvents: function () { 7 | document.addEventListener('deviceready', this.onDeviceReady, false); 8 | }, 9 | onDeviceReady: function () { 10 | app.receivedDeviceReady(); 11 | app.checkForUpdates(); 12 | }, 13 | // Update DOM on a Received Event 14 | receivedDeviceReady: function () { 15 | document.getElementById("deviceready").innerText = "Device is ready (scenario - install immediately)"; 16 | console.log('Received Event: deviceready'); 17 | }, 18 | checkForUpdates: function () { 19 | console.log("Checking for updates..."); 20 | window.codePush.checkForUpdate(app.checkSuccess, app.checkError); 21 | }, 22 | checkSuccess: function (remotePackage) { 23 | if (!remotePackage) { 24 | // A null remotePackage means that the server successfully responded, but there is no update available. 25 | console.log("The application is up to date."); 26 | app.sendTestMessage("CHECK_UP_TO_DATE"); 27 | } 28 | else { 29 | console.log("There is an update available. Remote package:" + JSON.stringify(remotePackage)); 30 | console.log("Downloading package..."); 31 | remotePackage.download(app.downloadSuccess, app.downloadError); 32 | } 33 | }, 34 | checkError: function (error) { 35 | console.log("An error occured while checking for updates."); 36 | app.sendTestMessage("CHECK_ERROR"); 37 | }, 38 | downloadSuccess: function (localPackage) { 39 | console.log("Download succeeded."); 40 | localPackage.install(app.installSuccess, app.installError, { installMode: InstallMode.IMMEDIATE }); 41 | }, 42 | downloadError: function (error) { 43 | console.log("Download error."); 44 | app.sendTestMessage("DOWNLOAD_ERROR"); 45 | }, 46 | installSuccess: function () { 47 | console.log("Update installed."); 48 | app.sendTestMessage("UPDATE_INSTALLED"); 49 | }, 50 | installError: function (error) { 51 | console.log("Install error."); 52 | app.sendTestMessage("INSTALL_ERROR"); 53 | }, 54 | sendTestMessage: function (message, args) { 55 | var xhr = new XMLHttpRequest(); 56 | xhr.open("POST", "CODE_PUSH_SERVER_URL/reportTestMessage", false); 57 | var body = JSON.stringify({ message: message, args: args }); 58 | console.log("Sending test message body: " + body); 59 | 60 | xhr.setRequestHeader("Content-type", "application/json"); 61 | 62 | xhr.send(body); 63 | } 64 | }; 65 | 66 | app.initialize(); -------------------------------------------------------------------------------- /test/template/www/js/scenarioInstallOnRestart2xWithRevert.js: -------------------------------------------------------------------------------- 1 | var app = { 2 | // Application Constructor 3 | initialize: function () { 4 | this.bindEvents(); 5 | }, 6 | bindEvents: function () { 7 | document.addEventListener('deviceready', this.onDeviceReady, false); 8 | }, 9 | onDeviceReady: function () { 10 | app.receivedDeviceReady(); 11 | app.checkForUpdates(); 12 | }, 13 | // Update DOM on a Received Event 14 | receivedDeviceReady: function () { 15 | document.getElementById("deviceready").innerText = "Device is ready (scenario - install on next restart 2x)"; 16 | console.log('Received Event: deviceready'); 17 | }, 18 | checkForUpdates: function () { 19 | console.log("Checking for updates..."); 20 | window.codePush.checkForUpdate(app.checkSuccess, app.checkError); 21 | }, 22 | checkSuccess: function (remotePackage) { 23 | if (!remotePackage) { 24 | // A null remotePackage means that the server successfully responded, but there is no update available. 25 | console.log("The application is up to date."); 26 | app.sendTestMessage("CHECK_UP_TO_DATE"); 27 | } 28 | else { 29 | if (remotePackage.failedInstall) { 30 | app.sendTestMessage("UPDATE_FAILED_PREVIOUSLY"); 31 | } else { 32 | console.log("There is an update available. Remote package:" + JSON.stringify(remotePackage)); 33 | console.log("Downloading package..."); 34 | remotePackage.download(app.downloadSuccess, app.downloadError); 35 | } 36 | } 37 | }, 38 | checkError: function (error) { 39 | console.log("An error occured while checking for updates."); 40 | app.sendTestMessage("CHECK_ERROR"); 41 | }, 42 | downloadSuccess: function (localPackage) { 43 | console.log("Download succeeded."); 44 | localPackage.install(app.installSuccess, app.installError, { installMode: InstallMode.ON_NEXT_RESTART }); 45 | }, 46 | downloadError: function (error) { 47 | console.log("Download error."); 48 | app.sendTestMessage("DOWNLOAD_ERROR"); 49 | }, 50 | installSuccess: function () { 51 | console.log("Update installed."); 52 | app.sendTestMessage("UPDATE_INSTALLED"); 53 | window.codePush.checkForUpdate(app.checkSuccess, app.checkError); 54 | }, 55 | installError: function (error) { 56 | console.log("Install error."); 57 | app.sendTestMessage("INSTALL_ERROR"); 58 | }, 59 | sendTestMessage: function (message, args) { 60 | var xhr = new XMLHttpRequest(); 61 | xhr.open("POST", "CODE_PUSH_SERVER_URL/reportTestMessage", false); 62 | var body = JSON.stringify({ message: message, args: args }); 63 | console.log("Sending test message body: " + body); 64 | 65 | xhr.setRequestHeader("Content-type", "application/json"); 66 | 67 | xhr.send(body); 68 | } 69 | }; 70 | 71 | app.initialize(); -------------------------------------------------------------------------------- /test/template/www/js/scenarioInstallOnRestartWithRevert.js: -------------------------------------------------------------------------------- 1 | var app = { 2 | // Application Constructor 3 | initialize: function () { 4 | this.bindEvents(); 5 | }, 6 | bindEvents: function () { 7 | document.addEventListener('deviceready', this.onDeviceReady, false); 8 | }, 9 | onDeviceReady: function () { 10 | app.receivedDeviceReady(); 11 | app.checkForUpdates(); 12 | }, 13 | // Update DOM on a Received Event 14 | receivedDeviceReady: function () { 15 | document.getElementById("deviceready").innerText = "Device is ready (scenario - install on next restart)"; 16 | console.log('Received Event: deviceready'); 17 | }, 18 | checkForUpdates: function () { 19 | console.log("Checking for updates..."); 20 | window.codePush.checkForUpdate(app.checkSuccess, app.checkError); 21 | }, 22 | checkSuccess: function (remotePackage) { 23 | if (!remotePackage) { 24 | // A null remotePackage means that the server successfully responded, but there is no update available. 25 | console.log("The application is up to date."); 26 | app.sendTestMessage("CHECK_UP_TO_DATE"); 27 | } 28 | else { 29 | if (remotePackage.failedInstall) { 30 | app.sendTestMessage("UPDATE_FAILED_PREVIOUSLY"); 31 | } else { 32 | console.log("There is an update available. Remote package:" + JSON.stringify(remotePackage)); 33 | console.log("Downloading package..."); 34 | remotePackage.download(app.downloadSuccess, app.downloadError); 35 | } 36 | } 37 | }, 38 | checkError: function (error) { 39 | console.log("An error occured while checking for updates."); 40 | app.sendTestMessage("CHECK_ERROR"); 41 | }, 42 | downloadSuccess: function (localPackage) { 43 | console.log("Download succeeded."); 44 | localPackage.install(app.installSuccess, app.installError, { installMode: InstallMode.ON_NEXT_RESTART }); 45 | }, 46 | downloadError: function (error) { 47 | console.log("Download error."); 48 | app.sendTestMessage("DOWNLOAD_ERROR"); 49 | }, 50 | installSuccess: function () { 51 | console.log("Update installed."); 52 | app.sendTestMessage("UPDATE_INSTALLED"); 53 | }, 54 | installError: function (error) { 55 | console.log("Install error."); 56 | app.sendTestMessage("INSTALL_ERROR"); 57 | }, 58 | sendTestMessage: function (message, args) { 59 | var xhr = new XMLHttpRequest(); 60 | xhr.open("POST", "CODE_PUSH_SERVER_URL/reportTestMessage", false); 61 | var body = JSON.stringify({ message: message, args: args }); 62 | console.log("Sending test message body: " + body); 63 | 64 | xhr.setRequestHeader("Content-type", "application/json"); 65 | 66 | xhr.send(body); 67 | } 68 | }; 69 | 70 | app.initialize(); -------------------------------------------------------------------------------- /test/template/www/js/scenarioInstallOnResumeWithRevert.js: -------------------------------------------------------------------------------- 1 | var app = { 2 | // Application Constructor 3 | initialize: function () { 4 | this.bindEvents(); 5 | }, 6 | bindEvents: function () { 7 | document.addEventListener('deviceready', this.onDeviceReady, false); 8 | }, 9 | onDeviceReady: function () { 10 | app.receivedDeviceReady(); 11 | app.checkForUpdates(); 12 | }, 13 | // Update DOM on a Received Event 14 | receivedDeviceReady: function () { 15 | document.getElementById("deviceready").innerText = "Device is ready (scenario - install on next resume)"; 16 | console.log('Received Event: deviceready'); 17 | }, 18 | checkForUpdates: function () { 19 | console.log("Checking for updates..."); 20 | window.codePush.checkForUpdate(app.checkSuccess, app.checkError); 21 | }, 22 | checkSuccess: function (remotePackage) { 23 | if (!remotePackage) { 24 | // A null remotePackage means that the server successfully responded, but there is no update available. 25 | console.log("The application is up to date."); 26 | app.sendTestMessage("CHECK_UP_TO_DATE"); 27 | } 28 | else { 29 | if (remotePackage.failedInstall) { 30 | app.sendTestMessage("UPDATE_FAILED_PREVIOUSLY"); 31 | } else { 32 | console.log("There is an update available. Remote package:" + JSON.stringify(remotePackage)); 33 | console.log("Downloading package..."); 34 | remotePackage.download(app.downloadSuccess, app.downloadError); 35 | } 36 | } 37 | }, 38 | checkError: function (error) { 39 | console.log("An error occured while checking for updates."); 40 | app.sendTestMessage("CHECK_ERROR"); 41 | }, 42 | downloadSuccess: function (localPackage) { 43 | console.log("Download succeeded."); 44 | localPackage.install(app.installSuccess, app.installError, { installMode: InstallMode.ON_NEXT_RESUME }); 45 | }, 46 | downloadError: function (error) { 47 | console.log("Download error."); 48 | app.sendTestMessage("DOWNLOAD_ERROR"); 49 | }, 50 | installSuccess: function () { 51 | console.log("Update installed."); 52 | app.sendTestMessage("UPDATE_INSTALLED"); 53 | }, 54 | installError: function (error) { 55 | console.log("Install error."); 56 | app.sendTestMessage("INSTALL_ERROR"); 57 | }, 58 | sendTestMessage: function (message, args) { 59 | var xhr = new XMLHttpRequest(); 60 | xhr.open("POST", "CODE_PUSH_SERVER_URL/reportTestMessage", false); 61 | var body = JSON.stringify({ message: message, args: args }); 62 | console.log("Sending test message body: " + body); 63 | 64 | xhr.setRequestHeader("Content-type", "application/json"); 65 | 66 | xhr.send(body); 67 | } 68 | }; 69 | 70 | app.initialize(); -------------------------------------------------------------------------------- /test/template/www/js/scenarioInstallWithRevert.js: -------------------------------------------------------------------------------- 1 | var app = { 2 | // Application Constructor 3 | initialize: function () { 4 | this.bindEvents(); 5 | }, 6 | bindEvents: function () { 7 | document.addEventListener('deviceready', this.onDeviceReady, false); 8 | }, 9 | onDeviceReady: function () { 10 | app.receivedDeviceReady(); 11 | app.checkForUpdates(); 12 | }, 13 | // Update DOM on a Received Event 14 | receivedDeviceReady: function () { 15 | document.getElementById("deviceready").innerText = "Device is ready (scenario - installWithRevert)"; 16 | console.log('Received Event: deviceready'); 17 | }, 18 | checkForUpdates: function () { 19 | console.log("Checking for updates..."); 20 | window.codePush.checkForUpdate(app.checkSuccess, app.checkError); 21 | }, 22 | checkSuccess: function (remotePackage) { 23 | if (!remotePackage) { 24 | // A null remotePackage means that the server successfully responded, but there is no update available. 25 | console.log("The application is up to date."); 26 | app.sendTestMessage("CHECK_UP_TO_DATE"); 27 | } 28 | else { 29 | if (remotePackage.failedInstall) { 30 | app.sendTestMessage("UPDATE_FAILED_PREVIOUSLY"); 31 | } else { 32 | console.log("There is an update available. Remote package:" + JSON.stringify(remotePackage)); 33 | console.log("Downloading package..."); 34 | remotePackage.download(app.downloadSuccess, app.downloadError); 35 | } 36 | } 37 | }, 38 | checkError: function (error) { 39 | console.log("An error occured while checking for updates."); 40 | app.sendTestMessage("CHECK_ERROR"); 41 | }, 42 | downloadSuccess: function (localPackage) { 43 | console.log("Download succeeded."); 44 | /* Wait for 5s before we revert the application if notifyApplicationReady is not invoked. */ 45 | localPackage.install(app.installSuccess, app.installError, { installMode: InstallMode.IMMEDIATE }); 46 | }, 47 | downloadError: function (error) { 48 | console.log("Download error."); 49 | app.sendTestMessage("DOWNLOAD_ERROR"); 50 | }, 51 | installSuccess: function () { 52 | console.log("Install success."); 53 | app.sendTestMessage("UPDATE_INSTALLED"); 54 | }, 55 | installError: function (error) { 56 | console.log("Install error."); 57 | app.sendTestMessage("INSTALL_ERROR"); 58 | }, 59 | sendTestMessage: function (message, args) { 60 | var xhr = new XMLHttpRequest(); 61 | xhr.open("POST", "CODE_PUSH_SERVER_URL/reportTestMessage", false); 62 | var body = JSON.stringify({ message: message, args: args }); 63 | console.log("Sending test message body: " + body); 64 | 65 | xhr.setRequestHeader("Content-type", "application/json"); 66 | 67 | xhr.send(body); 68 | } 69 | }; 70 | 71 | app.initialize(); -------------------------------------------------------------------------------- /test/template/www/js/scenarioRestart.js: -------------------------------------------------------------------------------- 1 | var app = { 2 | // Application Constructor 3 | initialize: function () { 4 | this.bindEvents(); 5 | }, 6 | bindEvents: function () { 7 | document.addEventListener('deviceready', this.onDeviceReady, false); 8 | }, 9 | onDeviceReady: function () { 10 | app.receivedDeviceReady(); 11 | }, 12 | // Update DOM on a Received Event 13 | receivedDeviceReady: function () { 14 | document.getElementById("deviceready").innerText = "Device is ready (scenario - restart)"; 15 | console.log('Received Event: deviceready'); 16 | 17 | /* send the packages, expecting null pending package and null current package */ 18 | app.sendCurrentAndPendingPackage(function () { 19 | window.codePush.sync( 20 | function (status) { 21 | app.sendTestMessage("SYNC_STATUS", [status]); 22 | if (status == SyncStatus.UPDATE_INSTALLED) { 23 | /* send packages, expending non-null pending and null current */ 24 | app.sendCurrentAndPendingPackage(function () { 25 | app.tryRestart(); 26 | }); 27 | } 28 | }, 29 | { 30 | installMode: InstallMode.ON_NEXT_RESTART 31 | }); 32 | }); 33 | }, 34 | /* tries to restart the application and sends the status to the mock server */ 35 | tryRestart: function (callback) { 36 | window.codePush.restartApplication( 37 | function () { 38 | callback && callback(); 39 | }, 40 | function () { 41 | /* error */ 42 | app.sendTestMessage("RESTART_FAILED"); 43 | callback && callback(); 44 | }); 45 | }, 46 | /* sends the current and pending package to the mock server */ 47 | sendCurrentAndPendingPackage: function (callback) { 48 | window.codePush.getPendingPackage(function (pendingPackage) { 49 | console.log("Pending package: " + pendingPackage); 50 | app.sendTestMessage("PENDING_PACKAGE", [pendingPackage ? pendingPackage.packageHash : null]); 51 | window.codePush.getCurrentPackage(function (currentPackage) { 52 | console.log("Current package: " + currentPackage); 53 | app.sendTestMessage("CURRENT_PACKAGE", [currentPackage ? currentPackage.packageHash : null]); 54 | callback && callback(); 55 | }); 56 | }); 57 | }, 58 | sendTestMessage: function (message, args) { 59 | var xhr = new XMLHttpRequest(); 60 | xhr.open("POST", "CODE_PUSH_SERVER_URL/reportTestMessage", false); 61 | var body = JSON.stringify({ message: message, args: args }); 62 | console.log("Sending test message body: " + body); 63 | 64 | xhr.setRequestHeader("Content-type", "application/json"); 65 | xhr.send(body); 66 | } 67 | }; 68 | 69 | app.initialize(); -------------------------------------------------------------------------------- /test/template/www/js/scenarioSetup.js: -------------------------------------------------------------------------------- 1 | var app = { 2 | // Application Constructor 3 | initialize: function () { 4 | this.bindEvents(); 5 | }, 6 | bindEvents: function () { 7 | document.addEventListener('deviceready', this.onDeviceReady, false); 8 | }, 9 | onDeviceReady: function () { 10 | app.receivedDeviceReady(); 11 | }, 12 | // Update DOM on a Received Event 13 | receivedDeviceReady: function () { 14 | document.getElementById("deviceready").innerText = "Device is ready (scenario - setup)"; 15 | console.log('Received Event: deviceready'); 16 | } 17 | }; 18 | 19 | app.initialize(); -------------------------------------------------------------------------------- /test/template/www/js/scenarioSync.js: -------------------------------------------------------------------------------- 1 | var app = { 2 | // Application Constructor 3 | initialize: function () { 4 | this.bindEvents(); 5 | }, 6 | bindEvents: function () { 7 | document.addEventListener('deviceready', this.onDeviceReady, false); 8 | }, 9 | onDeviceReady: function () { 10 | app.receivedDeviceReady(); 11 | }, 12 | // Update DOM on a Received Event 13 | receivedDeviceReady: function () { 14 | document.getElementById("deviceready").innerText = "Device is ready (scenario - sync)"; 15 | console.log('Received Event: deviceready'); 16 | /* invoke sync with no UI options */ 17 | window.codePush.sync( 18 | function (status) { 19 | app.sendTestMessage("SYNC_STATUS", [status]); 20 | }, 21 | { 22 | installMode: InstallMode.IMMEDIATE 23 | }); 24 | }, 25 | sendTestMessage: function (message, args) { 26 | var xhr = new XMLHttpRequest(); 27 | xhr.open("POST", "CODE_PUSH_SERVER_URL/reportTestMessage", false); 28 | var body = JSON.stringify({ message: message, args: args }); 29 | console.log("Sending test message body: " + body); 30 | 31 | xhr.setRequestHeader("Content-type", "application/json"); 32 | xhr.send(body); 33 | } 34 | }; 35 | 36 | app.initialize(); -------------------------------------------------------------------------------- /test/template/www/js/scenarioSync2x.js: -------------------------------------------------------------------------------- 1 | var app = { 2 | // Application Constructor 3 | initialize: function () { 4 | this.bindEvents(); 5 | }, 6 | bindEvents: function () { 7 | document.addEventListener('deviceready', this.onDeviceReady, false); 8 | }, 9 | onDeviceReady: function () { 10 | app.receivedDeviceReady(); 11 | }, 12 | // Update DOM on a Received Event 13 | receivedDeviceReady: function () { 14 | document.getElementById("deviceready").innerText = "Device is ready (scenario - sync 2x)"; 15 | console.log('Received Event: deviceready'); 16 | /* invoke sync with no UI options */ 17 | window.codePush.sync( 18 | function (status) { 19 | app.sendTestMessage("SYNC_STATUS", [status]); 20 | }, 21 | { 22 | installMode: InstallMode.IMMEDIATE 23 | }); 24 | /* Only send the sync status of the second sync as a test message */ 25 | window.codePush.sync( 26 | function (status) { 27 | app.sendTestMessage("SYNC_STATUS", [status]); 28 | }, 29 | { 30 | installMode: InstallMode.IMMEDIATE 31 | }); 32 | }, 33 | sendTestMessage: function (message, args) { 34 | var xhr = new XMLHttpRequest(); 35 | xhr.open("POST", "CODE_PUSH_SERVER_URL/reportTestMessage", false); 36 | var body = JSON.stringify({ message: message, args: args }); 37 | console.log("Sending test message body: " + body); 38 | 39 | xhr.setRequestHeader("Content-type", "application/json"); 40 | xhr.send(body); 41 | } 42 | }; 43 | 44 | app.initialize(); -------------------------------------------------------------------------------- /test/template/www/js/scenarioSyncMandatoryDefault.js: -------------------------------------------------------------------------------- 1 | var app = { 2 | // Application Constructor 3 | initialize: function () { 4 | this.bindEvents(); 5 | }, 6 | bindEvents: function () { 7 | document.addEventListener('deviceready', this.onDeviceReady, false); 8 | }, 9 | onDeviceReady: function () { 10 | app.receivedDeviceReady(); 11 | }, 12 | // Update DOM on a Received Event 13 | receivedDeviceReady: function () { 14 | document.getElementById("deviceready").innerText = "Device is ready (scenario - sync mandatory)"; 15 | console.log('Received Event: deviceready'); 16 | /* invoke sync with no UI options */ 17 | window.codePush.sync( 18 | function (status) { 19 | switch(status) { 20 | case SyncStatus.UP_TO_DATE: 21 | case SyncStatus.UPDATE_INSTALLED: 22 | case SyncStatus.UPDATE_IGNORED: 23 | case SyncStatus.ERROR: 24 | case SyncStatus.IN_PROGRESS: 25 | app.sendTestMessage("SYNC_STATUS", [status]); 26 | default: 27 | break; 28 | } 29 | }, 30 | { 31 | installMode: InstallMode.ON_NEXT_RESTART 32 | }); 33 | }, 34 | sendTestMessage: function (message, args) { 35 | var xhr = new XMLHttpRequest(); 36 | xhr.open("POST", "CODE_PUSH_SERVER_URL/reportTestMessage", false); 37 | var body = JSON.stringify({ message: message, args: args }); 38 | console.log("Sending test message body: " + body); 39 | 40 | xhr.setRequestHeader("Content-type", "application/json"); 41 | xhr.send(body); 42 | } 43 | }; 44 | 45 | app.initialize(); -------------------------------------------------------------------------------- /test/template/www/js/scenarioSyncMandatoryRestart.js: -------------------------------------------------------------------------------- 1 | var app = { 2 | // Application Constructor 3 | initialize: function () { 4 | this.bindEvents(); 5 | }, 6 | bindEvents: function () { 7 | document.addEventListener('deviceready', this.onDeviceReady, false); 8 | }, 9 | onDeviceReady: function () { 10 | app.receivedDeviceReady(); 11 | }, 12 | // Update DOM on a Received Event 13 | receivedDeviceReady: function () { 14 | document.getElementById("deviceready").innerText = "Device is ready (scenario - sync mandatory)"; 15 | console.log('Received Event: deviceready'); 16 | /* invoke sync with no UI options */ 17 | window.codePush.sync( 18 | function (status) { 19 | switch(status) { 20 | case SyncStatus.UP_TO_DATE: 21 | case SyncStatus.UPDATE_INSTALLED: 22 | case SyncStatus.UPDATE_IGNORED: 23 | case SyncStatus.ERROR: 24 | case SyncStatus.IN_PROGRESS: 25 | app.sendTestMessage("SYNC_STATUS", [status]); 26 | default: 27 | break; 28 | } 29 | }, 30 | { 31 | installMode: InstallMode.IMMEDIATE, 32 | mandatoryInstallMode: InstallMode.ON_NEXT_RESTART 33 | }); 34 | }, 35 | sendTestMessage: function (message, args) { 36 | var xhr = new XMLHttpRequest(); 37 | xhr.open("POST", "CODE_PUSH_SERVER_URL/reportTestMessage", false); 38 | var body = JSON.stringify({ message: message, args: args }); 39 | console.log("Sending test message body: " + body); 40 | 41 | xhr.setRequestHeader("Content-type", "application/json"); 42 | xhr.send(body); 43 | } 44 | }; 45 | 46 | app.initialize(); -------------------------------------------------------------------------------- /test/template/www/js/scenarioSyncMandatoryResume.js: -------------------------------------------------------------------------------- 1 | var app = { 2 | // Application Constructor 3 | initialize: function () { 4 | this.bindEvents(); 5 | }, 6 | bindEvents: function () { 7 | document.addEventListener('deviceready', this.onDeviceReady, false); 8 | }, 9 | onDeviceReady: function () { 10 | app.receivedDeviceReady(); 11 | }, 12 | // Update DOM on a Received Event 13 | receivedDeviceReady: function () { 14 | document.getElementById("deviceready").innerText = "Device is ready (scenario - sync mandatory)"; 15 | console.log('Received Event: deviceready'); 16 | /* invoke sync with no UI options */ 17 | window.codePush.sync( 18 | function (status) { 19 | switch(status) { 20 | case SyncStatus.UP_TO_DATE: 21 | case SyncStatus.UPDATE_INSTALLED: 22 | case SyncStatus.UPDATE_IGNORED: 23 | case SyncStatus.ERROR: 24 | case SyncStatus.IN_PROGRESS: 25 | app.sendTestMessage("SYNC_STATUS", [status]); 26 | default: 27 | break; 28 | } 29 | }, 30 | { 31 | installMode: InstallMode.ON_NEXT_RESTART, 32 | mandatoryInstallMode: InstallMode.ON_NEXT_RESUME 33 | }); 34 | }, 35 | sendTestMessage: function (message, args) { 36 | var xhr = new XMLHttpRequest(); 37 | xhr.open("POST", "CODE_PUSH_SERVER_URL/reportTestMessage", false); 38 | var body = JSON.stringify({ message: message, args: args }); 39 | console.log("Sending test message body: " + body); 40 | 41 | xhr.setRequestHeader("Content-type", "application/json"); 42 | xhr.send(body); 43 | } 44 | }; 45 | 46 | app.initialize(); -------------------------------------------------------------------------------- /test/template/www/js/scenarioSyncRestartDelay.js: -------------------------------------------------------------------------------- 1 | var app = { 2 | // Application Constructor 3 | initialize: function () { 4 | this.bindEvents(); 5 | }, 6 | bindEvents: function () { 7 | document.addEventListener('deviceready', this.onDeviceReady, false); 8 | }, 9 | onDeviceReady: function () { 10 | app.receivedDeviceReady(); 11 | }, 12 | // Update DOM on a Received Event 13 | receivedDeviceReady: function () { 14 | document.getElementById("deviceready").innerText = "Device is ready (scenario - sync on restart with minbackdur)"; 15 | console.log('Received Event: deviceready'); 16 | /* invoke sync with no UI options */ 17 | window.codePush.sync( 18 | function (status) { 19 | // only output result statuses 20 | switch(status) { 21 | case SyncStatus.UP_TO_DATE: 22 | case SyncStatus.UPDATE_INSTALLED: 23 | case SyncStatus.UPDATE_IGNORED: 24 | case SyncStatus.ERROR: 25 | case SyncStatus.IN_PROGRESS: 26 | app.sendTestMessage("SYNC_STATUS", [status]); 27 | default: 28 | break; 29 | } 30 | }, 31 | { 32 | installMode: InstallMode.ON_NEXT_RESTART, 33 | minimumBackgroundDuration: 15 34 | }); 35 | }, 36 | sendTestMessage: function (message, args) { 37 | var xhr = new XMLHttpRequest(); 38 | xhr.open("POST", "CODE_PUSH_SERVER_URL/reportTestMessage", false); 39 | var body = JSON.stringify({ message: message, args: args }); 40 | console.log("Sending test message body: " + body); 41 | 42 | xhr.setRequestHeader("Content-type", "application/json"); 43 | xhr.send(body); 44 | } 45 | }; 46 | 47 | app.initialize(); -------------------------------------------------------------------------------- /test/template/www/js/scenarioSyncResume.js: -------------------------------------------------------------------------------- 1 | var app = { 2 | // Application Constructor 3 | initialize: function () { 4 | this.bindEvents(); 5 | }, 6 | bindEvents: function () { 7 | document.addEventListener('deviceready', this.onDeviceReady, false); 8 | }, 9 | onDeviceReady: function () { 10 | app.receivedDeviceReady(); 11 | }, 12 | // Update DOM on a Received Event 13 | receivedDeviceReady: function () { 14 | document.getElementById("deviceready").innerText = "Device is ready (scenario - sync on resume with no minbackdur)"; 15 | console.log('Received Event: deviceready'); 16 | /* invoke sync with no UI options */ 17 | window.codePush.sync( 18 | function (status) { 19 | // only output result statuses 20 | switch(status) { 21 | case SyncStatus.UP_TO_DATE: 22 | case SyncStatus.UPDATE_INSTALLED: 23 | case SyncStatus.UPDATE_IGNORED: 24 | case SyncStatus.ERROR: 25 | case SyncStatus.IN_PROGRESS: 26 | app.sendTestMessage("SYNC_STATUS", [status]); 27 | default: 28 | break; 29 | } 30 | }, 31 | { 32 | installMode: InstallMode.ON_NEXT_RESUME 33 | }); 34 | }, 35 | sendTestMessage: function (message, args) { 36 | var xhr = new XMLHttpRequest(); 37 | xhr.open("POST", "CODE_PUSH_SERVER_URL/reportTestMessage", false); 38 | var body = JSON.stringify({ message: message, args: args }); 39 | console.log("Sending test message body: " + body); 40 | 41 | xhr.setRequestHeader("Content-type", "application/json"); 42 | xhr.send(body); 43 | } 44 | }; 45 | 46 | app.initialize(); -------------------------------------------------------------------------------- /test/template/www/js/scenarioSyncResumeDelay.js: -------------------------------------------------------------------------------- 1 | var app = { 2 | // Application Constructor 3 | initialize: function () { 4 | this.bindEvents(); 5 | }, 6 | bindEvents: function () { 7 | document.addEventListener('deviceready', this.onDeviceReady, false); 8 | }, 9 | onDeviceReady: function () { 10 | app.receivedDeviceReady(); 11 | }, 12 | // Update DOM on a Received Event 13 | receivedDeviceReady: function () { 14 | document.getElementById("deviceready").innerText = "Device is ready (scenario - sync on resume with minbackdur)"; 15 | console.log('Received Event: deviceready'); 16 | /* invoke sync with no UI options */ 17 | window.codePush.sync( 18 | function (status) { 19 | // only output result statuses 20 | switch(status) { 21 | case SyncStatus.UP_TO_DATE: 22 | case SyncStatus.UPDATE_INSTALLED: 23 | case SyncStatus.UPDATE_IGNORED: 24 | case SyncStatus.ERROR: 25 | case SyncStatus.IN_PROGRESS: 26 | app.sendTestMessage("SYNC_STATUS", [status]); 27 | default: 28 | break; 29 | } 30 | }, 31 | { 32 | installMode: InstallMode.ON_NEXT_RESUME, 33 | minimumBackgroundDuration: 5 34 | }); 35 | }, 36 | sendTestMessage: function (message, args) { 37 | var xhr = new XMLHttpRequest(); 38 | xhr.open("POST", "CODE_PUSH_SERVER_URL/reportTestMessage", false); 39 | var body = JSON.stringify({ message: message, args: args }); 40 | console.log("Sending test message body: " + body); 41 | 42 | xhr.setRequestHeader("Content-type", "application/json"); 43 | xhr.send(body); 44 | } 45 | }; 46 | 47 | app.initialize(); -------------------------------------------------------------------------------- /test/template/www/js/updateDeviceReady.js: -------------------------------------------------------------------------------- 1 | var app = { 2 | // Application Constructor 3 | initialize: function () { 4 | this.bindEvents(); 5 | }, 6 | bindEvents: function () { 7 | document.addEventListener('deviceready', this.onDeviceReady, false); 8 | }, 9 | onDeviceReady: function () { 10 | app.receivedDeviceReady(); 11 | }, 12 | // Update DOM on a Received Event 13 | receivedDeviceReady: function () { 14 | document.getElementById("deviceready").innerText = "Device is ready (scenario - updateDeviceReady)"; 15 | console.log('Received Event: deviceready'); 16 | app.sendTestMessage("DEVICE_READY_AFTER_UPDATE"); 17 | }, 18 | sendTestMessage: function (message, args) { 19 | var xhr = new XMLHttpRequest(); 20 | xhr.open("POST", "CODE_PUSH_SERVER_URL/reportTestMessage", false); 21 | var body = JSON.stringify({ message: message, args: args }); 22 | console.log("Sending test message body: " + body); 23 | 24 | xhr.setRequestHeader("Content-type", "application/json"); 25 | 26 | xhr.send(body); 27 | } 28 | }; 29 | 30 | app.initialize(); -------------------------------------------------------------------------------- /test/template/www/js/updateNARConditional.js: -------------------------------------------------------------------------------- 1 | var app = { 2 | // Application Constructor 3 | initialize: function () { 4 | this.bindEvents(); 5 | }, 6 | bindEvents: function () { 7 | document.addEventListener('deviceready', this.onDeviceReady, false); 8 | }, 9 | onDeviceReady: function () { 10 | app.receivedDeviceReady(); 11 | }, 12 | // Update DOM on a Received Event 13 | receivedDeviceReady: function () { 14 | document.getElementById("deviceready").innerText = "Device is ready (scenario - conditional notify application ready)"; 15 | console.log('Received Event: deviceready'); 16 | app.sendTestMessage("DEVICE_READY_AFTER_UPDATE", null, function (responseBody) { 17 | console.log("Server response: " + responseBody); 18 | if (responseBody !== "SKIP_NOTIFY_APPLICATION_READY") { 19 | 20 | var notifySucceeded = function () { 21 | app.sendTestMessage("NOTIFY_APP_READY_SUCCESS"); 22 | }; 23 | 24 | var notifyFailed = function () { 25 | app.sendTestMessage("NOTIFY_APP_READY_FAILURE"); 26 | }; 27 | 28 | window.codePush.notifyApplicationReady(notifySucceeded, notifyFailed); 29 | app.checkForUpdates(); 30 | } else { 31 | console.log("Skipping notifyApplicationReady!"); 32 | app.sendTestMessage("SKIPPED_NOTIFY_APPLICATION_READY"); 33 | } 34 | }); 35 | }, 36 | checkForUpdates: function () { 37 | console.log("Checking for updates..."); 38 | window.codePush.checkForUpdate(app.checkSuccess, app.checkError); 39 | }, 40 | checkSuccess: function (remotePackage) { 41 | if (!remotePackage) { 42 | // A null remotePackage means that the server successfully responded, but there is no update available. 43 | console.log("The application is up to date."); 44 | app.sendTestMessage("CHECK_UP_TO_DATE"); 45 | } 46 | else { 47 | if (remotePackage.failedInstall) { 48 | app.sendTestMessage("UPDATE_FAILED_PREVIOUSLY"); 49 | } else { 50 | console.log("There is an update available. Remote package:" + JSON.stringify(remotePackage)); 51 | console.log("Downloading package..."); 52 | remotePackage.download(app.downloadSuccess, app.downloadError); 53 | } 54 | } 55 | }, 56 | checkError: function (error) { 57 | console.log("An error occured while checking for updates."); 58 | app.sendTestMessage("CHECK_ERROR"); 59 | }, 60 | downloadSuccess: function (localPackage) { 61 | console.log("Download succeeded."); 62 | localPackage.install(app.installSuccess, app.installError, { installMode: InstallMode.ON_NEXT_RESTART }); 63 | }, 64 | downloadError: function (error) { 65 | console.log("Download error."); 66 | app.sendTestMessage("DOWNLOAD_ERROR"); 67 | }, 68 | installSuccess: function () { 69 | console.log("Update installed."); 70 | app.sendTestMessage("UPDATE_INSTALLED"); 71 | }, 72 | installError: function (error) { 73 | console.log("Install error."); 74 | app.sendTestMessage("INSTALL_ERROR"); 75 | }, 76 | sendTestMessage: function (message, args, callback) { 77 | var xhr = new XMLHttpRequest(); 78 | xhr.onreadystatechange = function () { 79 | if (xhr.readyState == 4 && xhr.status == 200) { 80 | callback(xhr.response); 81 | } 82 | }; 83 | 84 | xhr.open("POST", "CODE_PUSH_SERVER_URL/reportTestMessage", false); 85 | var body = JSON.stringify({ message: message, args: args }); 86 | console.log("Sending test message body: " + body); 87 | 88 | xhr.setRequestHeader("Content-type", "application/json"); 89 | 90 | xhr.send(body); 91 | } 92 | }; 93 | 94 | app.initialize(); -------------------------------------------------------------------------------- /test/template/www/js/updateNotifyApplicationReady.js: -------------------------------------------------------------------------------- 1 | var app = { 2 | // Application Constructor 3 | initialize: function () { 4 | this.bindEvents(); 5 | }, 6 | bindEvents: function () { 7 | document.addEventListener('deviceready', this.onDeviceReady, false); 8 | }, 9 | onDeviceReady: function () { 10 | app.receivedDeviceReady(); 11 | }, 12 | // Update DOM on a Received Event 13 | receivedDeviceReady: function () { 14 | document.getElementById("deviceready").innerText = "Device is ready (scenario - updateNotifyApplicationReady)"; 15 | console.log('Received Event: deviceready'); 16 | app.sendTestMessage("DEVICE_READY_AFTER_UPDATE"); 17 | 18 | var notifySucceeded = function () { 19 | app.sendTestMessage("NOTIFY_APP_READY_SUCCESS"); 20 | }; 21 | 22 | var notifyFailed = function () { 23 | app.sendTestMessage("NOTIFY_APP_READY_FAILURE"); 24 | }; 25 | 26 | window.codePush.notifyApplicationReady(notifySucceeded, notifyFailed); 27 | }, 28 | sendTestMessage: function (message, args) { 29 | var xhr = new XMLHttpRequest(); 30 | xhr.open("POST", "CODE_PUSH_SERVER_URL/reportTestMessage", false); 31 | var body = JSON.stringify({ message: message, args: args }); 32 | console.log("Sending test message body: " + body); 33 | 34 | xhr.setRequestHeader("Content-type", "application/json"); 35 | 36 | xhr.send(body); 37 | } 38 | }; 39 | 40 | app.initialize(); -------------------------------------------------------------------------------- /test/template/www/js/updateSync.js: -------------------------------------------------------------------------------- 1 | var app = { 2 | // Application Constructor 3 | initialize: function () { 4 | this.bindEvents(); 5 | }, 6 | bindEvents: function () { 7 | document.addEventListener('deviceready', this.onDeviceReady, false); 8 | }, 9 | onDeviceReady: function () { 10 | app.receivedDeviceReady(); 11 | }, 12 | // Update DOM on a Received Event 13 | receivedDeviceReady: function () { 14 | document.getElementById("deviceready").innerText = "Device is ready (scenario - update sync)"; 15 | console.log('Received Event: deviceready'); 16 | app.sendTestMessage("DEVICE_READY_AFTER_UPDATE"); 17 | /* invoke sync with UI options such that the update will not be installed */ 18 | window.codePush.sync(); 19 | }, 20 | sendTestMessage: function (message, args) { 21 | var xhr = new XMLHttpRequest(); 22 | xhr.open("POST", "CODE_PUSH_SERVER_URL/reportTestMessage", false); 23 | var body = JSON.stringify({ message: message, args: args }); 24 | console.log("Sending test message body: " + body); 25 | 26 | xhr.setRequestHeader("Content-type", "application/json"); 27 | xhr.send(body); 28 | } 29 | }; 30 | 31 | app.initialize(); -------------------------------------------------------------------------------- /test/template/www/js/updateSync2x.js: -------------------------------------------------------------------------------- 1 | var app = { 2 | // Application Constructor 3 | initialize: function () { 4 | this.bindEvents(); 5 | }, 6 | bindEvents: function () { 7 | document.addEventListener('deviceready', this.onDeviceReady, false); 8 | }, 9 | onDeviceReady: function () { 10 | app.receivedDeviceReady(); 11 | }, 12 | // Update DOM on a Received Event 13 | receivedDeviceReady: function () { 14 | document.getElementById("deviceready").innerText = "Device is ready (scenario - update sync 2x)"; 15 | console.log('Received Event: deviceready'); 16 | app.sendTestMessage("DEVICE_READY_AFTER_UPDATE"); 17 | /* invoke sync with UI options such that the update will not be installed */ 18 | window.codePush.sync(); 19 | window.codePush.sync( 20 | function (status) { 21 | app.sendTestMessage("SYNC_STATUS", [status]); 22 | }); 23 | }, 24 | sendTestMessage: function (message, args) { 25 | var xhr = new XMLHttpRequest(); 26 | xhr.open("POST", "CODE_PUSH_SERVER_URL/reportTestMessage", false); 27 | var body = JSON.stringify({ message: message, args: args }); 28 | console.log("Sending test message body: " + body); 29 | 30 | xhr.setRequestHeader("Content-type", "application/json"); 31 | xhr.send(body); 32 | } 33 | }; 34 | 35 | app.initialize(); -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "class-name": true, 4 | "comment-format": [ 5 | true, 6 | "check-space" 7 | ], 8 | "indent": [ 9 | true, 10 | "spaces" 11 | ], 12 | "one-line": [ 13 | true, 14 | "check-open-brace" 15 | ], 16 | "quotemark": [ 17 | true, 18 | "double" 19 | ], 20 | "semicolon": true, 21 | "whitespace": [ 22 | true, 23 | "check-branch", 24 | "check-operator", 25 | "check-separator", 26 | "check-type" 27 | ], 28 | "strict-null-checks": false, 29 | "typedef-whitespace": [ 30 | true, 31 | { 32 | "call-signature": "nospace", 33 | "index-signature": "nospace", 34 | "parameter": "nospace", 35 | "property-declaration": "nospace", 36 | "variable-declaration": "nospace" 37 | }, 38 | { 39 | "call-signature": "onespace", 40 | "index-signature": "onespace", 41 | "parameter": "onespace", 42 | "property-declaration": "onespace", 43 | "variable-declaration": "onespace" 44 | } 45 | ] 46 | } 47 | } -------------------------------------------------------------------------------- /typings/replace.d.ts: -------------------------------------------------------------------------------- 1 | interface ReplaceOptions { 2 | regex: string; 3 | replacement: string; 4 | paths: Array; 5 | recursive: boolean; 6 | silent: boolean; 7 | } 8 | 9 | declare module "replace" { 10 | function replace(options: ReplaceOptions): void; 11 | 12 | export = replace; 13 | } -------------------------------------------------------------------------------- /www/codePushUtil.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | "use strict"; 4 | 5 | declare var zip: any; 6 | 7 | /** 8 | * Callback / error / logging utilities. 9 | */ 10 | class CodePushUtil { 11 | 12 | /** 13 | * Tag used for logging to the console. 14 | */ 15 | private static TAG: string = "[CodePush]"; 16 | 17 | /** 18 | * Performs a copy of all members of fromParameter to toParameter, with the condition that they are unassigned or null in toParameter. 19 | */ 20 | public static copyUnassignedMembers(fromParameter: any, toParameter: any) { 21 | for (let key in fromParameter) { 22 | if ((toParameter)[key] === undefined || (toParameter)[key] === null) { 23 | (toParameter)[key] = (fromParameter)[key]; 24 | } 25 | } 26 | } 27 | 28 | /** 29 | * Given two Cordova style callbacks for success and error, this function returns a node.js 30 | * style callback where the error is the first parameter and the result the second. 31 | */ 32 | public static getNodeStyleCallbackFor(successCallback: SuccessCallback, errorCallback: { (error?: any): void; }): Callback { 33 | return (error: any, result: T) => { 34 | if (error) { 35 | errorCallback && errorCallback(error); 36 | } else { 37 | successCallback && successCallback(result); 38 | } 39 | }; 40 | } 41 | 42 | /** 43 | * Gets the message of an error, if any. Otherwise it returns the empty string. 44 | */ 45 | public static getErrorMessage(e: Error): string { 46 | return e && e.message || e && e.toString() || ""; 47 | } 48 | 49 | /** 50 | * Logs the error to the console and then forwards it to the provided ErrorCallback, if any. 51 | */ 52 | public static invokeErrorCallback = (error: Error, errorCallback: ErrorCallback): void => { 53 | CodePushUtil.logError(null, error); 54 | errorCallback && errorCallback(error); 55 | } 56 | 57 | /** 58 | * Logs a message using the CodePush tag. 59 | */ 60 | public static logMessage(msg: string): void { 61 | console.log(CodePushUtil.TAG + " " + msg); 62 | } 63 | 64 | /** 65 | * Logs an error message using the CodePush tag. 66 | */ 67 | public static logError(message: String, error?: Error): void { 68 | const errorMessage = `${message || ""} ${CodePushUtil.getErrorMessage(error)}`; 69 | const stackTrace = error && error.stack ? `. StackTrace: ${error.stack}` : ""; 70 | console.error(`${CodePushUtil.TAG} ${errorMessage}${stackTrace}`); 71 | } 72 | } 73 | 74 | export = CodePushUtil; 75 | -------------------------------------------------------------------------------- /www/httpRequester.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | "use strict"; 4 | 5 | import CodePushUtil = require("./codePushUtil"); 6 | 7 | declare var cordova: Cordova & { plugin: { http: AdvancedHttp.Plugin } }; 8 | 9 | /** 10 | * XMLHttpRequest-based implementation of Http.Requester. 11 | */ 12 | class HttpRequester implements Http.Requester { 13 | 14 | constructor(contentType?: string) { 15 | // Set headers for all requests 16 | cordova.plugin.http.setHeader("X-CodePush-Plugin-Name", "cordova-plugin-code-push"); 17 | cordova.plugin.http.setHeader("X-CodePush-Plugin-Version", cordova.require("cordova/plugin_list").metadata["cordova-plugin-code-push"]); 18 | cordova.plugin.http.setHeader("X-CodePush-SDK-Version", cordova.require("cordova/plugin_list").metadata["code-push"]); 19 | if (contentType) { 20 | cordova.plugin.http.setHeader("Content-Type", contentType); 21 | } 22 | } 23 | 24 | public request(verb: Http.Verb, url: string, callbackOrRequestBody: Callback | string, callback?: Callback): void { 25 | var requestCallback: Callback = callback; 26 | 27 | var options = HttpRequester.getInitialOptionsForVerb(verb); 28 | if (options instanceof Error) { 29 | CodePushUtil.logError("Could not make the HTTP request", options); 30 | requestCallback && requestCallback(options, undefined); 31 | return; 32 | } 33 | 34 | if (!requestCallback && typeof callbackOrRequestBody === "function") { 35 | requestCallback = >callbackOrRequestBody; 36 | } 37 | 38 | if (typeof callbackOrRequestBody === "string") { 39 | // should be already JSON.stringify-ied, using plaintext serializer 40 | options.serializer = "utf8"; 41 | options.data = callbackOrRequestBody; 42 | } 43 | 44 | options.responseType = "text"; // Backward compatibility to xhr.responseText 45 | 46 | cordova.plugin.http.sendRequest(url, options, function(success) { 47 | requestCallback && requestCallback(null, { 48 | body: success.data, // this should be plaintext 49 | statusCode: success.status, 50 | }); 51 | }, function(failure) { 52 | requestCallback && requestCallback(new Error(failure.error), null); 53 | }); 54 | } 55 | 56 | /** 57 | * Builds the initial options object for the advanced-http plugin, if the HTTP method is supported. 58 | * The reason for which this is needed is because the Http.Verb enum corresponds to integer values from native runtime. 59 | */ 60 | private static getInitialOptionsForVerb(verb: Http.Verb): AdvancedHttp.Options | Error { 61 | switch (verb) { 62 | case Http.Verb.GET: 63 | return { method: "get" }; 64 | case Http.Verb.DELETE: 65 | return { method: "delete" }; 66 | case Http.Verb.HEAD: 67 | return { method: "head" }; 68 | case Http.Verb.PATCH: 69 | return { method: "patch" }; 70 | case Http.Verb.POST: 71 | return { method: "post" }; 72 | case Http.Verb.PUT: 73 | return { method: "put" }; 74 | case Http.Verb.TRACE: 75 | case Http.Verb.OPTIONS: 76 | case Http.Verb.CONNECT: 77 | default: 78 | return new(class UnsupportedMethodError extends Error {})(`Unsupported HTTP method code [${verb}]`); 79 | } 80 | } 81 | } 82 | 83 | export = HttpRequester; 84 | -------------------------------------------------------------------------------- /www/installMode.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Defines the available install modes for updates. 3 | */ 4 | enum InstallMode { 5 | /** 6 | * The update will be applied to the running application immediately. The application will be reloaded with the new content immediately. 7 | */ 8 | IMMEDIATE, 9 | 10 | /** 11 | * The update is downloaded but not installed immediately. The new content will be available the next time the application is started. 12 | */ 13 | ON_NEXT_RESTART, 14 | 15 | /** 16 | * The udpate is downloaded but not installed immediately. The new content will be available the next time the application is resumed or restarted, whichever event happends first. 17 | */ 18 | ON_NEXT_RESUME 19 | } 20 | 21 | export = InstallMode; -------------------------------------------------------------------------------- /www/nativeAppInfo.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | "use strict"; 5 | 6 | declare var cordova: Cordova; 7 | 8 | const DefaultServerUrl: string = "https://codepush.appcenter.ms/"; 9 | 10 | /** 11 | * Provides information about the native app. 12 | */ 13 | class NativeAppInfo { 14 | 15 | /** 16 | * Gets the application build timestamp. 17 | */ 18 | public static getApplicationBuildTime(callback: Callback): void { 19 | var timestampSuccess = (timestamp?: String) => { callback(null, timestamp); }; 20 | var timestampError = () => { callback(new Error("Could not get application timestamp."), null); }; 21 | 22 | cordova.exec(timestampSuccess, timestampError, "CodePush", "getNativeBuildTime", []); 23 | } 24 | 25 | /** 26 | * Gets the application version. 27 | */ 28 | public static getApplicationVersion(callback: Callback): void { 29 | var versionSuccess = (version?: String) => { callback(null, version); }; 30 | var versionError = () => { callback(new Error("Could not get application version."), null); }; 31 | 32 | cordova.exec(versionSuccess, versionError, "CodePush", "getAppVersion", []); 33 | } 34 | 35 | /** 36 | * Gets a hash of the `www` folder contents compiled in the app store binary. 37 | */ 38 | public static getBinaryHash(callback: Callback): void { 39 | var binaryHashSuccess = (binaryHash?: String) => { callback(null, binaryHash); }; 40 | var binaryHashError = () => { callback(new Error("Could not get binary hash."), null); }; 41 | 42 | cordova.exec(binaryHashSuccess, binaryHashError, "CodePush", "getBinaryHash", []); 43 | } 44 | 45 | /** 46 | * Gets the server URL from config.xml by calling into the native platform. 47 | */ 48 | public static getServerURL(serverCallback: Callback): void { 49 | var serverSuccess = (serverURL?: String) => { serverCallback(null, serverURL); }; 50 | 51 | /* Default to the production CodePush server. */ 52 | var serverError = () => { serverCallback(null, DefaultServerUrl); }; 53 | 54 | cordova.exec(serverSuccess, serverError, "CodePush", "getServerURL", []); 55 | } 56 | 57 | /** 58 | * Gets the deployment key from config.xml by calling into the native platform. 59 | */ 60 | public static getDeploymentKey(deploymentKeyCallback: Callback): void { 61 | var deploymentSuccess = (deploymentKey?: String) => { deploymentKeyCallback(null, deploymentKey); }; 62 | var deploymentError = () => { deploymentKeyCallback(new Error("Deployment key not found."), null); }; 63 | 64 | cordova.exec(deploymentSuccess, deploymentError, "CodePush", "getDeploymentKey", []); 65 | } 66 | 67 | /** 68 | * Checks if a package update was previously attempted but failed for a given package hash. 69 | * Every reverted update is stored such that the application developer has the option to ignore 70 | * updates that previously failed. This way, an infinite update loop can be prevented in case of a bad update package. 71 | */ 72 | public static isFailedUpdate(packageHash: string, checkCallback: SuccessCallback): void { 73 | var win = (failed?: number) => { 74 | checkCallback && checkCallback(!!failed); 75 | }; 76 | 77 | var fail = (e?: Error) => { 78 | /* In case of an error, return false. */ 79 | win(0); 80 | }; 81 | 82 | cordova.exec(win, fail, "CodePush", "isFailedUpdate", [packageHash]); 83 | } 84 | 85 | /** 86 | * Checks if this is the first application run of a package after it has been applied. 87 | * The didUpdateCallback callback can be used for migrating data from the old app version to the new one. 88 | * 89 | * @param packageHash The hash value of the package. 90 | * @param firstRunCallback Callback invoked with a boolean parameter indicating if this is the first run after an update. 91 | */ 92 | public static isFirstRun(packageHash: string, firstRunCallback: SuccessCallback): void { 93 | var win = (firstRun?: number) => { 94 | firstRunCallback(!!firstRun); 95 | }; 96 | 97 | var fail = () => { 98 | firstRunCallback(false); 99 | }; 100 | 101 | cordova.exec(win, fail, "CodePush", "isFirstRun", [packageHash]); 102 | } 103 | 104 | /** 105 | * Checks with the native side if there is a pending update. 106 | */ 107 | public static isPendingUpdate(callback: SuccessCallback): void { 108 | var win = (firstRun?: number) => { 109 | callback(!!firstRun); 110 | }; 111 | 112 | var fail = () => { 113 | callback(false); 114 | }; 115 | 116 | cordova.exec(win, fail, "CodePush", "isPendingUpdate", []); 117 | } 118 | } 119 | 120 | export = NativeAppInfo; -------------------------------------------------------------------------------- /www/package.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | "use strict"; 4 | 5 | /** 6 | * Base class for CodePush packages. 7 | */ 8 | class Package implements IPackage { 9 | deploymentKey: string; 10 | description: string; 11 | label: string; 12 | appVersion: string; 13 | isMandatory: boolean; 14 | packageHash: string; 15 | packageSize: number; 16 | failedInstall: boolean; 17 | } 18 | 19 | export = Package; -------------------------------------------------------------------------------- /www/syncStatus.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | "use strict"; 4 | 5 | /** 6 | * Defines the possible result and intermediate statuses of the window.codePush.sync operation. 7 | * The result statuses are final, mutually exclusive statuses of the sync operation. The operation will end with only one of the possible result statuses. 8 | * The intermediate statuses are not final, one or more of them can happen before sync ends, based on the options you use and user interaction. 9 | * 10 | * NOTE: Adding new statuses or changing old statuses requires an update to CodePush.sync(), which must know which callbacks are results and which are not! 11 | * Also, don't forget to change the TestMessage module in ServerUtils! 12 | * AND THE codePush.d.ts (typings) file!!! 13 | */ 14 | enum SyncStatus { 15 | /** 16 | * Result status - the application is up to date. 17 | */ 18 | UP_TO_DATE, 19 | 20 | /** 21 | * Result status - an update is available, it has been downloaded, unzipped and copied to the deployment folder. 22 | * After the completion of the callback invoked with SyncStatus.UPDATE_INSTALLED, the application will be reloaded with the updated code and resources. 23 | */ 24 | UPDATE_INSTALLED, 25 | 26 | /** 27 | * Result status - an optional update is available, but the user declined to install it. The update was not downloaded. 28 | */ 29 | UPDATE_IGNORED, 30 | 31 | /** 32 | * Result status - an error happened during the sync operation. This might be an error while communicating with the server, downloading or unziping the update. 33 | * The console logs should contain more information about what happened. No update has been applied in this case. 34 | */ 35 | ERROR, 36 | 37 | /** 38 | * Result status - there is an ongoing sync in progress, so this attempt to sync has been aborted. 39 | */ 40 | IN_PROGRESS, 41 | 42 | /** 43 | * Intermediate status - the plugin is about to check for updates. 44 | */ 45 | CHECKING_FOR_UPDATE, 46 | 47 | /** 48 | * Intermediate status - a user dialog is about to be displayed. This status will be reported only if user interaction is enabled. 49 | */ 50 | AWAITING_USER_ACTION, 51 | 52 | /** 53 | * Intermediate status - the update packages is about to be downloaded. 54 | */ 55 | DOWNLOADING_PACKAGE, 56 | 57 | /** 58 | * Intermediate status - the update package is about to be installed. 59 | */ 60 | INSTALLING_UPDATE 61 | } 62 | 63 | export = SyncStatus; --------------------------------------------------------------------------------