├── .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 |
43 |
Connecting to Device
44 |
Device is Ready
45 |
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 |
43 |
Connecting to Device
44 |
Device is Ready
45 |
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 |
--------------------------------------------------------------------------------