├── .commitlintrc.json ├── .github ├── ISSUE_TEMPLATE.md └── workflows │ └── release.yml ├── .gitignore ├── .npmignore ├── .releaserc.json ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── android ├── .gitignore ├── build-extras.gradle ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── microsoft │ └── cordova │ ├── CodePush.java │ ├── CodePushException.java │ ├── CodePushPackageManager.java │ ├── CodePushPackageMetadata.java │ ├── CodePushPreferences.java │ ├── CodePushReportingManager.java │ ├── InstallMode.java │ ├── InstallOptions.java │ ├── ReportingStatus.java │ ├── StatusReport.java │ ├── UpdateHashUtils.java │ └── Utilities.java ├── gulpfile.js ├── ios ├── Base64 │ ├── CodePushMF_Base64Additions.h │ └── CodePushMF_Base64Additions.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 ├── 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 ├── test ├── platform.ts ├── projectManager.ts ├── serverUtil.ts ├── template │ ├── config.xml │ └── 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 ├── tsconfig.json ├── tslint.json ├── typings └── replace.d.ts └── www ├── callbackUtil.ts ├── codePush.ts ├── codePushUtil.ts ├── fileUtil.ts ├── http.ts ├── httpRequester.ts ├── installMode.ts ├── installOptions.ts ├── localPackage.ts ├── nativeAppInfo.ts ├── nativeCodePushPlugin.ts ├── package.ts ├── remotePackage.ts ├── sdk.ts ├── syncOptions.ts └── syncStatus.ts /.commitlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "@commitlint/config-conventional" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | repository_dispatch: 4 | types: [semantic-release] 5 | jobs: 6 | release: 7 | name: Release 8 | runs-on: ubuntu-18.04 9 | steps: 10 | - name: Checkout 11 | uses: actions/checkout@v1 12 | - name: Setup Node.js 13 | uses: actions/setup-node@v1 14 | with: 15 | node-version: 12 16 | - name: Install dependencies 17 | run: npm ci 18 | - name: Build 19 | run: npx tsc 20 | - name: Lint 21 | run: npx tslint --project . 22 | - name: Release 23 | env: 24 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 25 | NPM_TOKEN: ${{ secrets.GITHUB_TOKEN }} 26 | run: npx semantic-release 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | #ignore the files built by npm install 3 | node_modules/* 4 | 5 | #ignore the compiled files 6 | bin/* 7 | 8 | #ignore the test output files 9 | test-* 10 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .npmignore 2 | .gitignore 3 | .commitlintrc.json 4 | .releaserc.json 5 | gulpfile.js 6 | node_modules/* 7 | samples/* 8 | test/* 9 | www/* 10 | bin/test/* 11 | -------------------------------------------------------------------------------- /.releaserc.json: -------------------------------------------------------------------------------- 1 | { 2 | "branches": [ 3 | "master", 4 | { 5 | "name": "capacitor", 6 | "prerelease": true 7 | } 8 | ], 9 | "plugins": [ 10 | "@semantic-release/commit-analyzer", 11 | "@semantic-release/release-notes-generator", 12 | "@semantic-release/npm", 13 | "@semantic-release/git" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Deprecated 2 | 3 | ⚠️ This project is no longer maintained. 4 | 5 | Please use https://github.com/mapiacompany/capacitor-codepush instead which is a maintained fork. 6 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /.gradle 3 | /gradlew 4 | /gradlew.bat 5 | /gradle 6 | /local.properties 7 | -------------------------------------------------------------------------------- /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 | def contents = new HashMap() 34 | def sizes = new HashMap() 35 | contents[""] = inAssetsDir.list() 36 | def tree = fileTree(dir: inAssetsDir) 37 | tree.visit { fileDetails -> 38 | if (fileDetails.isDirectory()) { 39 | contents[fileDetails.relativePath.toString()] = fileDetails.file.list() 40 | } else { 41 | sizes[fileDetails.relativePath.toString()] = fileDetails.file.length() 42 | } 43 | } 44 | 45 | outAssetsDir.mkdirs() 46 | outFile.withObjectOutputStream { oos -> 47 | oos.writeObject(contents) 48 | oos.writeObject(sizes) 49 | } 50 | } 51 | newTask.inputs.dir inAssetsDir 52 | newTask.outputs.file outFile 53 | def preBuildTask = tasks["preBuild"] 54 | preBuildTask.dependsOn(newTask) 55 | } 56 | 57 | android.buildTypes.each { 58 | // to prevent incorrect long value restoration from strings.xml we need to wrap it with double quotes 59 | // https://github.com/Microsoft/cordova-plugin-code-push/issues/264 60 | it.resValue "string", "CODE_PUSH_APK_BUILD_TIME", String.format("\"%d\"", System.currentTimeMillis()) 61 | } 62 | 63 | dependencies { 64 | compile 'com.nimbusds:nimbus-jose-jwt:5.1' 65 | } 66 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | jcenter() 4 | google() 5 | } 6 | dependencies { 7 | classpath 'com.android.tools.build:gradle:3.1.1' 8 | } 9 | } 10 | 11 | apply plugin: 'com.android.library' 12 | 13 | android { 14 | compileSdkVersion 27 15 | defaultConfig { 16 | minSdkVersion 21 17 | targetSdkVersion 27 18 | versionCode 1 19 | versionName "1.0" 20 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 21 | } 22 | buildTypes { 23 | release { 24 | minifyEnabled false 25 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 26 | } 27 | } 28 | lintOptions { 29 | abortOnError false 30 | } 31 | } 32 | 33 | repositories { 34 | google() 35 | jcenter() 36 | mavenCentral() 37 | maven { 38 | url "https://dl.bintray.com/ionic-team/capacitor" 39 | } 40 | } 41 | 42 | dependencies { 43 | implementation fileTree(include: ['*.jar'], dir: 'libs') 44 | implementation 'com.nimbusds:nimbus-jose-jwt:5.1' 45 | testImplementation 'junit:junit:4.12' 46 | androidTestImplementation 'com.android.support.test:runner:1.0.1' 47 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' 48 | implementation project(':capacitor-android') 49 | } 50 | 51 | task capCreateAssetManifest { 52 | def inAssetsDir = rootProject.file("app/src/main/assets") 53 | 54 | def outAssetsDir = inAssetsDir 55 | def outFile = new File(outAssetsDir, "cdvasset.manifest") 56 | 57 | doLast { 58 | def contents = new HashMap() 59 | def sizes = new HashMap() 60 | contents[""] = inAssetsDir.list() 61 | def tree = fileTree(dir: inAssetsDir) 62 | tree.visit { fileDetails -> 63 | if (fileDetails.isDirectory()) { 64 | contents[fileDetails.relativePath.toString()] = fileDetails.file.list() 65 | } else { 66 | sizes[fileDetails.relativePath.toString()] = fileDetails.file.length() 67 | } 68 | } 69 | 70 | outAssetsDir.mkdirs() 71 | outFile.withObjectOutputStream { oos -> 72 | oos.writeObject(contents) 73 | oos.writeObject(sizes) 74 | } 75 | } 76 | } 77 | 78 | preBuild.dependsOn capCreateAssetManifest 79 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /android/src/main/java/com/microsoft/cordova/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 | -------------------------------------------------------------------------------- /android/src/main/java/com/microsoft/cordova/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 | -------------------------------------------------------------------------------- /android/src/main/java/com/microsoft/cordova/CodePushReportingManager.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.cordova; 2 | 3 | import java.util.Locale; 4 | import android.app.Activity; 5 | import android.webkit.WebView; 6 | 7 | /** 8 | * Handles the native -> JS reporting mechanism. 9 | */ 10 | public class CodePushReportingManager { 11 | 12 | private Activity cordovaActivity; 13 | private CodePushPreferences codePushPreferences; 14 | private Boolean hasFailedReport = null; 15 | 16 | public CodePushReportingManager(Activity cordovaActivity, CodePushPreferences codePushPreferences) { 17 | this.cordovaActivity = cordovaActivity; 18 | this.codePushPreferences = codePushPreferences; 19 | } 20 | 21 | /** 22 | * Invokes the window.codePush.reportStatus JS function for the given webView. 23 | */ 24 | public void reportStatus(StatusReport statusReport, final WebView webView) { 25 | /* JS function to call: window.codePush.reportStatus(status: number, label: String, appVersion: String, currentDeploymentKey: String, previousLabelOrAppVersion?: string, previousDeploymentKey?: string) */ 26 | if (statusReport.deploymentKey == null || statusReport.deploymentKey.isEmpty()) { 27 | return; 28 | } 29 | 30 | // TODO: fix js call 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 | -------------------------------------------------------------------------------- /android/src/main/java/com/microsoft/cordova/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 | -------------------------------------------------------------------------------- /android/src/main/java/com/microsoft/cordova/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 | -------------------------------------------------------------------------------- /android/src/main/java/com/microsoft/cordova/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 | } -------------------------------------------------------------------------------- /android/src/main/java/com/microsoft/cordova/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 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | 21 | void CPLog(NSString *formatString, ...); 22 | @end 23 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | */ -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /ios/UpdateHashUtils.h: -------------------------------------------------------------------------------- 1 | @interface UpdateHashUtils : NSObject 2 | 3 | + (NSString*)getBinaryHash:(NSError**)error; 4 | + (NSString*)getHashForPath:(NSString*)path error:(NSError**)error; 5 | 6 | @end -------------------------------------------------------------------------------- /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 | if ([[self ignoredFilenames] containsObject:fileName]) { 33 | continue; 34 | } 35 | NSString* fullFilePath = [folderPath stringByAppendingPathComponent:fileName]; 36 | NSString* relativePath = [pathPrefix stringByAppendingPathComponent:fileName]; 37 | BOOL isDir = NO; 38 | if ([[NSFileManager defaultManager] fileExistsAtPath:fullFilePath 39 | isDirectory:&isDir] && isDir) { 40 | [self addFolderEntriesToManifest:fullFilePath 41 | pathPrefix:relativePath 42 | manifestEntries:manifestEntries 43 | error:error]; 44 | if (*error) { 45 | return; 46 | } 47 | } else { 48 | NSData* fileContents = [NSData dataWithContentsOfFile:fullFilePath]; 49 | NSString* fileContentsHash = [self computeHashForData:fileContents]; 50 | [manifestEntries addObject:[[relativePath stringByAppendingString:@":"] stringByAppendingString:fileContentsHash]]; 51 | } 52 | } 53 | } 54 | 55 | + (NSString*)computeFinalHashFromManifestEntries:(NSMutableArray*)manifest 56 | error:(NSError**)error 57 | { 58 | NSArray* sortedManifest = [manifest sortedArrayUsingSelector:@selector(compare:)]; 59 | NSData* manifestData = [NSJSONSerialization dataWithJSONObject:sortedManifest 60 | options:kNilOptions 61 | error:error]; 62 | if (*error) { 63 | return nil; 64 | } 65 | 66 | NSString* manifestString = [[NSString alloc] initWithData:manifestData 67 | encoding:NSUTF8StringEncoding]; 68 | // The JSON serialization turns path separators into "\/", e.g. "www\/images\/image.png" 69 | manifestString = [manifestString stringByReplacingOccurrencesOfString:@"\\/" 70 | withString:@"/"]; 71 | return [self computeHashForData:[NSData dataWithBytes:manifestString.UTF8String length:manifestString.length]]; 72 | } 73 | 74 | + (NSString*)computeHashForData:(NSData*)inputData 75 | { 76 | uint8_t digest[CC_SHA256_DIGEST_LENGTH]; 77 | CC_SHA256(inputData.bytes, (CC_LONG)inputData.length, digest); 78 | NSMutableString* inputHash = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2]; 79 | for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) { 80 | [inputHash appendFormat:@"%02x", digest[i]]; 81 | } 82 | 83 | return inputHash; 84 | } 85 | 86 | + (NSString*)getBinaryHash:(NSError**)error 87 | { 88 | return [self getHashForPath:[self binaryAssetsPath] error:error]; 89 | } 90 | 91 | + (NSString*)getHashForPath:(NSString*)path error:(NSError**)error 92 | { 93 | NSMutableArray* manifestEntries = [NSMutableArray array]; 94 | [self addFolderEntriesToManifest:path 95 | pathPrefix:@"www" 96 | manifestEntries:manifestEntries 97 | error:error]; 98 | return [self computeFinalHashFromManifestEntries:manifestEntries error:error]; 99 | } 100 | 101 | @end 102 | -------------------------------------------------------------------------------- /ios/Utilities.h: -------------------------------------------------------------------------------- 1 | @interface Utilities : NSObject 2 | 3 | + (NSString*)getApplicationVersion; 4 | + (NSString*)getApplicationTimestamp; 5 | + (NSDate*)getApplicationBuildTime; 6 | 7 | @end -------------------------------------------------------------------------------- /ios/Utilities.m: -------------------------------------------------------------------------------- 1 | #import "Utilities.h" 2 | 3 | @implementation Utilities 4 | 5 | + (NSString*)getApplicationVersion{ 6 | return [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; 7 | } 8 | 9 | + (NSString*)getApplicationTimestamp{ 10 | NSDate* applicationBuildTime = [self getApplicationBuildTime]; 11 | if (applicationBuildTime){ 12 | NSNumber* timestamp = [[NSNumber alloc] initWithDouble: floor([applicationBuildTime timeIntervalSince1970] * 1000)]; 13 | return [timestamp stringValue]; 14 | } 15 | 16 | return nil; 17 | } 18 | 19 | + (NSDate*)getApplicationBuildTime{ 20 | //get path for plist file to check modification date because iOS10.2 failed to get modification date of main bundle 21 | NSString *appPlistPath = [[NSBundle mainBundle] pathForResource:nil ofType:@"plist"]; 22 | NSDictionary *executableAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:appPlistPath error:nil]; 23 | NSDate *fileDate = [executableAttributes objectForKey:@"NSFileModificationDate"]; 24 | return fileDate; 25 | } 26 | 27 | void CPLog(NSString *formatString, ...) { 28 | va_list args; 29 | va_start(args, formatString); 30 | NSString *prependedFormatString = [NSString stringWithFormat:@"\n[CodePush] %@", formatString]; 31 | NSLogv(prependedFormatString, args); 32 | va_end(args); 33 | } 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@areo/capacitor-codepush", 3 | "version": "1.0.0-capacitor.3", 4 | "description": "CodePush Plugin for Capacitor", 5 | "main": "bin/www/codePush.js", 6 | "repository": "github:areo/capacitor-codepush", 7 | "publishConfig": { 8 | "registry": "https://npm.pkg.github.com/" 9 | }, 10 | "keywords": [ 11 | "capacitor", 12 | "code", 13 | "push", 14 | "ecosystem:capacitor", 15 | "capacitor-android", 16 | "capacitor-ios" 17 | ], 18 | "author": "Microsoft Corporation", 19 | "license": "MIT", 20 | "types": "./bin/www/codePush.d.ts", 21 | "devDependencies": { 22 | "@capacitor/core": "^1.3.0", 23 | "@commitlint/cli": "^8.2.0", 24 | "@commitlint/config-conventional": "^8.2.0", 25 | "@semantic-release/git": "^7.1.0-beta.11", 26 | "@types/assert": "^1.4.3", 27 | "@types/cordova": "0.0.34", 28 | "@types/mkdirp": "^0.5.2", 29 | "@types/mocha": "^5.2.7", 30 | "@types/node": "^12.7.2", 31 | "@types/power-assert": "^1.5.0", 32 | "@types/q": "^1.5.2", 33 | "archiver": "^3.1.1", 34 | "body-parser": "^1.19.0", 35 | "del": "^5.1.0", 36 | "express": "^4.17.1", 37 | "gulp": "^4.0.2", 38 | "gulp-insert": "^0.5.0", 39 | "gulp-tslint": "^8.1.4", 40 | "gulp-typescript": "^5.0.1", 41 | "husky": "^3.1.0", 42 | "mkdirp": "^0.5.1", 43 | "mocha": "^6.2.0", 44 | "mocha-junit-reporter": "^1.23.1", 45 | "q": "^1.5.1", 46 | "replace": "^1.1.1", 47 | "run-sequence": "^2.2.1", 48 | "semantic-release": "^16.0.0-beta.41", 49 | "tslint": "^5.19.0", 50 | "typescript": "^3.7.2" 51 | }, 52 | "dependencies": { 53 | "code-push": "^3.0.1" 54 | }, 55 | "capacitor": { 56 | "ios": { 57 | "src": "ios" 58 | }, 59 | "android": { 60 | "src": "android" 61 | } 62 | }, 63 | "husky": { 64 | "hooks": { 65 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /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/areo/capacitor-codepush/28e40d456e6520fb5a6030905bf0b4f21e1c443d/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/areo/capacitor-codepush/28e40d456e6520fb5a6030905bf0b4f21e1c443d/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(); -------------------------------------------------------------------------------- /test/serverUtil.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Class used to mock the codePush.checkForUpdate() response from the server. 3 | */ 4 | export class CheckForUpdateResponseMock { 5 | downloadURL: string; 6 | isAvailable: boolean; 7 | packageSize: number; 8 | updateAppVersion: boolean; 9 | appVersion: string; 10 | description: string; 11 | label: string; 12 | packageHash: string; 13 | isMandatory: boolean; 14 | } 15 | 16 | /** 17 | * The model class of the codePush.checkForUpdate() request to the server. 18 | */ 19 | export class UpdateCheckRequestMock { 20 | deploymentKey: string; 21 | appVersion: string; 22 | packageHash: string; 23 | isCompanion: boolean; 24 | } 25 | 26 | /** 27 | * Contains all the messages sent from the application to the mock server during tests. 28 | */ 29 | export class TestMessage { 30 | public static CHECK_UP_TO_DATE = "CHECK_UP_TO_DATE"; 31 | public static CHECK_UPDATE_AVAILABLE = "CHECK_UPDATE_AVAILABLE"; 32 | public static CHECK_ERROR = "CHECK_ERROR"; 33 | public static DOWNLOAD_SUCCEEDED = "DOWNLOAD_SUCCEEDED"; 34 | public static DOWNLOAD_ERROR = "DOWNLOAD_ERROR"; 35 | public static UPDATE_INSTALLED = "UPDATE_INSTALLED"; 36 | public static INSTALL_ERROR = "INSTALL_ERROR"; 37 | public static DEVICE_READY_AFTER_UPDATE = "DEVICE_READY_AFTER_UPDATE"; 38 | public static UPDATE_FAILED_PREVIOUSLY = "UPDATE_FAILED_PREVIOUSLY"; 39 | public static NOTIFY_APP_READY_SUCCESS = "NOTIFY_APP_READY_SUCCESS"; 40 | public static NOTIFY_APP_READY_FAILURE = "NOTIFY_APP_READY_FAILURE"; 41 | public static SKIPPED_NOTIFY_APPLICATION_READY = "SKIPPED_NOTIFY_APPLICATION_READY"; 42 | public static SYNC_STATUS = "SYNC_STATUS"; 43 | public static RESTART_SUCCEEDED = "RESTART_SUCCEEDED"; 44 | public static RESTART_FAILED = "RESTART_FAILED"; 45 | public static PENDING_PACKAGE = "PENDING_PACKAGE"; 46 | public static CURRENT_PACKAGE = "CURRENT_PACKAGE"; 47 | 48 | public static SYNC_UP_TO_DATE = 0; 49 | public static SYNC_UPDATE_INSTALLED = 1; 50 | public static SYNC_UPDATE_IGNORED = 2; 51 | public static SYNC_ERROR = 3; 52 | public static SYNC_IN_PROGRESS = 4; 53 | public static SYNC_CHECKING_FOR_UPDATE = 5; 54 | public static SYNC_AWAITING_USER_ACTION = 6; 55 | public static SYNC_DOWNLOADING_PACKAGE = 7; 56 | public static SYNC_INSTALLING_UPDATE = 8; 57 | } 58 | 59 | /** 60 | * Contains all the messages sent from the mock server back to the application during tests. 61 | */ 62 | export class TestMessageResponse { 63 | public static SKIP_NOTIFY_APPLICATION_READY = "SKIP_NOTIFY_APPLICATION_READY"; 64 | } 65 | 66 | /** 67 | * Defines the messages sent from the application to the mock server during tests. 68 | */ 69 | export class AppMessage { 70 | message: string; 71 | args: any[]; 72 | 73 | constructor(message: string, args: any[]) { 74 | this.message = message; 75 | this.args = args; 76 | } 77 | 78 | static fromString(message: string): AppMessage { 79 | return new AppMessage(message, undefined); 80 | } 81 | } 82 | 83 | /** 84 | * Checks if two messages are equal. 85 | */ 86 | export function areEqual(m1: AppMessage, m2: AppMessage): boolean { 87 | /* compare objects */ 88 | if (m1 === m2) { 89 | return true; 90 | } 91 | 92 | /* compare messages */ 93 | if (!m1 || !m2 || m1.message !== m2.message) { 94 | return false; 95 | } 96 | 97 | /* compare arguments */ 98 | if (m1.args === m2.args) { 99 | return true; 100 | } 101 | 102 | if (!m1.args || !m2.args || m1.args.length !== m2.args.length) { 103 | return false; 104 | } 105 | 106 | for (var i = 0; i < m1.args.length; i++) { 107 | if (m1.args[i] !== m2.args[i]) { 108 | return false; 109 | } 110 | } 111 | 112 | return true; 113 | } 114 | -------------------------------------------------------------------------------- /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/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(); -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "alwaysStrict": true, 4 | "declaration": true, 5 | "noImplicitAny": true, 6 | "noEmitOnError": true, 7 | "target": "ES2017", 8 | "module": "commonjs", 9 | "sourceMap": false, 10 | "outDir": "bin", 11 | "removeComments": true 12 | }, 13 | "include": [ 14 | "www/**/*.ts", 15 | "test/**/*.ts" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /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/callbackUtil.ts: -------------------------------------------------------------------------------- 1 | export interface Callback { (error: Error, parameter: T): void; } 2 | export interface SuccessCallback { (result?: T): void; } 3 | export interface ErrorCallback { (error?: Error): void; } 4 | -------------------------------------------------------------------------------- /www/codePushUtil.ts: -------------------------------------------------------------------------------- 1 | import { Callback, ErrorCallback, SuccessCallback } from "./callbackUtil"; 2 | 3 | /** 4 | * Callback / error / logging utilities. 5 | */ 6 | export class CodePushUtil { 7 | 8 | /** 9 | * Tag used for logging to the console. 10 | */ 11 | private static TAG: string = "[CodePush]"; 12 | 13 | /** 14 | * Performs a copy of all members of fromParameter to toParameter, with the condition that they are unassigned or null in toParameter. 15 | */ 16 | public static copyUnassignedMembers(fromParameter: any, toParameter: any) { 17 | for (let key in fromParameter) { 18 | if ((toParameter)[key] === undefined || (toParameter)[key] === null) { 19 | (toParameter)[key] = (fromParameter)[key]; 20 | } 21 | } 22 | } 23 | 24 | /** 25 | * Given two Cordova style callbacks for success and error, this function returns a node.js 26 | * style callback where the error is the first parameter and the result the second. 27 | */ 28 | public static getNodeStyleCallbackFor(successCallback: SuccessCallback, errorCallback: { (error?: any): void; }): Callback { 29 | return (error: any, result: T) => { 30 | if (error) { 31 | errorCallback && errorCallback(error); 32 | } else { 33 | successCallback && successCallback(result); 34 | } 35 | }; 36 | } 37 | 38 | /** 39 | * Gets the message of an error, if any. Otherwise it returns the empty string. 40 | */ 41 | public static getErrorMessage(e: Error): string { 42 | return e && e.message || e && e.toString() || ""; 43 | } 44 | 45 | /** 46 | * Logs the error to the console and then forwards it to the provided ErrorCallback, if any. 47 | * TODO: remove me 48 | */ 49 | public static invokeErrorCallback = (error: Error, errorCallback: ErrorCallback): void => { 50 | CodePushUtil.logError(null, error); 51 | errorCallback && errorCallback(error); 52 | } 53 | 54 | /** 55 | * Logs the error to the console and then throws the error. 56 | */ 57 | public static throwError = (error: Error): void => { 58 | CodePushUtil.logError(null, error); 59 | throw error; 60 | } 61 | 62 | /** 63 | * Logs a message using the CodePush tag. 64 | */ 65 | public static logMessage(msg: string): void { 66 | console.log(CodePushUtil.TAG + " " + msg); 67 | } 68 | 69 | /** 70 | * Logs an error message using the CodePush tag. 71 | */ 72 | public static logError(message: String, error?: Error): void { 73 | const errorMessage = `${message || ""} ${CodePushUtil.getErrorMessage(error)}`; 74 | const stackTrace = error && error.stack ? `. StackTrace: ${error.stack}` : ""; 75 | console.error(`${CodePushUtil.TAG} ${errorMessage}${stackTrace}`); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /www/http.ts: -------------------------------------------------------------------------------- 1 | import { Callback } from "./callbackUtil"; 2 | 3 | export const enum Verb { 4 | GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, CONNECT, PATCH 5 | } 6 | 7 | export interface Response { 8 | statusCode: number; 9 | body?: string; 10 | } 11 | 12 | export interface Requester { 13 | request(verb: Verb, url: string, callback: Callback): void; 14 | request(verb: Verb, url: string, requestBody: string, callback: Callback): void; 15 | } 16 | -------------------------------------------------------------------------------- /www/httpRequester.ts: -------------------------------------------------------------------------------- 1 | import { Http } from "code-push/script/acquisition-sdk"; 2 | import { Callback } from "./callbackUtil"; 3 | 4 | 5 | /** 6 | * XMLHttpRequest-based implementation of Http.Requester. 7 | */ 8 | export class HttpRequester implements Http.Requester { 9 | 10 | private contentType: string; 11 | 12 | constructor(contentType?: string) { 13 | this.contentType = contentType; 14 | } 15 | 16 | public request(verb: Http.Verb, url: string, callbackOrRequestBody: Callback | string, callback?: Callback): void { 17 | var requestBody: string; 18 | var requestCallback: Callback = callback; 19 | 20 | if (!requestCallback && typeof callbackOrRequestBody === "function") { 21 | requestCallback = >callbackOrRequestBody; 22 | } 23 | 24 | if (typeof callbackOrRequestBody === "string") { 25 | requestBody = callbackOrRequestBody; 26 | } 27 | 28 | var xhr = new XMLHttpRequest(); 29 | var methodName = this.getHttpMethodName(verb); 30 | xhr.onreadystatechange = function(): void { 31 | if (xhr.readyState === 4) { 32 | var response: Http.Response = { statusCode: xhr.status, body: xhr.responseText }; 33 | requestCallback && requestCallback(null, response); 34 | } 35 | }; 36 | xhr.open(methodName, url, true); 37 | if (this.contentType) { 38 | xhr.setRequestHeader("Content-Type", this.contentType); 39 | } 40 | 41 | xhr.setRequestHeader("X-CodePush-Plugin-Name", "capacitor-plugin-code-push"); 42 | xhr.setRequestHeader("X-CodePush-Plugin-Version", "1.11.13"); 43 | xhr.setRequestHeader("X-CodePush-SDK-Version", "2.0.6"); 44 | xhr.send(requestBody); 45 | } 46 | 47 | /** 48 | * Gets the HTTP method name as a string. 49 | * The reason for which this is needed is because the Http.Verb enum is defined as a constant => Verb[Verb.METHOD_NAME] is not defined in the compiled JS. 50 | */ 51 | private getHttpMethodName(verb: Http.Verb): string { 52 | switch (verb) { 53 | case Http.Verb.GET: 54 | return "GET"; 55 | case Http.Verb.CONNECT: 56 | return "CONNECT"; 57 | case Http.Verb.DELETE: 58 | return "DELETE"; 59 | case Http.Verb.HEAD: 60 | return "HEAD"; 61 | case Http.Verb.OPTIONS: 62 | return "OPTIONS"; 63 | case Http.Verb.PATCH: 64 | return "PATCH"; 65 | case Http.Verb.POST: 66 | return "POST"; 67 | case Http.Verb.PUT: 68 | return "PUT"; 69 | case Http.Verb.TRACE: 70 | return "TRACE"; 71 | default: 72 | return null; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /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 default InstallMode; 22 | -------------------------------------------------------------------------------- /www/installOptions.ts: -------------------------------------------------------------------------------- 1 | import InstallMode from "./installMode"; 2 | 3 | /** 4 | * Defines the install operation options. 5 | */ 6 | export interface InstallOptions { 7 | /** 8 | * Used to specify the InstallMode used for the install operation. This is optional and defaults to InstallMode.ON_NEXT_RESTART. 9 | */ 10 | installMode?: InstallMode; 11 | 12 | /** 13 | * If installMode === ON_NEXT_RESUME, the minimum amount of time (in seconds) which needs to pass with the app in the background before an update install occurs when the app is resumed. 14 | */ 15 | minimumBackgroundDuration?: number; 16 | 17 | /** 18 | * Used to specify the InstallMode used for the install operation if the update is mandatory. This is optional and defaults to InstallMode.IMMEDIATE. 19 | */ 20 | mandatoryInstallMode?: InstallMode; 21 | } 22 | -------------------------------------------------------------------------------- /www/nativeAppInfo.ts: -------------------------------------------------------------------------------- 1 | import { Plugins } from "@capacitor/core"; 2 | import { NativeCodePushPlugin } from "./nativeCodePushPlugin"; 3 | 4 | const NativeCodePush = Plugins.CodePush as NativeCodePushPlugin; 5 | 6 | const DefaultServerUrl: string = "https://codepush.azurewebsites.net/"; 7 | 8 | /** 9 | * Provides information about the native app. 10 | */ 11 | export class NativeAppInfo { 12 | 13 | /** 14 | * Gets the application build timestamp. 15 | */ 16 | public static async getApplicationBuildTime(): Promise { 17 | try { 18 | const result = await NativeCodePush.getNativeBuildTime(); 19 | return result.value; 20 | } catch (e) { 21 | throw new Error("Could not get application timestamp."); 22 | } 23 | } 24 | 25 | /** 26 | * Gets the application version. 27 | */ 28 | public static async getApplicationVersion(): Promise { 29 | try { 30 | const result = await NativeCodePush.getAppVersion(); 31 | return result.value; 32 | } catch (e) { 33 | throw new Error("Could not get application version."); 34 | } 35 | } 36 | 37 | /** 38 | * Gets a hash of the `www` folder contents compiled in the app store binary. 39 | */ 40 | public static async getBinaryHash(): Promise { 41 | try { 42 | const result = await NativeCodePush.getBinaryHash(); 43 | return result.value; 44 | } catch (e) { 45 | throw new Error("Could not get binary hash."); 46 | } 47 | } 48 | 49 | /** 50 | * Gets the server URL from config.xml by calling into the native platform. 51 | */ 52 | public static async getServerURL(): Promise { 53 | try { 54 | const result = await NativeCodePush.getServerURL(); 55 | return result.value; 56 | } catch (e) { 57 | return DefaultServerUrl; 58 | } 59 | } 60 | 61 | /** 62 | * Gets the deployment key from config.xml by calling into the native platform. 63 | */ 64 | public static async getDeploymentKey(): Promise { 65 | try { 66 | const result = await NativeCodePush.getDeploymentKey(); 67 | return result.value; 68 | } catch (e) { 69 | throw new Error("Deployment key not found."); 70 | } 71 | } 72 | 73 | /** 74 | * Checks if a package update was previously attempted but failed for a given package hash. 75 | * Every reverted update is stored such that the application developer has the option to ignore 76 | * updates that previously failed. This way, an infinite update loop can be prevented in case of a bad update package. 77 | */ 78 | public static async isFailedUpdate(packageHash: string): Promise { 79 | try { 80 | const result = await NativeCodePush.isFailedUpdate({packageHash}); 81 | return result.value; 82 | } catch (e) { 83 | /* In case of an error, return false. */ 84 | return false; 85 | } 86 | } 87 | 88 | /** 89 | * Checks if this is the first application run of a package after it has been applied. 90 | * The didUpdateCallback callback can be used for migrating data from the old app version to the new one. 91 | * 92 | * @param packageHash The hash value of the package. 93 | * @returns Whether it is the first run after an update. 94 | */ 95 | public static async isFirstRun(packageHash: string): Promise { 96 | try { 97 | const result = await NativeCodePush.isFirstRun({packageHash}); 98 | return result.value; 99 | } catch (e) { 100 | /* In case of an error, return false. */ 101 | return false; 102 | } 103 | } 104 | 105 | /** 106 | * Checks with the native side if there is a pending update. 107 | */ 108 | public static async isPendingUpdate(): Promise { 109 | try { 110 | const result = await NativeCodePush.isPendingUpdate(); 111 | return result.value; 112 | } catch (e) { 113 | /* In case of an error, return false. */ 114 | return false; 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /www/nativeCodePushPlugin.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for Apache Cordova CodePush plugin. 2 | // Project: https://github.com/Microsoft/cordova-plugin-code-push 3 | // 4 | // Copyright (c) Microsoft Corporation 5 | // All rights reserved. 6 | // Licensed under the MIT license. 7 | 8 | import { InstallOptions } from "./installOptions"; 9 | 10 | interface StatusReport { 11 | status: number; 12 | label: string; 13 | appVersion: string; 14 | deploymentKey: string; 15 | previousLabelOrAppVersion: string; 16 | previousDeploymentKey: string; 17 | } 18 | 19 | interface PluginCallResponse { 20 | value: T; 21 | } 22 | 23 | interface NativeDecodeSignatureOptions { 24 | publicKey: string; 25 | signature: string; 26 | } 27 | 28 | interface NativePathOptions { 29 | path: string; 30 | } 31 | 32 | interface NativeHashOptions { 33 | packageHash: string; 34 | } 35 | 36 | interface NativeInstallOptions extends InstallOptions { 37 | startLocation: string; 38 | } 39 | 40 | interface NativeUnzipOptions extends InstallOptions { 41 | zipFile: string; 42 | targetDirectory: string; 43 | } 44 | 45 | interface NativeStatusReportOptions { 46 | statusReport: StatusReport; 47 | } 48 | 49 | export interface NativeCodePushPlugin { 50 | getDeploymentKey(): Promise>; 51 | getServerURL(): Promise>; 52 | getPublicKey(): Promise>; 53 | decodeSignature(options: NativeDecodeSignatureOptions): Promise>; 54 | getBinaryHash(): Promise>; 55 | getPackageHash(options: NativePathOptions): Promise>; 56 | notifyApplicationReady(): Promise; 57 | isFirstRun(options: NativeHashOptions): Promise>; 58 | isPendingUpdate(): Promise>; 59 | isFailedUpdate(options: NativeHashOptions): Promise>; 60 | install(options: NativeInstallOptions): Promise; 61 | reportFailed(options: NativeStatusReportOptions): Promise; 62 | reportSucceeded(options: NativeStatusReportOptions): Promise; 63 | restartApplication(): Promise; 64 | preInstall(options: NativeInstallOptions): Promise; 65 | getAppVersion(): Promise>; 66 | getNativeBuildTime(): Promise>; 67 | unzip(options: NativeUnzipOptions): Promise; 68 | 69 | addListener(eventName: "codePushStatus", listenerFunc: (info: any) => void): void; 70 | } 71 | 72 | -------------------------------------------------------------------------------- /www/package.ts: -------------------------------------------------------------------------------- 1 | import { SuccessCallback } from "./callbackUtil"; 2 | import InstallMode from "./installMode"; 3 | import { InstallOptions } from "./installOptions"; 4 | 5 | /** 6 | * Defines a package. All fields are non-nullable, except when retrieving the currently running package on the first run of the app, 7 | * in which case only the appVersion is compulsory. 8 | * 9 | * !! THIS TYPE IS READ FROM NATIVE CODE AS WELL. ANY CHANGES TO THIS INTERFACE NEEDS TO BE UPDATED IN NATIVE CODE !! 10 | */ 11 | export interface IPackage { 12 | deploymentKey: string; 13 | description: string; 14 | label: string; 15 | appVersion: string; 16 | isMandatory: boolean; 17 | packageHash: string; 18 | packageSize: number; 19 | failedInstall: boolean; 20 | } 21 | 22 | /** 23 | * Defines the format of the DownloadProgress object, used to send periodical update notifications on the progress of the update download. 24 | */ 25 | export interface DownloadProgress { 26 | totalBytes: number; 27 | receivedBytes: number; 28 | } 29 | 30 | /** 31 | * Defines a remote package, which represents an update package available for download. 32 | */ 33 | export interface IRemotePackage extends IPackage { 34 | /** 35 | * The URL at which the package is available for download. 36 | */ 37 | downloadUrl: string; 38 | 39 | /** 40 | * Downloads the package update from the CodePush service. 41 | * 42 | * @param downloadProgress Optional callback invoked during the download process. It is called several times with one DownloadProgress parameter. 43 | * @returns the downloaded package information, once the download completed successfully. 44 | */ 45 | download(downloadProgress?: SuccessCallback): Promise; 46 | 47 | /** 48 | * Aborts the current download session, previously started with download(). 49 | */ 50 | abortDownload(): Promise; 51 | } 52 | 53 | /** 54 | * Defines a local package. 55 | * 56 | * !! THIS TYPE IS READ FROM NATIVE CODE AS WELL. ANY CHANGES TO THIS INTERFACE NEEDS TO BE UPDATED IN NATIVE CODE !! 57 | */ 58 | export interface ILocalPackage extends IPackage { 59 | /** 60 | * The local storage path where this package is located. 61 | */ 62 | localPath: string; 63 | 64 | /** 65 | * Indicates if the current application run is the first one after the package was applied. 66 | */ 67 | isFirstRun: boolean; 68 | 69 | /** 70 | * Applies this package to the application. The application will be reloaded with this package and on every application launch this package will be loaded. 71 | * On the first run after the update, the application will wait for a codePush.notifyApplicationReady() call. Once this call is made, the install operation is considered a success. 72 | * Otherwise, the install operation will be marked as failed, and the application is reverted to its previous version on the next run. 73 | * 74 | * @param installOptions Optional parameter used for customizing the installation behavior. 75 | * @returns the install mode. 76 | */ 77 | install(installOptions?: InstallOptions): Promise; 78 | } 79 | 80 | /** 81 | * Defines the JSON format of the current package information file. 82 | * This file is stored in the local storage of the device and persists between store updates and code-push updates. 83 | * 84 | * !! THIS FILE IS READ FROM NATIVE CODE AS WELL. ANY CHANGES TO THIS INTERFACE NEEDS TO BE UPDATED IN NATIVE CODE !! 85 | */ 86 | export interface IPackageInfoMetadata extends ILocalPackage { 87 | nativeBuildTime: string; 88 | } 89 | 90 | /** 91 | * Base class for CodePush packages. 92 | */ 93 | export class Package implements IPackage { 94 | deploymentKey: string; 95 | description: string; 96 | label: string; 97 | appVersion: string; 98 | isMandatory: boolean; 99 | packageHash: string; 100 | packageSize: number; 101 | failedInstall: boolean; 102 | } 103 | -------------------------------------------------------------------------------- /www/remotePackage.ts: -------------------------------------------------------------------------------- 1 | import { FilesystemDirectory, Plugins } from "@capacitor/core"; 2 | import { SuccessCallback } from "./callbackUtil"; 3 | import { CodePushUtil } from "./codePushUtil"; 4 | import { LocalPackage } from "./localPackage"; 5 | import { NativeAppInfo } from "./nativeAppInfo"; 6 | import { DownloadProgress, ILocalPackage, IRemotePackage, Package } from "./package"; 7 | import { Sdk } from "./sdk"; 8 | 9 | const { Filesystem, FileTransfer } = Plugins; 10 | 11 | /** 12 | * Defines a remote package, which represents an update package available for download. 13 | */ 14 | export class RemotePackage extends Package implements IRemotePackage { 15 | 16 | /** 17 | * The URL at which the package is available for download. 18 | */ 19 | public downloadUrl: string; 20 | 21 | /** 22 | * Downloads the package update from the CodePush service. 23 | * TODO: implement download progress 24 | * 25 | * @param downloadSuccess Called with one parameter, the downloaded package information, once the download completed successfully. 26 | * @param downloadError Optional callback invoked in case of an error. 27 | * @param downloadProgress Optional callback invoked during the download process. It is called several times with one DownloadProgress parameter. 28 | */ 29 | public async download(downloadProgress?: SuccessCallback): Promise { 30 | CodePushUtil.logMessage("Downloading update"); 31 | if (!this.downloadUrl) { 32 | CodePushUtil.throwError(new Error("The remote package does not contain a download URL.")); 33 | return; 34 | } 35 | 36 | const dataDirectory = await Filesystem.getUri({directory: FilesystemDirectory.Data, path: ""}); 37 | const file = dataDirectory.uri + "/" + LocalPackage.DownloadDir + "/" + LocalPackage.PackageUpdateFileName; 38 | 39 | try { 40 | await FileTransfer.download({source: this.downloadUrl, target: file}); 41 | } catch (e) { 42 | CodePushUtil.throwError(new Error("An error occured while downloading the package. " + (e && e.message) ? e.message : "")); 43 | return; 44 | } 45 | 46 | const installFailed = await NativeAppInfo.isFailedUpdate(this.packageHash); 47 | const localPackage = new LocalPackage(); 48 | localPackage.deploymentKey = this.deploymentKey; 49 | localPackage.description = this.description; 50 | localPackage.label = this.label; 51 | localPackage.appVersion = this.appVersion; 52 | localPackage.isMandatory = this.isMandatory; 53 | localPackage.packageHash = this.packageHash; 54 | localPackage.isFirstRun = false; 55 | localPackage.failedInstall = installFailed; 56 | localPackage.localPath = file; 57 | 58 | CodePushUtil.logMessage("Package download success: " + JSON.stringify(localPackage)); 59 | Sdk.reportStatusDownload(localPackage, localPackage.deploymentKey); 60 | 61 | return localPackage; 62 | } 63 | 64 | /** 65 | * Aborts the current download session, previously started with download(). 66 | */ 67 | public async abortDownload(): Promise { 68 | // TODO: implement download abort 69 | throw new Error("Not implemented"); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /www/sdk.ts: -------------------------------------------------------------------------------- 1 | import { Plugins } from "@capacitor/core"; 2 | import { AcquisitionManager, Configuration } from "code-push/script/acquisition-sdk"; 3 | import { Callback } from "./callbackUtil"; 4 | import { HttpRequester } from "./httpRequester"; 5 | import { NativeAppInfo } from "./nativeAppInfo"; 6 | import { IPackage } from "./package"; 7 | 8 | const { Device } = Plugins; 9 | 10 | /** 11 | * Interacts with the CodePush Acquisition SDK. 12 | */ 13 | export class Sdk { 14 | 15 | private static DefaultAcquisitionManager: AcquisitionManager; 16 | private static DefaultConfiguration: Configuration; 17 | 18 | /** 19 | * Reads the CodePush configuration and creates an AcquisitionManager instance using it. 20 | */ 21 | public static async getAcquisitionManager(userDeploymentKey?: string, contentType?: string): Promise { 22 | const resolveManager = (): Promise => { 23 | if (userDeploymentKey !== Sdk.DefaultConfiguration.deploymentKey || contentType) { 24 | var customConfiguration: Configuration = { 25 | deploymentKey: userDeploymentKey || Sdk.DefaultConfiguration.deploymentKey, 26 | serverUrl: Sdk.DefaultConfiguration.serverUrl, 27 | ignoreAppVersion: Sdk.DefaultConfiguration.ignoreAppVersion, 28 | appVersion: Sdk.DefaultConfiguration.appVersion, 29 | clientUniqueId: Sdk.DefaultConfiguration.clientUniqueId 30 | }; 31 | var requester = new HttpRequester(contentType); 32 | var customAcquisitionManager: AcquisitionManager = new AcquisitionManager(requester, customConfiguration); 33 | return Promise.resolve(customAcquisitionManager); 34 | } else if (Sdk.DefaultConfiguration.deploymentKey) { 35 | return Promise.resolve(Sdk.DefaultAcquisitionManager); 36 | } else { 37 | return Promise.reject(new Error("No deployment key provided, please provide a default one in your config.xml or specify one in the call to checkForUpdate() or sync().")); 38 | } 39 | }; 40 | 41 | if (Sdk.DefaultAcquisitionManager) { 42 | return resolveManager(); 43 | } else { 44 | let serverUrl = null; 45 | try { 46 | serverUrl = await NativeAppInfo.getServerURL(); 47 | } catch (e) { 48 | throw new Error("Could not get the CodePush configuration. Please check your config.xml file."); 49 | } 50 | 51 | let appVersion = null; 52 | try { 53 | appVersion = await NativeAppInfo.getApplicationVersion(); 54 | } catch (e) { 55 | throw new Error("Could not get the app version. Please check your config.xml file."); 56 | } 57 | 58 | let deploymentKey = null; 59 | try { 60 | deploymentKey = await NativeAppInfo.getDeploymentKey(); 61 | } catch (e) {} 62 | 63 | const device = await Device.getInfo(); 64 | Sdk.DefaultConfiguration = { 65 | deploymentKey, 66 | serverUrl, 67 | ignoreAppVersion: false, 68 | appVersion, 69 | clientUniqueId: device.uuid 70 | }; 71 | 72 | if (deploymentKey) { 73 | Sdk.DefaultAcquisitionManager = new AcquisitionManager(new HttpRequester(), Sdk.DefaultConfiguration); 74 | } 75 | 76 | return resolveManager(); 77 | } 78 | } 79 | 80 | /** 81 | * Reports the deployment status to the CodePush server. 82 | */ 83 | public static async reportStatusDeploy(pkg?: IPackage, status?: string, currentDeploymentKey?: string, previousLabelOrAppVersion?: string, previousDeploymentKey?: string, callback?: Callback) { 84 | try { 85 | const acquisitionManager = await Sdk.getAcquisitionManager(currentDeploymentKey, "application/json"); 86 | acquisitionManager.reportStatusDeploy(pkg, status, previousLabelOrAppVersion, previousDeploymentKey, callback); 87 | } catch (e) { 88 | callback && callback(e, null); 89 | } 90 | } 91 | 92 | /** 93 | * Reports the download status to the CodePush server. 94 | */ 95 | public static async reportStatusDownload(pkg: IPackage, deploymentKey?: string, callback?: Callback) { 96 | try { 97 | const acquisitionManager = await Sdk.getAcquisitionManager(deploymentKey, "application/json"); 98 | acquisitionManager.reportStatusDownload(pkg, callback); 99 | } catch (e) { 100 | callback && callback(new Error("An error occured while reporting the download status. " + e), null); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /www/syncOptions.ts: -------------------------------------------------------------------------------- 1 | import { InstallOptions } from "./installOptions"; 2 | 3 | /** 4 | * Defines the sync operation options. 5 | */ 6 | export interface SyncOptions extends InstallOptions { 7 | /** 8 | * Optional boolean flag. If set, previous updates which were rolled back will be ignored. Defaults to true. 9 | */ 10 | ignoreFailedUpdates?: boolean; 11 | 12 | /** 13 | * Used to enable, disable or customize the user interaction during sync. 14 | * If set to false, user interaction will be disabled. If set to true, the user will be alerted or asked to confirm new updates, based on whether the update is mandatory. 15 | * To customize the user dialog, this option can be set to a custom UpdateDialogOptions instance. 16 | */ 17 | updateDialog?: boolean | UpdateDialogOptions; 18 | 19 | /** 20 | * Overrides the config.xml deployment key when checking for updates. 21 | */ 22 | deploymentKey?: string; 23 | } 24 | 25 | /** 26 | * Defines the configuration options for the alert or confirmation dialog 27 | */ 28 | export interface UpdateDialogOptions { 29 | /** 30 | * If a mandatory update is available and this option is set, the message will be displayed to the user in an alert dialog before downloading and installing the update. 31 | * The user will not be able to cancel the operation, since the update is mandatory. 32 | */ 33 | mandatoryUpdateMessage?: string; 34 | 35 | /** 36 | * If an optional update is available and this option is set, the message will be displayed to the user in a confirmation dialog. 37 | * If the user confirms the update, it will be downloaded and installed. Otherwise, the update update is not downloaded. 38 | */ 39 | optionalUpdateMessage?: string; 40 | 41 | /** 42 | * The title of the dialog box used for interacting with the user in case of a mandatory or optional update. 43 | * This title will only be used if at least one of mandatoryUpdateMessage or optionalUpdateMessage options are set. 44 | */ 45 | updateTitle?: string; 46 | 47 | /** 48 | * The label of the confirmation button in case of an optional update. 49 | */ 50 | optionalInstallButtonLabel?: string; 51 | 52 | /** 53 | * The label of the cancel button in case of an optional update. 54 | */ 55 | optionalIgnoreButtonLabel?: string; 56 | 57 | /** 58 | * The label of the continue button in case of a mandatory update. 59 | */ 60 | mandatoryContinueButtonLabel?: string; 61 | 62 | /** 63 | * Flag indicating if the update description provided by the CodePush server should be displayed in the dialog box appended to the update message. 64 | */ 65 | appendReleaseDescription?: boolean; 66 | 67 | /** 68 | * Optional prefix to add to the release description. 69 | */ 70 | descriptionPrefix?: string; 71 | } 72 | -------------------------------------------------------------------------------- /www/syncStatus.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Defines the possible result and intermediate statuses of the window.codePush.sync operation. 3 | * The result statuses are final, mutually exclusive statuses of the sync operation. The operation will end with only one of the possible result statuses. 4 | * 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. 5 | * 6 | * 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! 7 | * Also, don't forget to change the TestMessage module in ServerUtils! 8 | * AND THE codePush.d.ts (typings) file!!! 9 | */ 10 | export enum SyncStatus { 11 | /** 12 | * Result status - the application is up to date. 13 | */ 14 | UP_TO_DATE, 15 | 16 | /** 17 | * Result status - an update is available, it has been downloaded, unzipped and copied to the deployment folder. 18 | * After the completion of the callback invoked with SyncStatus.UPDATE_INSTALLED, the application will be reloaded with the updated code and resources. 19 | */ 20 | UPDATE_INSTALLED, 21 | 22 | /** 23 | * Result status - an optional update is available, but the user declined to install it. The update was not downloaded. 24 | */ 25 | UPDATE_IGNORED, 26 | 27 | /** 28 | * Result status - an error happened during the sync operation. This might be an error while communicating with the server, downloading or unziping the update. 29 | * The console logs should contain more information about what happened. No update has been applied in this case. 30 | */ 31 | ERROR, 32 | 33 | /** 34 | * Result status - there is an ongoing sync in progress, so this attempt to sync has been aborted. 35 | */ 36 | IN_PROGRESS, 37 | 38 | /** 39 | * Intermediate status - the plugin is about to check for updates. 40 | */ 41 | CHECKING_FOR_UPDATE, 42 | 43 | /** 44 | * Intermediate status - a user dialog is about to be displayed. This status will be reported only if user interaction is enabled. 45 | */ 46 | AWAITING_USER_ACTION, 47 | 48 | /** 49 | * Intermediate status - the update packages is about to be downloaded. 50 | */ 51 | DOWNLOADING_PACKAGE, 52 | 53 | /** 54 | * Intermediate status - the update package is about to be installed. 55 | */ 56 | INSTALLING_UPDATE 57 | } 58 | --------------------------------------------------------------------------------