@webos-tools/cli 3.0.4 → 3.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintignore +1 -1
- package/.eslintrc.js +52 -52
- package/APIs.js +79 -79
- package/CHANGELOG.md +128 -128
- package/LICENSE +201 -201
- package/README.md +218 -218
- package/bin/ares-config.js +199 -199
- package/bin/ares-device-info.js +30 -30
- package/bin/ares-device.js +219 -219
- package/bin/ares-generate.js +270 -270
- package/bin/ares-inspect.js +179 -179
- package/bin/ares-install.js +223 -223
- package/bin/ares-launch.js +318 -318
- package/bin/ares-log.js +255 -255
- package/bin/ares-novacom.js +223 -223
- package/bin/ares-package.js +336 -336
- package/bin/ares-pull.js +156 -156
- package/bin/ares-push.js +155 -155
- package/bin/ares-server.js +174 -174
- package/bin/ares-setup-device.js +520 -520
- package/bin/ares-shell.js +132 -132
- package/bin/ares.js +166 -166
- package/files/conf/ares.json +49 -49
- package/files/conf/command-service.json +73 -73
- package/files/conf/config.json +22 -22
- package/files/conf/ipk.json +30 -30
- package/files/conf/novacom-devices.json +35 -35
- package/files/conf/query/query-app.json +14 -14
- package/files/conf/query/query-hosted.json +18 -18
- package/files/conf/query/query-package.json +10 -10
- package/files/conf/query/query-service.json +6 -6
- package/files/conf/sdk.json +8 -8
- package/files/conf/template.json +57 -57
- package/files/conf/webos_emul +27 -27
- package/files/conf-base/env/sdk-ose.json +8 -8
- package/files/conf-base/env/sdk-tv.json +8 -8
- package/files/conf-base/profile/config-ose.json +21 -21
- package/files/conf-base/profile/config-tv.json +22 -22
- package/files/conf-base/query/query-app.json +14 -14
- package/files/conf-base/query/query-hosted.json +18 -18
- package/files/conf-base/query/query-package.json +10 -10
- package/files/conf-base/query/query-service.json +6 -6
- package/files/conf-base/template-conf/ose-templates.json +67 -67
- package/files/conf-base/template-conf/tv-sdk-templates.json +57 -57
- package/files/help/ares-config.help +43 -43
- package/files/help/ares-device.help +94 -94
- package/files/help/ares-generate.help +65 -65
- package/files/help/ares-inspect.help +70 -70
- package/files/help/ares-install.help +90 -90
- package/files/help/ares-launch.help +100 -100
- package/files/help/ares-log-pmlogd.help +84 -84
- package/files/help/ares-log.help +101 -101
- package/files/help/ares-novacom.help +68 -68
- package/files/help/ares-package.help +101 -101
- package/files/help/ares-pull.help +38 -38
- package/files/help/ares-push.help +38 -38
- package/files/help/ares-server.help +39 -39
- package/files/help/ares-setup-device.help +75 -75
- package/files/help/ares-shell.help +42 -42
- package/files/help/ares.help +47 -47
- package/files/help/readme.help +23 -23
- package/files/schema/ApplicationDescription.schema +319 -319
- package/files/schema/NovacomDevices.schema +61 -61
- package/files/templates/ose-sdk-templates/appinfo/appinfo.json +10 -10
- package/files/templates/ose-sdk-templates/bootplate-web/index.html +88 -88
- package/files/templates/ose-sdk-templates/hosted-webapp/index.html +13 -13
- package/files/templates/ose-sdk-templates/icon/icon.png +0 -0
- package/files/templates/ose-sdk-templates/js-service/helloclient.js +31 -31
- package/files/templates/ose-sdk-templates/js-service/helloworld_webos_service.js +188 -188
- package/files/templates/ose-sdk-templates/qml-app/main.qml +68 -68
- package/files/templates/ose-sdk-templates/qmlappinfo/appinfo.json +10 -10
- package/files/templates/ose-sdk-templates/serviceinfo/package.json +11 -11
- package/files/templates/ose-sdk-templates/serviceinfo/services.json +8 -8
- package/files/templates/tv-sdk-templates/appinfo/appinfo.json +10 -10
- package/files/templates/tv-sdk-templates/bootplate-web/index.html +58 -58
- package/files/templates/tv-sdk-templates/bootplate-web/webOSTVjs-1.2.10/LICENSE-2.0.txt +202 -202
- package/files/templates/tv-sdk-templates/hosted-webapp/index.html +14 -14
- package/files/templates/tv-sdk-templates/js-service/helloworld_service.js +39 -39
- package/files/templates/tv-sdk-templates/packageinfo/packageinfo.json +3 -3
- package/files/templates/tv-sdk-templates/serviceinfo/package.json +11 -11
- package/files/templates/tv-sdk-templates/serviceinfo/services.json +8 -8
- package/files/templates/tv-sdk-templates/webicon/icon.png +0 -0
- package/files/templates/tv-sdk-templates/webicon/largeIcon.png +0 -0
- package/lib/base/ares.html +40 -40
- package/lib/base/cli-appdata.js +290 -290
- package/lib/base/cli-control.js +44 -44
- package/lib/base/common-tools.js +29 -29
- package/lib/base/error-handler.js +265 -265
- package/lib/base/file-watcher.js +155 -155
- package/lib/base/help-format.js +147 -147
- package/lib/base/luna.js +178 -178
- package/lib/base/novacom.js +1191 -1191
- package/lib/base/sdkenv.js +59 -59
- package/lib/base/server.js +137 -137
- package/lib/base/setup-device.js +328 -328
- package/lib/base/version-tools.js +79 -79
- package/lib/device.js +1419 -1419
- package/lib/generator.js +377 -377
- package/lib/inspect.js +494 -494
- package/lib/install.js +463 -463
- package/lib/launch.js +605 -605
- package/lib/log.js +584 -584
- package/lib/package.js +2129 -2129
- package/lib/pull.js +231 -231
- package/lib/pusher.js +210 -210
- package/lib/session.js +74 -74
- package/lib/shell.js +193 -193
- package/lib/tar-filter-pack.js +62 -62
- package/lib/util/copy.js +31 -31
- package/lib/util/createFileName.js +40 -40
- package/lib/util/eof.js +30 -30
- package/lib/util/json.js +63 -63
- package/lib/util/merge.js +14 -14
- package/lib/util/objclone.js +40 -40
- package/lib/util/spinner.js +37 -37
- package/npm-shrinkwrap.json +9115 -9115
- package/package.json +100 -100
- package/scripts/postinstall.js +24 -24
- package/spec/helpers/reporter.js +65 -65
- package/spec/jsSpecs/apiTest/generator.spec.js +372 -372
- package/spec/jsSpecs/apiTest/inspector.spec.js +89 -89
- package/spec/jsSpecs/apiTest/installer.spec.js +67 -67
- package/spec/jsSpecs/apiTest/launcher.spec.js +150 -150
- package/spec/jsSpecs/apiTest/packager.spec.js +194 -194
- package/spec/jsSpecs/apiTest/puller.spec.js +101 -101
- package/spec/jsSpecs/apiTest/pusher.spec.js +103 -103
- package/spec/jsSpecs/apiTest/server.spec.js +115 -115
- package/spec/jsSpecs/apiTest/setupDevice.spec.js +93 -93
- package/spec/jsSpecs/apiTest/shell.spec.js +49 -49
- package/spec/jsSpecs/ares-config.spec.js +78 -78
- package/spec/jsSpecs/ares-device.spec.js +443 -443
- package/spec/jsSpecs/ares-generate.spec.js +397 -397
- package/spec/jsSpecs/ares-inspect.spec.js +252 -252
- package/spec/jsSpecs/ares-install.spec.js +150 -150
- package/spec/jsSpecs/ares-launch.spec.js +301 -301
- package/spec/jsSpecs/ares-log.spec.js +824 -824
- package/spec/jsSpecs/ares-novacom.spec.js +149 -149
- package/spec/jsSpecs/ares-package.spec.js +1211 -1211
- package/spec/jsSpecs/ares-pull.spec.js +157 -157
- package/spec/jsSpecs/ares-push.spec.js +146 -146
- package/spec/jsSpecs/ares-server.spec.js +160 -160
- package/spec/jsSpecs/ares-setup-device.spec.js +281 -281
- package/spec/jsSpecs/ares-shell.spec.js +220 -220
- package/spec/jsSpecs/ares.spec.js +83 -83
- package/spec/jsSpecs/common-spec.js +169 -169
- package/spec/support/jasmine.json +22 -22
- package/spec/tempFiles/nativeApp/auto/pkg_arm64/GLES2 +0 -0
- package/spec/tempFiles/nativeApp/auto/pkg_arm64/appinfo.json +9 -9
- package/spec/tempFiles/nativeApp/ose/pkg_arm/Hello +0 -0
- package/spec/tempFiles/nativeApp/ose/pkg_arm/appinfo.json +8 -8
- package/spec/tempFiles/nativeApp/ose/pkg_arm/package.properties +2 -2
- package/spec/tempFiles/nativeApp/oseEmul/pkg_x86/Hello +0 -0
- package/spec/tempFiles/nativeApp/oseEmul/pkg_x86/appinfo.json +9 -9
- package/spec/tempFiles/nativeApp/rsi/pkg_x86/GLES2 +0 -0
- package/spec/tempFiles/nativeApp/rsi/pkg_x86/appinfo.json +9 -9
- package/spec/tempFiles/sign/sign.crt +32 -32
- package/spec/tempFiles/sign/signPriv.key +52 -52
- package/spec/test_data/ares-generate.json +41 -41
- package/spec/test_data/ares.json +33 -33
package/lib/package.js
CHANGED
|
@@ -1,2129 +1,2129 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright (c) 2020-2024 LG Electronics Inc.
|
|
3
|
-
*
|
|
4
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const ar = require('ar-async'),
|
|
8
|
-
async = require('async'),
|
|
9
|
-
chardet = require('chardet'),
|
|
10
|
-
CombinedStream = require('combined-stream'),
|
|
11
|
-
crypto = require('crypto'),
|
|
12
|
-
decompress = require('decompress'),
|
|
13
|
-
decompressTargz = require('decompress-targz'),
|
|
14
|
-
ElfParser = require('elfy').Parser,
|
|
15
|
-
encoding = require('encoding'),
|
|
16
|
-
fs = require('fs'),
|
|
17
|
-
fstream = require('fstream'),
|
|
18
|
-
Validator = require('jsonschema').Validator,
|
|
19
|
-
mkdirp = require('mkdirp'),
|
|
20
|
-
log = require('npmlog'),
|
|
21
|
-
path = require('path'),
|
|
22
|
-
rimraf = require('rimraf'),
|
|
23
|
-
shelljs = require('shelljs'),
|
|
24
|
-
stripbom = require('strip-bom'),
|
|
25
|
-
temp = require('temp'),
|
|
26
|
-
uglify = require('terser'),
|
|
27
|
-
util = require('util'),
|
|
28
|
-
zlib = require('zlib'),
|
|
29
|
-
tarFilterPack = require('./tar-filter-pack'),
|
|
30
|
-
errHndl = require('./base/error-handler');
|
|
31
|
-
|
|
32
|
-
(function() {
|
|
33
|
-
log.heading = 'packager';
|
|
34
|
-
log.level = 'warn';
|
|
35
|
-
|
|
36
|
-
const servicePkgMethod = 'id',
|
|
37
|
-
defaultAssetsFields = {
|
|
38
|
-
"main": true,
|
|
39
|
-
"icon": true,
|
|
40
|
-
"largeIcon": true,
|
|
41
|
-
"bgImage": true,
|
|
42
|
-
"splashBackground": true,
|
|
43
|
-
"imageForRecents": true,
|
|
44
|
-
"sysAssetsBasePath": true
|
|
45
|
-
},
|
|
46
|
-
FILE_TYPE = {
|
|
47
|
-
file: 'file',
|
|
48
|
-
dir: 'dir',
|
|
49
|
-
symlink: 'symlink'
|
|
50
|
-
},
|
|
51
|
-
packager = {};
|
|
52
|
-
let objectCounter = 0;
|
|
53
|
-
|
|
54
|
-
if (typeof module !== 'undefined' && module.exports) {
|
|
55
|
-
module.exports = packager;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function Packager() {
|
|
59
|
-
this.objectId = objectCounter++;
|
|
60
|
-
this.verbose = false;
|
|
61
|
-
this.silent = true;
|
|
62
|
-
this.noclean = false;
|
|
63
|
-
this.nativecmd = false;
|
|
64
|
-
this.minify = true;
|
|
65
|
-
this.excludeFiles = [];
|
|
66
|
-
this.rom = false;
|
|
67
|
-
this.encrypt = false;
|
|
68
|
-
this.sign = "";
|
|
69
|
-
this.certificate = "";
|
|
70
|
-
this.appCount = 0;
|
|
71
|
-
this.services = [];
|
|
72
|
-
this.pkgServiceNames = [];
|
|
73
|
-
this.rscCount = 0;
|
|
74
|
-
this.resources = [];
|
|
75
|
-
this.pkgResourceNames = [];
|
|
76
|
-
this.pkgId;
|
|
77
|
-
this.pkginfofile;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
packager.Packager = Packager;
|
|
81
|
-
|
|
82
|
-
Packager.prototype = {
|
|
83
|
-
prepareOptions: function(options, next) {
|
|
84
|
-
if (options && options.level) {
|
|
85
|
-
log.level = options.level;
|
|
86
|
-
if (['warn', 'error'].indexOf(options.level) !== -1) {
|
|
87
|
-
this.silent = false;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (options && options.noclean === true) {
|
|
92
|
-
this.noclean = true;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (options && options.nativecmd === true) {
|
|
96
|
-
this.nativecmd = true;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (options && Object.prototype.hasOwnProperty.call(options, 'minify')) {
|
|
100
|
-
this.minify = options.minify;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (options && Object.prototype.hasOwnProperty.call(options, 'excludefiles')) {
|
|
104
|
-
if (options.excludefiles instanceof Array) {
|
|
105
|
-
this.excludeFiles = options.excludefiles;
|
|
106
|
-
} else {
|
|
107
|
-
this.excludeFiles.push(options.excludefiles);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
this.excludeFiles.forEach(function(excl_file) {
|
|
111
|
-
if (excl_file === "appinfo.json") {
|
|
112
|
-
return next(errHndl.getErrMsg("NOT_EXCLUDE_APPINFO"));
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
if (options && Object.prototype.hasOwnProperty.call(options, 'rom')) {
|
|
118
|
-
this.rom = options.rom;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
if (options && Object.prototype.hasOwnProperty.call(options, 'encrypt')) {
|
|
122
|
-
this.encrypt = options.encrypt;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (options && Object.prototype.hasOwnProperty.call(options, 'sign')) {
|
|
126
|
-
if (!fs.existsSync(path.resolve(options.sign))) {
|
|
127
|
-
return next(errHndl.getErrMsg("NOT_EXIST_PATH", options.sign));
|
|
128
|
-
}
|
|
129
|
-
this.sign = options.sign;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if (options && Object.prototype.hasOwnProperty.call(options, 'certificate')) {
|
|
133
|
-
if (!fs.existsSync(path.resolve(options.certificate))) {
|
|
134
|
-
return next(errHndl.getErrMsg("NOT_EXIST_PATH", options.certificate));
|
|
135
|
-
}
|
|
136
|
-
this.certificate = options.certificate;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// check sign option must be used with certificate option
|
|
140
|
-
if ((this.sign && !this.certificate) ||
|
|
141
|
-
(this.certificate && !this.sign)) {
|
|
142
|
-
return next(errHndl.getErrMsg("USE_WITH_OPTIONS", "sign, certificate"));
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
log.verbose("package#Packager()", "Packager id:" + this.objectId);
|
|
146
|
-
|
|
147
|
-
this.pkgVersion = options.pkgversion;
|
|
148
|
-
|
|
149
|
-
if (options && Object.prototype.hasOwnProperty.call(options, 'pkgid')) {
|
|
150
|
-
this.pkgId = options.pkgid;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
if (options && Object.prototype.hasOwnProperty.call(options, 'pkginfofile')) {
|
|
154
|
-
this.pkginfofile = options.pkginfofile;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
if (this.pkgId && this.pkginfofile) {
|
|
158
|
-
return next(errHndl.getErrMsg("NOT_USE_WITH_OPTIONS", "pkginfofile, pkgid"));
|
|
159
|
-
}
|
|
160
|
-
this.appCount = 0;
|
|
161
|
-
},
|
|
162
|
-
checkInputDirectories: function(inDirs, options, next) {
|
|
163
|
-
log.verbose("package#Packager#checkInputDirectories()", "input directory:", inDirs);
|
|
164
|
-
this.prepareOptions(options, next);
|
|
165
|
-
|
|
166
|
-
async.forEachSeries(inDirs, checkDirectory.bind(this, options), function(err) {
|
|
167
|
-
if (err) {
|
|
168
|
-
setImmediate(next, err);
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
setImmediate(next);
|
|
172
|
-
});
|
|
173
|
-
return {app: this.appCount, resource: this.rscCount};
|
|
174
|
-
},
|
|
175
|
-
servicePackaging: function(inDirs, destination, options, middleCb, next) {
|
|
176
|
-
log.info("package#Packager#servicePackaging()");
|
|
177
|
-
|
|
178
|
-
if (!Object.hasOwnProperty.call(options, 'pkgid') && !Object.hasOwnProperty.call(options, 'pkginfofile')) {
|
|
179
|
-
return next(errHndl.getErrMsg("USE_PKGID_PKGINFO"));
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
async.series([
|
|
183
|
-
this.checkInputDirectories.bind(this, inDirs, options),
|
|
184
|
-
setUmask.bind(this, 0),
|
|
185
|
-
loadPkgInfo.bind(this),
|
|
186
|
-
createTmpDir.bind(this),
|
|
187
|
-
excludeIpkFileFromApp.bind(this),
|
|
188
|
-
createPackageDir.bind(this),
|
|
189
|
-
fillPackageDir.bind(this),
|
|
190
|
-
findServiceDir.bind(this, this.services),
|
|
191
|
-
loadServiceInfo.bind(this),
|
|
192
|
-
checkServiceInfo.bind(this),
|
|
193
|
-
createServiceDir.bind(this),
|
|
194
|
-
copyService.bind(this),
|
|
195
|
-
addServiceInPkgInfo.bind(this),
|
|
196
|
-
copyData.bind(this, inDirs, options.force),
|
|
197
|
-
loadPackageProperties.bind(this, inDirs),
|
|
198
|
-
excludeFromApp.bind(this, middleCb),
|
|
199
|
-
outputPackage.bind(this, destination),
|
|
200
|
-
encryptPackage.bind(this),
|
|
201
|
-
copyOutputToDst.bind(this, destination, middleCb),
|
|
202
|
-
recoverUmask.bind(this),
|
|
203
|
-
cleanupTmpDir.bind(this, middleCb)
|
|
204
|
-
], function(err) {
|
|
205
|
-
if (err) {
|
|
206
|
-
// TODO: call cleanupTmpDir() before returning
|
|
207
|
-
setImmediate(next, err);
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
// TODO: probably some more checkings are needed
|
|
211
|
-
setImmediate(next, null, {ipk: this.ipk, msg: "Success"});
|
|
212
|
-
}.bind(this));
|
|
213
|
-
},
|
|
214
|
-
resourcePackaging: function(inDirs, destination, options, middleCb, next) {
|
|
215
|
-
log.info("package#Packager#resourcePackaging()");
|
|
216
|
-
this.dataCopyCount = 0;
|
|
217
|
-
async.series([
|
|
218
|
-
this.checkInputDirectories.bind(this, inDirs, options),
|
|
219
|
-
setUmask.bind(this, 0),
|
|
220
|
-
loadPkgInfo.bind(this),
|
|
221
|
-
createTmpDir.bind(this),
|
|
222
|
-
excludeIpkFileFromApp.bind(this),
|
|
223
|
-
createPackageDir.bind(this),
|
|
224
|
-
fillPackageDir.bind(this),
|
|
225
|
-
loadResourceInfo.bind(this),
|
|
226
|
-
checkResourceInfo.bind(this),
|
|
227
|
-
createResourceDir.bind(this),
|
|
228
|
-
copyResource.bind(this),
|
|
229
|
-
addResourceInPkgInfo.bind(this),
|
|
230
|
-
copyData.bind(this, inDirs, options.force),
|
|
231
|
-
loadPackageProperties.bind(this, inDirs),
|
|
232
|
-
excludeFromApp.bind(this, middleCb),
|
|
233
|
-
outputPackage.bind(this, destination),
|
|
234
|
-
encryptPackage.bind(this),
|
|
235
|
-
copyOutputToDst.bind(this, destination, middleCb),
|
|
236
|
-
recoverUmask.bind(this),
|
|
237
|
-
cleanupTmpDir.bind(this, middleCb)
|
|
238
|
-
], function(err) {
|
|
239
|
-
if (err) {
|
|
240
|
-
setImmediate(next, err);
|
|
241
|
-
return;
|
|
242
|
-
}
|
|
243
|
-
setImmediate(next, null, {ipk: this.ipk, msg: "Success"});
|
|
244
|
-
}.bind(this));
|
|
245
|
-
},
|
|
246
|
-
generatePackage: function(inDirs, destination, options, middleCb, next) {
|
|
247
|
-
log.info("package#Packager#generatePackage()", "from ", inDirs);
|
|
248
|
-
// check whether app or service directories are copied or not
|
|
249
|
-
this.dataCopyCount = 0;
|
|
250
|
-
this.minifyDone = !this.minify;
|
|
251
|
-
|
|
252
|
-
async.series([
|
|
253
|
-
this.checkInputDirectories.bind(this, inDirs, options),
|
|
254
|
-
setUmask.bind(this, 0),
|
|
255
|
-
loadPkgInfo.bind(this),
|
|
256
|
-
loadAppInfo.bind(this),
|
|
257
|
-
checkAppInfo.bind(this),
|
|
258
|
-
createTmpDir.bind(this),
|
|
259
|
-
createAppDir.bind(this),
|
|
260
|
-
checkELFHeader.bind(this),
|
|
261
|
-
fillAssetsField.bind(this),
|
|
262
|
-
copyAssets.bind(this),
|
|
263
|
-
copyApp.bind(this),
|
|
264
|
-
excludeIpkFileFromApp.bind(this),
|
|
265
|
-
createPackageDir.bind(this),
|
|
266
|
-
fillPackageDir.bind(this),
|
|
267
|
-
findServiceDir.bind(this, this.services),
|
|
268
|
-
loadServiceInfo.bind(this),
|
|
269
|
-
checkServiceInfo.bind(this),
|
|
270
|
-
createServiceDir.bind(this),
|
|
271
|
-
copyService.bind(this),
|
|
272
|
-
addServiceInPkgInfo.bind(this),
|
|
273
|
-
removeServiceFromAppDir.bind(this, middleCb),
|
|
274
|
-
copyData.bind(this, inDirs, options.force),
|
|
275
|
-
loadPackageProperties.bind(this, inDirs),
|
|
276
|
-
excludeFromApp.bind(this, middleCb),
|
|
277
|
-
outputPackage.bind(this, destination),
|
|
278
|
-
encryptPackage.bind(this),
|
|
279
|
-
copyOutputToDst.bind(this, destination, middleCb),
|
|
280
|
-
recoverUmask.bind(this),
|
|
281
|
-
cleanupTmpDir.bind(this, middleCb)
|
|
282
|
-
], function(err) {
|
|
283
|
-
if (err) {
|
|
284
|
-
// TODO: call cleanupTmpDir() before returning
|
|
285
|
-
setImmediate(next, err);
|
|
286
|
-
return;
|
|
287
|
-
}
|
|
288
|
-
// TODO: probably some more checkings are needed
|
|
289
|
-
setImmediate(next, null, {ipk: this.ipk, msg: "Success"});
|
|
290
|
-
}.bind(this));
|
|
291
|
-
},
|
|
292
|
-
analyzeIPK: function(options, next) {
|
|
293
|
-
log.info("package#Packager#analyzeIPK()");
|
|
294
|
-
let ipkConfigFile, ipkFile, ipkConfig, tmpDirPath;
|
|
295
|
-
this.prepareOptions(options);
|
|
296
|
-
|
|
297
|
-
async.series([
|
|
298
|
-
_setConfig,
|
|
299
|
-
_unpackIpk,
|
|
300
|
-
_unpackTar,
|
|
301
|
-
_analyzeMetaFile,
|
|
302
|
-
_removeTmpDir,
|
|
303
|
-
], function(err, results) {
|
|
304
|
-
log.silly("package#analyzeIPK()", "err:", err, ", results:", results);
|
|
305
|
-
if (err) {
|
|
306
|
-
return next(err);
|
|
307
|
-
}
|
|
308
|
-
return next(null, {msg: results[3].trim()});
|
|
309
|
-
});
|
|
310
|
-
|
|
311
|
-
function _setConfig(next) {
|
|
312
|
-
log.info("package#analyzeIPK()#_setConfig()");
|
|
313
|
-
if (options.info === 'true') {
|
|
314
|
-
return next(errHndl.getErrMsg("EMPTY_VALUE", 'info'));
|
|
315
|
-
} else if (options.infodetail === 'true') {
|
|
316
|
-
return next(errHndl.getErrMsg("EMPTY_VALUE", 'info-detail'));
|
|
317
|
-
}
|
|
318
|
-
ipkFile = options.info ? options.info : options.infodetail;
|
|
319
|
-
ipkConfigFile = path.resolve(__dirname, "../files/conf/ipk.json");
|
|
320
|
-
|
|
321
|
-
if (path.extname(ipkFile) !== ".ipk") {
|
|
322
|
-
return next(errHndl.getErrMsg("SUPPORT_ONLY_IPK", ipkFile));
|
|
323
|
-
}
|
|
324
|
-
if (!fs.existsSync(ipkFile)) {
|
|
325
|
-
return next(errHndl.getErrMsg("NOT_EXIST_PATH", ipkFile));
|
|
326
|
-
}
|
|
327
|
-
if (!fs.existsSync(ipkConfigFile)) {
|
|
328
|
-
return next(errHndl.getErrMsg("NOT_EXIST_PATH", ipkConfigFile));
|
|
329
|
-
}
|
|
330
|
-
ipkConfig = JSON.parse(fs.readFileSync(ipkConfigFile));
|
|
331
|
-
tmpDirPath = temp.path(ipkConfig.tmpPath);
|
|
332
|
-
|
|
333
|
-
if (!fs.existsSync(tmpDirPath)) {
|
|
334
|
-
fs.mkdirSync(tmpDirPath);
|
|
335
|
-
}
|
|
336
|
-
next();
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
function _unpackIpk(next) {
|
|
340
|
-
log.info("package#analyzeIPK()#_unpackIpk()");
|
|
341
|
-
const reader = new ar.ArReader(ipkFile);
|
|
342
|
-
|
|
343
|
-
reader.on("entry", function(entry, next) {
|
|
344
|
-
const name = entry.fileName();
|
|
345
|
-
entry.fileData()
|
|
346
|
-
.pipe(fs.createWriteStream(path.resolve(tmpDirPath, name)))
|
|
347
|
-
.on("finish", next);
|
|
348
|
-
});
|
|
349
|
-
reader.on("error", function(err) {
|
|
350
|
-
return next(err);
|
|
351
|
-
});
|
|
352
|
-
reader.on("close", function() {
|
|
353
|
-
log.verbose("package#analyzeIPK()#_unpackIpk()", "unpack ipk close");
|
|
354
|
-
next();
|
|
355
|
-
});
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
function _unpackTar(next) {
|
|
359
|
-
log.info("package#analyzeIPK()#_unpackTar()");
|
|
360
|
-
if (fs.existsSync(path.resolve(tmpDirPath, "control.tar.gz"))) {
|
|
361
|
-
(async function() {
|
|
362
|
-
await decompress(path.resolve(tmpDirPath, "control.tar.gz"), tmpDirPath, {
|
|
363
|
-
plugins: [
|
|
364
|
-
decompressTargz()
|
|
365
|
-
]
|
|
366
|
-
});
|
|
367
|
-
log.verbose("package#analyzeIPK()#_unpackTar()", "control.tar.gz decompressed");
|
|
368
|
-
})();
|
|
369
|
-
} else if (fs.existsSync(path.resolve(tmpDirPath, "control.tar.xz"))) {
|
|
370
|
-
return next(errHndl.getErrMsg("NOT_SUPPORT_XZ"));
|
|
371
|
-
} else {
|
|
372
|
-
return next(errHndl.getErrMsg("NO_COMPONENT_FILE", ", control tar file"));
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
if (fs.existsSync(path.resolve(tmpDirPath, "data.tar.gz"))) {
|
|
376
|
-
(async function() {
|
|
377
|
-
await decompress(path.resolve(tmpDirPath, "data.tar.gz"), tmpDirPath, {
|
|
378
|
-
plugins: [
|
|
379
|
-
decompressTargz()
|
|
380
|
-
]
|
|
381
|
-
});
|
|
382
|
-
log.verbose("package#analyzeIPK()#_unpackTar()", "data.tar.gz decompressed");
|
|
383
|
-
next();
|
|
384
|
-
})();
|
|
385
|
-
} else if (fs.existsSync(path.resolve(tmpDirPath, "data.tar.xz"))) {
|
|
386
|
-
return next(errHndl.getErrMsg("NOT_SUPPORT_XZ"));
|
|
387
|
-
} else {
|
|
388
|
-
return next(errHndl.getErrMsg("NO_COMPONENT_FILE", "data tar file"));
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
function _analyzeMetaFile(next) {
|
|
393
|
-
log.info("package#analyzeIPK()#_analyzeMetaFile()");
|
|
394
|
-
let result = "", targetFile, tmpTxt;
|
|
395
|
-
|
|
396
|
-
ipkConfig.webOSMetaFiles.forEach(function(item) {
|
|
397
|
-
const targetPath = path.resolve(tmpDirPath, ipkConfig[item].path);
|
|
398
|
-
// Analyze control file
|
|
399
|
-
if (item === "control") {
|
|
400
|
-
targetFile = path.resolve(targetPath, ipkConfig[item].fileName);
|
|
401
|
-
if (!fs.existsSync(targetFile)) {
|
|
402
|
-
return next(errHndl.getErrMsg("NO_COMPONENT_FILE", "control file"));
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
if (options.infodetail) {
|
|
406
|
-
result = "\n\n< " + ipkConfig[item].fileName + " >\n";
|
|
407
|
-
result += fs.readFileSync(targetFile).toString().trim();
|
|
408
|
-
} else {
|
|
409
|
-
result = ipkConfig[item].heading + "\n";
|
|
410
|
-
tmpTxt = fs.readFileSync(targetFile).toString().trim();
|
|
411
|
-
ipkConfig[item].info.forEach(function(field) {
|
|
412
|
-
if (tmpTxt.match(new RegExp(field + ": (.*)", "gi"))) {
|
|
413
|
-
result += tmpTxt.match(new RegExp(field + ": (.*)", "gi"))[0] + "\n";
|
|
414
|
-
}
|
|
415
|
-
});
|
|
416
|
-
}
|
|
417
|
-
// Analyze appinfo.json, packageinfo.json, services.json, package.json files
|
|
418
|
-
} else if (fs.existsSync(targetPath)) {
|
|
419
|
-
const dirArr = fs.readdirSync(targetPath);
|
|
420
|
-
let beforeDir = dirArr[0];
|
|
421
|
-
|
|
422
|
-
dirArr.forEach(function(dir) {
|
|
423
|
-
if (ipkConfig[item].fileName) {
|
|
424
|
-
ipkConfig[item].fileName.forEach(function(file) {
|
|
425
|
-
targetFile = path.resolve(targetPath, dir, file);
|
|
426
|
-
if (fs.existsSync(targetFile)) {
|
|
427
|
-
if (options.infodetail) {
|
|
428
|
-
result += "\n\n< " + file + " >\n";
|
|
429
|
-
result += fs.readFileSync(targetFile).toString().trim();
|
|
430
|
-
} else {
|
|
431
|
-
if (ipkConfig[item].heading && beforeDir !== path.join(ipkConfig[item].path, dir)) {
|
|
432
|
-
result += "\n" + ipkConfig[item].heading + "\n";
|
|
433
|
-
}
|
|
434
|
-
tmpTxt = fs.readFileSync(targetFile).toString().trim();
|
|
435
|
-
const tmpJson = JSON.parse(tmpTxt);
|
|
436
|
-
ipkConfig[item].info.forEach(function(field) {
|
|
437
|
-
if (tmpJson[field]) {
|
|
438
|
-
const fieldValue = tmpJson[field];
|
|
439
|
-
if (typeof fieldValue === "object") {
|
|
440
|
-
if (ipkConfig[item][field]) { // Case of services.json, services_name field
|
|
441
|
-
const subField = ipkConfig[item][field],
|
|
442
|
-
subResult = [];
|
|
443
|
-
fieldValue.forEach(function(subItem) {
|
|
444
|
-
subResult.push(subItem[subField]);
|
|
445
|
-
});
|
|
446
|
-
result += field + ": " + JSON.stringify(subResult) + "\n";
|
|
447
|
-
} else { // Case of packageinfo.json, services field
|
|
448
|
-
result += field + ": " + JSON.stringify(fieldValue) + "\n";
|
|
449
|
-
}
|
|
450
|
-
} else {
|
|
451
|
-
result += field + ": " + fieldValue + "\n";
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
});
|
|
455
|
-
beforeDir = path.join(ipkConfig[item].path, dir);
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
});
|
|
459
|
-
}
|
|
460
|
-
});
|
|
461
|
-
}
|
|
462
|
-
});
|
|
463
|
-
next(null, result);
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
function _removeTmpDir(next) {
|
|
467
|
-
log.info("package#analyzeIPK()#_removeTmpDir()");
|
|
468
|
-
rimraf(tmpDirPath, function(err) {
|
|
469
|
-
log.verbose("package#analyzeIPK()#_removeTmpDir()", "removed " + tmpDirPath);
|
|
470
|
-
next(err);
|
|
471
|
-
});
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
};
|
|
475
|
-
|
|
476
|
-
function Service() {
|
|
477
|
-
this.srcDir = "";
|
|
478
|
-
this.dstDirs = [];
|
|
479
|
-
this.valid = false;
|
|
480
|
-
this.serviceInfo = "";
|
|
481
|
-
this.dirName = "";
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
// Private functions
|
|
485
|
-
function loadPkgInfo(next) {
|
|
486
|
-
log.verbose("loadPkgInfo");
|
|
487
|
-
let data;
|
|
488
|
-
|
|
489
|
-
if (this.pkginfofile) {
|
|
490
|
-
log.silly("Use pkginfofile option");
|
|
491
|
-
if (fs.existsSync(this.pkginfofile)) {
|
|
492
|
-
if ("packageinfo.json" !== path.basename(this.pkginfofile)) {
|
|
493
|
-
return setImmediate(next, errHndl.getErrMsg("INVALID_FILE", "packageinfo.json"));
|
|
494
|
-
}
|
|
495
|
-
data = rewriteFileWoBOMAsUtf8(this.pkginfofile, true);
|
|
496
|
-
try {
|
|
497
|
-
log.verbose("PKGINFO >>" + data + "<<");
|
|
498
|
-
this.pkginfo = JSON.parse(data);
|
|
499
|
-
|
|
500
|
-
if (!Object.prototype.hasOwnProperty.call(this.pkginfo, 'id')) {
|
|
501
|
-
return setImmediate(next, errHndl.getErrMsg("REQUIRED_FIELD", "id"));
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
this.pkgId = this.pkginfo.id;
|
|
505
|
-
this.pkgVersion = this.pkgVersion || this.pkginfo.version;
|
|
506
|
-
setImmediate(next);
|
|
507
|
-
}
|
|
508
|
-
catch (err) {
|
|
509
|
-
return setImmediate(next, errHndl.getErrMsg("INVALID_JSON_FORMAT", "packageinfo.json"));
|
|
510
|
-
}
|
|
511
|
-
} else {
|
|
512
|
-
return setImmediate(next, errHndl.getErrMsg("NOT_EXIST_PATH", this.pkginfofile));
|
|
513
|
-
}
|
|
514
|
-
} else if (this.pkgDir) {
|
|
515
|
-
log.silly("Use packageinfo.json from PKG_DIR");
|
|
516
|
-
data = rewriteFileWoBOMAsUtf8(path.join(this.pkgDir, "packageinfo.json"));
|
|
517
|
-
try {
|
|
518
|
-
log.verbose("PKGINFO >>" + data + "<<");
|
|
519
|
-
this.pkginfo = JSON.parse(data);
|
|
520
|
-
|
|
521
|
-
if (!Object.prototype.hasOwnProperty.call(this.pkginfo, 'id')) {
|
|
522
|
-
return setImmediate(next, errHndl.getErrMsg("REQUIRED_FIELD", "id"));
|
|
523
|
-
}
|
|
524
|
-
this.pkgId = this.pkginfo.id;
|
|
525
|
-
this.pkgVersion = this.pkgVersion || this.pkginfo.version;
|
|
526
|
-
setImmediate(next);
|
|
527
|
-
}
|
|
528
|
-
catch (err) {
|
|
529
|
-
return setImmediate(next, errHndl.getErrMsg("INVALID_JSON_FORMAT", "packageinfo.json"));
|
|
530
|
-
}
|
|
531
|
-
} else {
|
|
532
|
-
return setImmediate(next);
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
function loadAppInfo(next) {
|
|
537
|
-
log.verbose("loadAppInfo");
|
|
538
|
-
if (this.appCount === 0) {
|
|
539
|
-
return setImmediate(next);
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
const filepath = path.join(this.appDir, "appinfo.json"),
|
|
543
|
-
data = rewriteFileWoBOMAsUtf8(filepath, true);
|
|
544
|
-
try {
|
|
545
|
-
this.appinfo = JSON.parse(data);
|
|
546
|
-
log.silly("package#loadAppInfo()", "content of appinfo.json:", this.appinfo);
|
|
547
|
-
|
|
548
|
-
if (!this.appinfo.version || this.appinfo.version === undefined) {
|
|
549
|
-
this.appinfo.version = "1.0.0";
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
this.pkgVersion = this.pkgVersion || this.appinfo.version;
|
|
553
|
-
setImmediate(next);
|
|
554
|
-
} catch(err) {
|
|
555
|
-
setImmediate(next, err);
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
function checkAppInfo(next) {
|
|
560
|
-
if (this.appCount === 0) {
|
|
561
|
-
return setImmediate(next);
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
// check enyo app
|
|
565
|
-
if (this.pkgJSExist && this.appinfo.main && this.appinfo.main.match(/(\.html|\.htm)$/gi)) {
|
|
566
|
-
const mainFile = path.join(this.appDir, this.appinfo.main);
|
|
567
|
-
if (!fs.existsSync(mainFile)) {
|
|
568
|
-
return setImmediate(next, errHndl.getErrMsg("NOT_EXIST_PATH", this.appinfo.main));
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
const regex = new RegExp("(<script[^>]*src[ \t]*=[ \t]*['\"])[^'\"]*/enyo.js(['\"])"),
|
|
572
|
-
data = fs.readFileSync(mainFile);
|
|
573
|
-
if (data.toString().match(regex)) {
|
|
574
|
-
// If enyo app, stop packaging.
|
|
575
|
-
return setImmediate(next, errHndl.getErrMsg("NOT_SUPPORT_ENYO"));
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
if (!this.appinfo.id || this.appinfo.id === undefined) {
|
|
580
|
-
return setImmediate(next, errHndl.getErrMsg("REQUIRED_FIELD", "id"));
|
|
581
|
-
}
|
|
582
|
-
if (this.appinfo.id.length < 1 || !(/^[a-z0-9.+-]*$/.test(this.appinfo.id))) {
|
|
583
|
-
log.error(errHndl.getErrMsg("INVALID_VALUE", "id", this.appinfo.id));
|
|
584
|
-
return setImmediate(next, errHndl.getErrMsg("INVALID_ID_RULE"));
|
|
585
|
-
}
|
|
586
|
-
if (this.pkgId && this.appinfo.id.indexOf(this.pkgId) !== 0) {
|
|
587
|
-
log.error(errHndl.getErrMsg("INVALID_VALUE", "id", this.appinfo.id));
|
|
588
|
-
return setImmediate(next, errHndl.getErrMsg("INVALID_APPID", this.pkgId));
|
|
589
|
-
}
|
|
590
|
-
if (!this.appinfo.version || this.appinfo.version === undefined) {
|
|
591
|
-
return setImmediate(next, errHndl.getErrMsg("REQUIRED_FIELD", "version"));
|
|
592
|
-
}
|
|
593
|
-
if (this.appinfo.version.length < 1 || !(/^([1-9]\d{0,8}|\d)\.([1-9]\d{0,8}|\d)\.([1-9]\d{0,8}|\d)$/.test(this.appinfo.version))) {
|
|
594
|
-
log.error(errHndl.getErrMsg("INVALID_VALUE", "version", this.appinfo.version));
|
|
595
|
-
return setImmediate(next, errHndl.getErrMsg("INVALID_VERSION_RULE"));
|
|
596
|
-
}
|
|
597
|
-
if (this.appinfo.type && this.appinfo.type.match(/clock/gi)) {
|
|
598
|
-
return setImmediate(next);
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
const schemaFile = path.resolve(__dirname, "../files/schema/ApplicationDescription.schema");
|
|
602
|
-
async.waterfall([
|
|
603
|
-
fs.readFile.bind(this, schemaFile, "utf-8"),
|
|
604
|
-
function getSchema(data, next) {
|
|
605
|
-
try {
|
|
606
|
-
const schema = JSON.parse(data);
|
|
607
|
-
/* "required" keyword is redefined in draft 4.
|
|
608
|
-
But current jsonschema lib support only draft 3.
|
|
609
|
-
So this line changes "required" attribute according to the draft 3.
|
|
610
|
-
*/
|
|
611
|
-
const reqKeys = schema.required;
|
|
612
|
-
if (reqKeys) {
|
|
613
|
-
for (const key in schema.properties) {
|
|
614
|
-
if (reqKeys.indexOf(key) !== -1) {
|
|
615
|
-
schema.properties[key].required = true;
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
next(null, schema);
|
|
620
|
-
} catch(err) {
|
|
621
|
-
next(errHndl.getErrMsg("INVALID_JSON_FORMAT", "AppDescription schema"));
|
|
622
|
-
}
|
|
623
|
-
},
|
|
624
|
-
function checkValid(schema, next) {
|
|
625
|
-
try {
|
|
626
|
-
next(null, new Validator().validate(this.appinfo, schema));
|
|
627
|
-
} catch (err) {
|
|
628
|
-
log.error(err);
|
|
629
|
-
next(errHndl.getErrMsg("INVALID_JSON_FORMAT"));
|
|
630
|
-
}
|
|
631
|
-
}.bind(this)
|
|
632
|
-
], function(err, result) {
|
|
633
|
-
if (err) {
|
|
634
|
-
setImmediate(next, err);
|
|
635
|
-
} else {
|
|
636
|
-
if (result && result.errors.length > 0) {
|
|
637
|
-
const errFile = "appinfo.json";
|
|
638
|
-
let errMsg = "";
|
|
639
|
-
for (const idx in result.errors) {
|
|
640
|
-
let errMsgLine = result.errors[idx].property + " " + result.errors[idx].message;
|
|
641
|
-
if (errMsgLine.indexOf("instance.") > -1) {
|
|
642
|
-
errMsgLine = errMsgLine.substring("instance.".length);
|
|
643
|
-
errMsg = errMsg.concat("\n");
|
|
644
|
-
errMsg = errMsg.concat(errMsgLine);
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
errMsg = errHndl.getErrMsg("INVALID_FILE", errFile, errMsg);
|
|
648
|
-
return setImmediate(next, errMsg);
|
|
649
|
-
} else {
|
|
650
|
-
log.verbose("package#checkAppInfo()", "APPINFO is valid");
|
|
651
|
-
}
|
|
652
|
-
setImmediate(next);
|
|
653
|
-
}
|
|
654
|
-
});
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
function fillAssetsField(next) {
|
|
658
|
-
if (this.appCount === 0) {
|
|
659
|
-
return setImmediate(next);
|
|
660
|
-
}
|
|
661
|
-
// make appinfo.assets to have default values so that they can be copied into the package
|
|
662
|
-
this.appinfo.assets = this.appinfo.assets || [];
|
|
663
|
-
for (const i in this.appinfo) {
|
|
664
|
-
if (Object.prototype.hasOwnProperty.call(this.appinfo, i) && defaultAssetsFields[i]) {
|
|
665
|
-
// no duplicated adding & value should not null string & file/dir should exist
|
|
666
|
-
if ((this.appinfo.assets.indexOf(this.appinfo[i]) === -1) && this.appinfo[i]) {
|
|
667
|
-
this.appinfo.assets.push(this.appinfo[i]);
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
// refer to appinfo.json files in localization directory.
|
|
673
|
-
const appInfoPath = this.originAppDir,
|
|
674
|
-
checkDir = path.join(this.originAppDir, "resources"),
|
|
675
|
-
foundFilePath = [],
|
|
676
|
-
resourcesAssets = [];
|
|
677
|
-
|
|
678
|
-
try {
|
|
679
|
-
const stat = fs.lstatSync(checkDir);
|
|
680
|
-
if (!stat.isDirectory()) {
|
|
681
|
-
return setImmediate(next, null);
|
|
682
|
-
}
|
|
683
|
-
} catch(err) {
|
|
684
|
-
if (err.code === "ENOENT") {
|
|
685
|
-
return setImmediate(next, null);
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
async.series([
|
|
690
|
-
walkFolder.bind(null, checkDir, "appinfo.json", foundFilePath, 1),
|
|
691
|
-
function(next) {
|
|
692
|
-
async.forEach(foundFilePath, function(filePath, next) {
|
|
693
|
-
rewriteFileWoBOMAsUtf8(filePath, true, function(err, data) {
|
|
694
|
-
try {
|
|
695
|
-
const appInfo = JSON.parse(data),
|
|
696
|
-
dirPath = path.dirname(filePath);
|
|
697
|
-
for (const i in appInfo) {
|
|
698
|
-
if (Object.prototype.hasOwnProperty.call(appInfo, i) && defaultAssetsFields[i]) {
|
|
699
|
-
if (appInfo[i]) {
|
|
700
|
-
const itemPath = path.join(dirPath, appInfo[i]),
|
|
701
|
-
relPath = path.relative(appInfoPath, itemPath);
|
|
702
|
-
// no duplicated adding & value should not null string & file/dir should exist
|
|
703
|
-
if ((resourcesAssets.indexOf(relPath) === -1)) {
|
|
704
|
-
resourcesAssets.push(relPath);
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
setImmediate(next, null);
|
|
710
|
-
} catch(error) {
|
|
711
|
-
setImmediate(next, errHndl.getErrMsg("INVALID_JSON_FORMAT", filePath));
|
|
712
|
-
}
|
|
713
|
-
});
|
|
714
|
-
}, function(err) {
|
|
715
|
-
setImmediate(next, err);
|
|
716
|
-
});
|
|
717
|
-
},
|
|
718
|
-
function(next) {
|
|
719
|
-
this.appinfo.assets = this.appinfo.assets.concat(resourcesAssets);
|
|
720
|
-
setImmediate(next, null);
|
|
721
|
-
}.bind(this)
|
|
722
|
-
], function(err) {
|
|
723
|
-
setImmediate(next, err);
|
|
724
|
-
});
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
function createTmpDir(next) {
|
|
728
|
-
this.tempDir = temp.path({prefix: 'com.palm.ares.hermes.bdOpenwebOS'}) + '.d';
|
|
729
|
-
log.verbose("package#createTmpDir()", "temp dir:", this.tempDir);
|
|
730
|
-
mkdirp(this.tempDir, next);
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
function createAppDir(next) {
|
|
734
|
-
if (this.appCount === 0) {
|
|
735
|
-
return setImmediate(next);
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
try {
|
|
739
|
-
this.applicationDir = path.resolve(this.tempDir, "data/usr/palm/applications", this.appinfo.id);
|
|
740
|
-
log.info("package#createAppDir()", "application dir:" + this.applicationDir);
|
|
741
|
-
mkdirp(this.applicationDir, next);
|
|
742
|
-
} catch (err) {
|
|
743
|
-
return setImmediate(next, err);
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
function copySrcToDst(src, dst, next) {
|
|
748
|
-
const fileList = [],
|
|
749
|
-
self = this,
|
|
750
|
-
requireMinify = !!((!!self.minify && !self.minifyDone));
|
|
751
|
-
src = path.normalize(path.resolve(src));
|
|
752
|
-
dst = path.normalize(path.resolve(dst));
|
|
753
|
-
|
|
754
|
-
async.series([
|
|
755
|
-
function(next) {
|
|
756
|
-
const stat = fs.statSync(src);
|
|
757
|
-
if (stat.isFile()) {
|
|
758
|
-
_pushList(fileList, 'file', path.dirname(src), path.basename(src), true, null);
|
|
759
|
-
setImmediate(next);
|
|
760
|
-
} else {
|
|
761
|
-
_getFileList(src, src, fileList, next);
|
|
762
|
-
}
|
|
763
|
-
},
|
|
764
|
-
_copySrcToDst.bind(null, fileList, dst, requireMinify)
|
|
765
|
-
], function(err) {
|
|
766
|
-
next(err);
|
|
767
|
-
});
|
|
768
|
-
|
|
769
|
-
function _pushList(list, type, basePath, relPath, isSubPath, indRelPath) {
|
|
770
|
-
if (!FILE_TYPE[type]) {
|
|
771
|
-
return;
|
|
772
|
-
}
|
|
773
|
-
list.push({
|
|
774
|
-
type: type,
|
|
775
|
-
basePath: basePath,
|
|
776
|
-
relPath: relPath,
|
|
777
|
-
isSubPath: isSubPath,
|
|
778
|
-
indRelPath: indRelPath
|
|
779
|
-
});
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
function _getFileList(dirPath, basePath, files, next) {
|
|
783
|
-
// TODO: the following code should be more concise.
|
|
784
|
-
// Handling symbolic links
|
|
785
|
-
// if the path sym-link indicates is a sub-path of source directory, treat a sym-link as it is.
|
|
786
|
-
// otherwise the files sym-link indicates should be copied
|
|
787
|
-
async.waterfall([
|
|
788
|
-
fs.readdir.bind(null, dirPath),
|
|
789
|
-
function(fileNames, next) {
|
|
790
|
-
if (fileNames.length === 0) {
|
|
791
|
-
_pushList(files, 'dir', basePath, path.relative(basePath, dirPath), true, null);
|
|
792
|
-
return setImmediate(next);
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
async.forEachSeries(fileNames, function(fileName, next) {
|
|
796
|
-
const filePath = path.join(dirPath, fileName),
|
|
797
|
-
relPath = path.relative(basePath, filePath);
|
|
798
|
-
|
|
799
|
-
async.waterfall([
|
|
800
|
-
fs.lstat.bind(null, filePath),
|
|
801
|
-
function(lstat, next) {
|
|
802
|
-
if (lstat.isSymbolicLink()) {
|
|
803
|
-
let indicateFullPath;
|
|
804
|
-
try {
|
|
805
|
-
indicateFullPath = fs.realpathSync(filePath);
|
|
806
|
-
} catch (err) {
|
|
807
|
-
if (err.code === 'ENOENT') {
|
|
808
|
-
log.warn("The file for symbolic link (" + filePath + ") is missing...");
|
|
809
|
-
return setImmediate(next);
|
|
810
|
-
}
|
|
811
|
-
return setImmediate(next, err);
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
const indicateRelPath = fs.readlinkSync(filePath);
|
|
815
|
-
if (indicateFullPath.indexOf(basePath) !== -1) {
|
|
816
|
-
_pushList(files, 'symlink', basePath, relPath, true, indicateRelPath);
|
|
817
|
-
} else {
|
|
818
|
-
const stat = fs.statSync(filePath);
|
|
819
|
-
if (stat.isDirectory()) {
|
|
820
|
-
return _getFileList(filePath, basePath, files, next);
|
|
821
|
-
} else if (stat.isFile()) {
|
|
822
|
-
_pushList(files, 'file', basePath, relPath, true, null);
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
|
-
setImmediate(next);
|
|
826
|
-
} else if (lstat.isDirectory()) {
|
|
827
|
-
return _getFileList(filePath, basePath, files, next);
|
|
828
|
-
} else if (lstat.isFile()) {
|
|
829
|
-
_pushList(files, 'file', basePath, relPath, true, null);
|
|
830
|
-
setImmediate(next);
|
|
831
|
-
} else {
|
|
832
|
-
setImmediate(next);
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
], next); // async.waterfall
|
|
836
|
-
}, next); // async.forEach
|
|
837
|
-
}
|
|
838
|
-
], function(err) {
|
|
839
|
-
return setImmediate(next, err);
|
|
840
|
-
}); // async.waterfall
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
function _copySrcToDst(files, dstPath, minify, next) {
|
|
844
|
-
try {
|
|
845
|
-
async.forEachSeries(files, function(file, next) {
|
|
846
|
-
if (!FILE_TYPE[file.type]) {
|
|
847
|
-
log.verbose("package#copySrcToDst()#_copySrcToDst()", "ignore 'unknown file type'("+file.type+")");
|
|
848
|
-
return;
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
if (!file.relPath) {
|
|
852
|
-
log.verbose("package#copySrcToDst()#_copySrcToDst()", "ignore 'unknown path'");
|
|
853
|
-
return setImmediate(next);
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
if (file.type === FILE_TYPE.dir) {
|
|
857
|
-
mkdirp.sync(path.join(dstPath, file.relPath));
|
|
858
|
-
return setImmediate(next);
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
const dstDirPath = path.dirname(path.join(dstPath, file.relPath));
|
|
862
|
-
if (!fs.existsSync(dstDirPath)) {
|
|
863
|
-
mkdirp.sync(dstDirPath);
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
if (file.type === FILE_TYPE.symlink) {
|
|
867
|
-
if (file.isSubPath && file.indRelPath) {
|
|
868
|
-
const linkFile = path.join(dstPath, file.relPath);
|
|
869
|
-
if (fs.existsSync(linkFile)) {
|
|
870
|
-
if (fs.lstatSync(linkFile).isSymbolicLink()) {
|
|
871
|
-
fs.unlinkSync(linkFile);
|
|
872
|
-
}
|
|
873
|
-
}
|
|
874
|
-
fs.symlinkSync(file.indRelPath, linkFile, null);
|
|
875
|
-
}
|
|
876
|
-
} else {
|
|
877
|
-
const sourceFile = path.join(file.basePath, file.relPath);
|
|
878
|
-
if (fs.existsSync(sourceFile)) {
|
|
879
|
-
if (minify && '.js' === path.extname(sourceFile) && file.relPath.indexOf('node_modules') === -1) {
|
|
880
|
-
log.verbose("package#copySrcToDst()#_copySrcToDst()", "require minification # sourceFile:", sourceFile);
|
|
881
|
-
try {
|
|
882
|
-
const data = uglify.minify(fs.readFileSync(sourceFile,'utf8'));
|
|
883
|
-
if (data.error) {
|
|
884
|
-
throw data.error;
|
|
885
|
-
}
|
|
886
|
-
fs.writeFileSync(path.join(dstPath, file.relPath), data.code, 'utf8');
|
|
887
|
-
} catch (e) {
|
|
888
|
-
log.verbose("package#copySrcToDst()#_copySrcToDst()", util.format('Failed to uglify code %s: %s', sourceFile, e.stack));
|
|
889
|
-
return setImmediate(next, errHndl.getErrMsg("FAILED_MINIFY", sourceFile));
|
|
890
|
-
}
|
|
891
|
-
} else {
|
|
892
|
-
shelljs.cp('-Rf', sourceFile, path.join(dstPath, file.relPath, '..'));
|
|
893
|
-
}
|
|
894
|
-
} else {
|
|
895
|
-
log.verbose("package#copySrcToDst()#_copySrcToDst()", "ignore '" + file.relPath + "'");
|
|
896
|
-
}
|
|
897
|
-
}
|
|
898
|
-
setImmediate(next);
|
|
899
|
-
}, function(err) {
|
|
900
|
-
if (!err && minify) {
|
|
901
|
-
self.minifyDone = true;
|
|
902
|
-
}
|
|
903
|
-
setImmediate(next, err);
|
|
904
|
-
});
|
|
905
|
-
} catch(err) {
|
|
906
|
-
setImmediate(next, err);
|
|
907
|
-
}
|
|
908
|
-
}
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
function checkELFHeader(next) {
|
|
912
|
-
const self = this,
|
|
913
|
-
ELF_HEADER_LEN = 64,
|
|
914
|
-
buf = Buffer.alloc(ELF_HEADER_LEN),
|
|
915
|
-
mainFile = path.resolve(path.join(this.appDir, this.appinfo.main));
|
|
916
|
-
|
|
917
|
-
if (!fs.existsSync(mainFile)) {
|
|
918
|
-
return setImmediate(next, errHndl.getErrMsg("NOT_EXIST_PATH", mainFile));
|
|
919
|
-
}
|
|
920
|
-
|
|
921
|
-
const fd = fs.openSync(mainFile, 'r'),
|
|
922
|
-
stats = fs.fstatSync(fd),
|
|
923
|
-
elfParser = new ElfParser(),
|
|
924
|
-
_isELF = function(_buf) {
|
|
925
|
-
if (_buf.slice(0, 4).toString() !== '\x7fELF') {
|
|
926
|
-
return false;
|
|
927
|
-
} else {
|
|
928
|
-
return true;
|
|
929
|
-
}
|
|
930
|
-
};
|
|
931
|
-
|
|
932
|
-
if (stats.size < ELF_HEADER_LEN) {
|
|
933
|
-
log.verbose("package#checkELFHeader()", "file size is smaller than ELF Header size");
|
|
934
|
-
return setImmediate(next);
|
|
935
|
-
}
|
|
936
|
-
|
|
937
|
-
fs.read(fd, buf, 0, ELF_HEADER_LEN, 0, function(err, bytesRead, _buf) {
|
|
938
|
-
if (bytesRead < ELF_HEADER_LEN || err) {
|
|
939
|
-
log.silly("package#checkELFHeader()", "err:", err, ", bytesRead:", bytesRead);
|
|
940
|
-
log.silly("package#checkELFHeader()", "readBuf to parse ELF header is small or error occurred during reading file.");
|
|
941
|
-
return setImmediate(next);
|
|
942
|
-
}
|
|
943
|
-
|
|
944
|
-
if (!_isELF(_buf)) {
|
|
945
|
-
log.silly("package#checkELFHeader()", mainFile + " is not ELF format");
|
|
946
|
-
} else {
|
|
947
|
-
log.silly("package#checkELFHeader()", mainFile + " is ELF format");
|
|
948
|
-
try {
|
|
949
|
-
const elfHeader = elfParser.parseHeader(_buf);
|
|
950
|
-
log.silly("package#checkELFHeader()", "elfHeader:", elfHeader);
|
|
951
|
-
|
|
952
|
-
if (elfHeader.machine && elfHeader.machine.match(/86$/)) {
|
|
953
|
-
// current emulator opkg is allowing only all, noarch and i586.
|
|
954
|
-
// when it is used with --offline-root.
|
|
955
|
-
self.architecture = 'i586';
|
|
956
|
-
} else if (elfHeader.machine && elfHeader.machine.match(/amd64$/)) {
|
|
957
|
-
// change amd64 to x86_64
|
|
958
|
-
self.architecture = 'x86_64';
|
|
959
|
-
} else if (elfHeader.machine && elfHeader.machine.match(/AArch64$/)) {
|
|
960
|
-
// change AArch64 to aarch64
|
|
961
|
-
self.architecture = 'aarch64';
|
|
962
|
-
} else {
|
|
963
|
-
self.architecture = elfHeader.machine;
|
|
964
|
-
}
|
|
965
|
-
} catch(e) {
|
|
966
|
-
log.verbose("package#checkELFHeader()", "exception:", e);
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
log.verbose("package#checkELFHeader()", "machine:", self.architecture);
|
|
970
|
-
fs.close(fd);
|
|
971
|
-
setImmediate(next);
|
|
972
|
-
});
|
|
973
|
-
}
|
|
974
|
-
|
|
975
|
-
function copyApp(next) {
|
|
976
|
-
if (this.appCount === 0) {
|
|
977
|
-
return setImmediate(next);
|
|
978
|
-
}
|
|
979
|
-
|
|
980
|
-
this.dataCopyCount++;
|
|
981
|
-
log.info("package#copyApp()", "copy " + this.appDir + " ==> " + this.applicationDir);
|
|
982
|
-
copySrcToDst.call(this, this.appDir, this.applicationDir, next);
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
function copyAssets(next) {
|
|
986
|
-
if (this.appCount === 0) {
|
|
987
|
-
return setImmediate(next);
|
|
988
|
-
}
|
|
989
|
-
|
|
990
|
-
try {
|
|
991
|
-
async.forEachSeries(this.appinfo.assets, _handleAssets.bind(this), next);
|
|
992
|
-
} catch (err) {
|
|
993
|
-
return setImmediate(next, err);
|
|
994
|
-
}
|
|
995
|
-
|
|
996
|
-
function _handleAssets(file, next) {
|
|
997
|
-
if (path.resolve(this.originAppDir) === path.resolve(this.appDir)) {
|
|
998
|
-
_checkAppinfo.call(this, file, next);
|
|
999
|
-
} else {
|
|
1000
|
-
async.series([
|
|
1001
|
-
_checkAppinfo.bind(this, file),
|
|
1002
|
-
_copyAssets.bind(this, file)
|
|
1003
|
-
], next);
|
|
1004
|
-
}
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
|
-
function _checkAppinfo(file, next) {
|
|
1008
|
-
let source;
|
|
1009
|
-
if (path.resolve(file) === path.normalize(file)) {
|
|
1010
|
-
return next(errHndl.getErrMsg("NOT_RELATIVE_PATH_APPINFO", file));
|
|
1011
|
-
} else {
|
|
1012
|
-
source = path.join(this.originAppDir, file);
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
if (path.resolve(source).indexOf(this.originAppDir) !== 0) {
|
|
1016
|
-
return next(errHndl.getErrMsg("NOT_RELATIVE_PATH_APPINFO", file));
|
|
1017
|
-
}
|
|
1018
|
-
|
|
1019
|
-
if (!fs.existsSync(source) && !this.rom) {
|
|
1020
|
-
const msg = errHndl.getErrMsg("NOT_EXIST_PATH", file);
|
|
1021
|
-
if (path.basename(source).indexOf('$') === 0) {
|
|
1022
|
-
// ignore property with starting $ prefix (dynamic property handling in the platform)
|
|
1023
|
-
return setImmediate(next);
|
|
1024
|
-
} else {
|
|
1025
|
-
return setImmediate(next, msg);
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
1028
|
-
setImmediate(next);
|
|
1029
|
-
}
|
|
1030
|
-
|
|
1031
|
-
function _copyAssets(file, next) {
|
|
1032
|
-
log.verbose("package#copyAssets()#_copyAssets", "'" + file + "' will be located in app directory");
|
|
1033
|
-
const source = path.join(this.originAppDir, file),
|
|
1034
|
-
destination = this.appDir;
|
|
1035
|
-
|
|
1036
|
-
async.series([
|
|
1037
|
-
function(next) {
|
|
1038
|
-
if (!fs.existsSync(destination)) {
|
|
1039
|
-
mkdirp(destination, next);
|
|
1040
|
-
} else {
|
|
1041
|
-
setImmediate(next);
|
|
1042
|
-
}
|
|
1043
|
-
}
|
|
1044
|
-
], function(err) {
|
|
1045
|
-
if (err) {
|
|
1046
|
-
return setImmediate(next, err);
|
|
1047
|
-
}
|
|
1048
|
-
shelljs.cp('-Rf', source, destination);
|
|
1049
|
-
setImmediate(next);
|
|
1050
|
-
});
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
|
|
1054
|
-
function excludeIpkFileFromApp(next) {
|
|
1055
|
-
// Exclude a pre-built .ipk file
|
|
1056
|
-
this.excludeFiles = this.excludeFiles.concat([
|
|
1057
|
-
// eslint-disable-next-line no-useless-escape
|
|
1058
|
-
"[.]*[\\.]ipk",
|
|
1059
|
-
".DS_Store",
|
|
1060
|
-
// ".vscode",
|
|
1061
|
-
// ".reloadignore",
|
|
1062
|
-
// ".launchparams.json"
|
|
1063
|
-
]);
|
|
1064
|
-
setImmediate(next);
|
|
1065
|
-
}
|
|
1066
|
-
|
|
1067
|
-
function _retrieve(list, regExp, dirPath, depth, next) {
|
|
1068
|
-
async.waterfall([
|
|
1069
|
-
fs.readdir.bind(null, dirPath), function(fileNames, next) {
|
|
1070
|
-
async.forEach(fileNames, function(fileName, next) {
|
|
1071
|
-
const filePath = path.join(dirPath, fileName);
|
|
1072
|
-
async.waterfall([
|
|
1073
|
-
fs.lstat.bind(null, filePath), function(stat, next) {
|
|
1074
|
-
let result = false;
|
|
1075
|
-
if (depth > 1 && regExp.test(fileName)) {
|
|
1076
|
-
result = true;
|
|
1077
|
-
list.push(filePath);
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
|
-
if (!result && stat.isDirectory()) {
|
|
1081
|
-
_retrieve(list, regExp, filePath, depth + 1, next);
|
|
1082
|
-
} else {
|
|
1083
|
-
setImmediate(next);
|
|
1084
|
-
}
|
|
1085
|
-
}
|
|
1086
|
-
], next);
|
|
1087
|
-
}, next);
|
|
1088
|
-
}
|
|
1089
|
-
], function(err) {
|
|
1090
|
-
setImmediate(next, err);
|
|
1091
|
-
});
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
function excludeFromApp(middleCb, next) {
|
|
1095
|
-
let excludes;
|
|
1096
|
-
if (this.appCount === 0) {
|
|
1097
|
-
excludes = this.excludeFiles;
|
|
1098
|
-
} else {
|
|
1099
|
-
excludes = this.excludeFiles.concat(this.appinfo.exclude || []);
|
|
1100
|
-
}
|
|
1101
|
-
|
|
1102
|
-
const regExpQueries = excludes.map(function(exclude) {
|
|
1103
|
-
return exclude.replace(/^\./g,"^\\.").replace(/^\*/g,"").replace(/$/g,"$");
|
|
1104
|
-
}, this),
|
|
1105
|
-
strRegExp = regExpQueries.join("|"),
|
|
1106
|
-
regExp = new RegExp(strRegExp, "i"),
|
|
1107
|
-
excludeList = [],
|
|
1108
|
-
tempDirPrefix = path.resolve(this.tempDir, "data/usr/palm");
|
|
1109
|
-
|
|
1110
|
-
async.series([
|
|
1111
|
-
_retrieve.bind(this, excludeList, regExp, tempDirPrefix, 0), function(next) {
|
|
1112
|
-
const meta_files = ["appinfo.json", "services.json", "packageinfo.json", "resourceinfo.json"],
|
|
1113
|
-
unexcludable = [],
|
|
1114
|
-
excluded = [];
|
|
1115
|
-
try {
|
|
1116
|
-
excludeList.forEach(function(file) {
|
|
1117
|
-
if (meta_files.includes(path.basename(file))) {
|
|
1118
|
-
unexcludable.push(path.basename(file));
|
|
1119
|
-
} else {
|
|
1120
|
-
const sliced = file.slice(tempDirPrefix.length + 1);
|
|
1121
|
-
excluded.push(sliced.slice(sliced.indexOf(path.sep) + 1));
|
|
1122
|
-
shelljs.rm('-rf', file);
|
|
1123
|
-
}
|
|
1124
|
-
});
|
|
1125
|
-
if (unexcludable.length > 0) {
|
|
1126
|
-
middleCb("Cannot exclude: " + unexcludable); // FIXME
|
|
1127
|
-
}
|
|
1128
|
-
if (excluded.length > 0) {
|
|
1129
|
-
middleCb("Excluded: " + excluded); // FIXME
|
|
1130
|
-
}
|
|
1131
|
-
setImmediate(next);
|
|
1132
|
-
} catch(err) {
|
|
1133
|
-
setImmediate(next, err);
|
|
1134
|
-
}
|
|
1135
|
-
}
|
|
1136
|
-
], function(err) {
|
|
1137
|
-
if (err) {
|
|
1138
|
-
return setImmediate(next, err);
|
|
1139
|
-
}
|
|
1140
|
-
setImmediate(next);
|
|
1141
|
-
});
|
|
1142
|
-
}
|
|
1143
|
-
|
|
1144
|
-
function createPackageDir(next) {
|
|
1145
|
-
if (!this.rom) {
|
|
1146
|
-
const pkgDirName = this.pkgId || this.appinfo.id;
|
|
1147
|
-
this.packageDir = path.join(this.tempDir, "data/usr/palm/packages", pkgDirName);
|
|
1148
|
-
log.verbose("package#createPackageDir", "directory:", this.packageDir);
|
|
1149
|
-
mkdirp(this.packageDir, next);
|
|
1150
|
-
} else {
|
|
1151
|
-
setImmediate(next);
|
|
1152
|
-
}
|
|
1153
|
-
}
|
|
1154
|
-
|
|
1155
|
-
function fillPackageDir(next) {
|
|
1156
|
-
log.verbose("fillPackageDir");
|
|
1157
|
-
|
|
1158
|
-
if (!this.rom) {
|
|
1159
|
-
let data = "";
|
|
1160
|
-
if (this.pkginfo) {
|
|
1161
|
-
// Use pkginfofile option or PKG_DIR
|
|
1162
|
-
if (!this.pkginfo.version) {
|
|
1163
|
-
this.pkginfo.version = this.pkgVersion || "1.0.0";
|
|
1164
|
-
}
|
|
1165
|
-
if (this.appinfo) {
|
|
1166
|
-
this.pkginfo.app = this.appinfo.id;
|
|
1167
|
-
}
|
|
1168
|
-
_checkPkgInfo(this.pkginfo.id, this.pkginfo.version, next);
|
|
1169
|
-
data = JSON.stringify(this.pkginfo, null, 2) + "\n";
|
|
1170
|
-
} else {
|
|
1171
|
-
// Use pkgid option or no option
|
|
1172
|
-
const pkginfo = {
|
|
1173
|
-
"id": this.pkgId || this.appinfo.id,
|
|
1174
|
-
"version": this.pkgVersion || "1.0.0"
|
|
1175
|
-
};
|
|
1176
|
-
if (this.appinfo) {
|
|
1177
|
-
pkginfo.app = this.appinfo.id;
|
|
1178
|
-
}
|
|
1179
|
-
_checkPkgInfo(pkginfo.id, pkginfo.version, next);
|
|
1180
|
-
data = JSON.stringify(pkginfo, null, 2) + "\n";
|
|
1181
|
-
}
|
|
1182
|
-
log.verbose("Generating packageinfo.json: " + data);
|
|
1183
|
-
fs.writeFile(path.join(this.packageDir, "packageinfo.json"), data, next);
|
|
1184
|
-
} else {
|
|
1185
|
-
setImmediate(next);
|
|
1186
|
-
}
|
|
1187
|
-
|
|
1188
|
-
function _checkPkgInfo(id, version, next) {
|
|
1189
|
-
if (!(/^[a-z0-9.+-]*$/.test(id))) {
|
|
1190
|
-
log.error(errHndl.getErrMsg("INVALID_VALUE", "pkg id", id));
|
|
1191
|
-
return setImmediate(next, errHndl.getErrMsg("INVALID_ID_RULE"));
|
|
1192
|
-
}
|
|
1193
|
-
|
|
1194
|
-
if (!(/^([1-9]\d{0,8}|\d)\.([1-9]\d{0,8}|\d)\.([1-9]\d{0,8}|\d)$/.test(version))) {
|
|
1195
|
-
log.error(errHndl.getErrMsg("INVALID_VALUE", "pkg version", version));
|
|
1196
|
-
return setImmediate(next, errHndl.getErrMsg("INVALID_VERSION_RULE"));
|
|
1197
|
-
}
|
|
1198
|
-
}
|
|
1199
|
-
}
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
function loadPackageProperties(inDirs, next) {
|
|
1203
|
-
const self = this;
|
|
1204
|
-
self.packageProperties = {};
|
|
1205
|
-
|
|
1206
|
-
async.forEach(inDirs, function(inDir, next) {
|
|
1207
|
-
const filename = path.join(inDir, "package.properties");
|
|
1208
|
-
|
|
1209
|
-
function _checkFileMode(file) {
|
|
1210
|
-
file = file.replace(/\\/g, "/").trim();
|
|
1211
|
-
const idx = file.lastIndexOf("/");
|
|
1212
|
-
file = (idx !== -1) ? file.slice(idx + 1) : file;
|
|
1213
|
-
self.packageProperties[file] = this.fileMode;
|
|
1214
|
-
}
|
|
1215
|
-
|
|
1216
|
-
if (fs.existsSync(filename)) {
|
|
1217
|
-
const data = fs.readFileSync(filename);
|
|
1218
|
-
try {
|
|
1219
|
-
const lines = data.toString().split("\n");
|
|
1220
|
-
let seperatorIndex;
|
|
1221
|
-
|
|
1222
|
-
for (const i in lines) {
|
|
1223
|
-
if (lines[i].indexOf("filemode.") === 0) {
|
|
1224
|
-
seperatorIndex = lines[i].indexOf("=");
|
|
1225
|
-
const fileList = lines[i].slice(seperatorIndex + 1).trim(),
|
|
1226
|
-
fileArray = fileList.split(",");
|
|
1227
|
-
this.fileMode = lines[i].slice(9, seperatorIndex).trim();
|
|
1228
|
-
|
|
1229
|
-
fileArray.forEach(_checkFileMode);
|
|
1230
|
-
}
|
|
1231
|
-
}
|
|
1232
|
-
setImmediate(next);
|
|
1233
|
-
} catch (error) {
|
|
1234
|
-
setImmediate(next, error);
|
|
1235
|
-
}
|
|
1236
|
-
} else {
|
|
1237
|
-
setImmediate(next);
|
|
1238
|
-
}
|
|
1239
|
-
}, function(err) {
|
|
1240
|
-
// Exclude package.propeties from ipk file
|
|
1241
|
-
self.excludeFiles = self.excludeFiles.concat([
|
|
1242
|
-
"package.properties"
|
|
1243
|
-
]);
|
|
1244
|
-
setImmediate(next, err);
|
|
1245
|
-
});
|
|
1246
|
-
}
|
|
1247
|
-
|
|
1248
|
-
function outputPackage(destination, next) {
|
|
1249
|
-
log.info("package#outputPackage()");
|
|
1250
|
-
|
|
1251
|
-
// Check that the directories exist
|
|
1252
|
-
if (fs.existsSync(destination)) {
|
|
1253
|
-
const stats = fs.statSync(destination);
|
|
1254
|
-
if (!stats.isDirectory()) {
|
|
1255
|
-
return next(errHndl.getErrMsg("NOT_DIRTYPE_PATH", destination));
|
|
1256
|
-
}
|
|
1257
|
-
} else {
|
|
1258
|
-
log.verbose("setOutputDir()", "creating directory '" + destination + "' ...");
|
|
1259
|
-
mkdirp.sync(destination);
|
|
1260
|
-
}
|
|
1261
|
-
destination = fs.realpathSync(destination);
|
|
1262
|
-
|
|
1263
|
-
if (this.rom) {
|
|
1264
|
-
copySrcToDst.call(this, path.join(this.tempDir, 'data'), destination, next);
|
|
1265
|
-
} else {
|
|
1266
|
-
const tempDir = this.tempDir,
|
|
1267
|
-
tempCtrlDir = path.join(tempDir, 'ctrl'),
|
|
1268
|
-
ctrlTgzFile = path.join(tempDir, 'control.tar.gz'),
|
|
1269
|
-
tempDataDir = path.join(tempDir, 'data'),
|
|
1270
|
-
dataTgzFile = path.join(tempDir, 'data.tar.gz');
|
|
1271
|
-
|
|
1272
|
-
async.series([
|
|
1273
|
-
decidePkgName.bind(this, this.pkgId, this.pkgVersion),
|
|
1274
|
-
getFileSize.bind(this, tempDataDir),
|
|
1275
|
-
makeTgz.bind(this, tempDataDir, dataTgzFile),
|
|
1276
|
-
createDir.bind(this, tempCtrlDir),
|
|
1277
|
-
createControlFile.bind(this, tempCtrlDir, false),
|
|
1278
|
-
createSign.bind(this, tempCtrlDir, dataTgzFile),
|
|
1279
|
-
makeTgz.bind(this, tempCtrlDir, ctrlTgzFile),
|
|
1280
|
-
createDebianBinary.bind(this, tempDir),
|
|
1281
|
-
setIpkFileName.bind(this),
|
|
1282
|
-
removeExistingIpk.bind(this, destination),
|
|
1283
|
-
makeIpk.bind(this, tempDir)
|
|
1284
|
-
], function(err) {
|
|
1285
|
-
if (err) {
|
|
1286
|
-
setImmediate(next, err);
|
|
1287
|
-
return;
|
|
1288
|
-
}
|
|
1289
|
-
setImmediate(next);
|
|
1290
|
-
});
|
|
1291
|
-
}
|
|
1292
|
-
}
|
|
1293
|
-
|
|
1294
|
-
function decidePkgName(pkgName, pkgVersion, next) {
|
|
1295
|
-
if (this.appCount !== 0) {
|
|
1296
|
-
this.pkg = {
|
|
1297
|
-
name: pkgName || this.appinfo.id,
|
|
1298
|
-
version: pkgVersion || this.appinfo.version
|
|
1299
|
-
};
|
|
1300
|
-
} else if (this.services.length > 0) {
|
|
1301
|
-
this.pkg = {
|
|
1302
|
-
name: pkgName || this.services[0].serviceInfo.id || this.services[0].serviceInfo.services[0].name,
|
|
1303
|
-
version: pkgVersion || "1.0.0"
|
|
1304
|
-
};
|
|
1305
|
-
} else {
|
|
1306
|
-
this.pkg = {
|
|
1307
|
-
name: pkgName || "unknown",
|
|
1308
|
-
version: pkgVersion || "1.0.0"
|
|
1309
|
-
};
|
|
1310
|
-
}
|
|
1311
|
-
setImmediate(next);
|
|
1312
|
-
}
|
|
1313
|
-
|
|
1314
|
-
function getFileSize(srcDir, next) {
|
|
1315
|
-
const self = this;
|
|
1316
|
-
|
|
1317
|
-
async.waterfall([
|
|
1318
|
-
_readSizeRecursive.bind(this, srcDir)
|
|
1319
|
-
], function(err, size) {
|
|
1320
|
-
if (!err && size) {
|
|
1321
|
-
log.verbose("package#getFileSize()", "Installed-Size:", size);
|
|
1322
|
-
self.size = size;
|
|
1323
|
-
}
|
|
1324
|
-
setImmediate(next, err);
|
|
1325
|
-
});
|
|
1326
|
-
|
|
1327
|
-
function _readSizeRecursive(item, next) {
|
|
1328
|
-
fs.lstat(item, function(err, stats) {
|
|
1329
|
-
let total = stats.size;
|
|
1330
|
-
|
|
1331
|
-
if (!err && stats.isDirectory()) {
|
|
1332
|
-
fs.readdir(item, function(error, list) {
|
|
1333
|
-
if (error) {
|
|
1334
|
-
return next(error);
|
|
1335
|
-
}
|
|
1336
|
-
|
|
1337
|
-
async.forEach(list, function(diritem, callback) {
|
|
1338
|
-
_readSizeRecursive(path.join(item, diritem), function(_err, size) {
|
|
1339
|
-
total += size;
|
|
1340
|
-
callback(_err);
|
|
1341
|
-
});
|
|
1342
|
-
}, function(e) {
|
|
1343
|
-
next(e, total);
|
|
1344
|
-
}
|
|
1345
|
-
);
|
|
1346
|
-
});
|
|
1347
|
-
} else {
|
|
1348
|
-
next(err, total);
|
|
1349
|
-
}
|
|
1350
|
-
});
|
|
1351
|
-
}
|
|
1352
|
-
}
|
|
1353
|
-
|
|
1354
|
-
function createDir(dstPath, next) {
|
|
1355
|
-
log.verbose("package#createDir()", "createDir:" + dstPath);
|
|
1356
|
-
mkdirp(dstPath, next);
|
|
1357
|
-
}
|
|
1358
|
-
|
|
1359
|
-
function createControlFile(dstDir, encInfo, next) {
|
|
1360
|
-
const dstFilePath = path.join(dstDir, 'control');
|
|
1361
|
-
log.verbose("package#createControlFile()", "createControlFile:" + dstFilePath);
|
|
1362
|
-
|
|
1363
|
-
const lines = [
|
|
1364
|
-
"Package: " + this.pkg.name,
|
|
1365
|
-
"Version: " + this.pkg.version,
|
|
1366
|
-
"Section: misc",
|
|
1367
|
-
"Priority: optional",
|
|
1368
|
-
"Architecture: " + (this.architecture || "all"),
|
|
1369
|
-
"Installed-Size: " + (this.size || 1234), // TODO: TBC
|
|
1370
|
-
"Maintainer: N/A <nobody@example.com>", // TODO: TBC
|
|
1371
|
-
"Description: This is a webOS application.",
|
|
1372
|
-
"webOS-Package-Format-Version: 2", // TODO: TBC
|
|
1373
|
-
"webOS-Packager-Version: x.y.x" // TODO: TBC
|
|
1374
|
-
];
|
|
1375
|
-
|
|
1376
|
-
if (encInfo) {
|
|
1377
|
-
lines.push("Encrypt-Algorithm: AES-256-CBC");
|
|
1378
|
-
}
|
|
1379
|
-
lines.push(''); // for the trailing \n
|
|
1380
|
-
fs.writeFile(dstFilePath, lines.join("\n"), next);
|
|
1381
|
-
}
|
|
1382
|
-
|
|
1383
|
-
function createSign(dstDir, dataTgzPath, next) {
|
|
1384
|
-
if ((!this.sign) || (!this.certificate)) {
|
|
1385
|
-
log.verbose("package#createSign()", "App signing is skipped");
|
|
1386
|
-
return setImmediate(next);
|
|
1387
|
-
}
|
|
1388
|
-
|
|
1389
|
-
const sigFilePath = path.join(dstDir, 'data.tar.gz.sha256.txt'),
|
|
1390
|
-
keyPath = path.resolve(this.sign),
|
|
1391
|
-
crtPath = path.resolve(this.certificate);
|
|
1392
|
-
|
|
1393
|
-
log.verbose("package#createSign()", "dataTgzPath:" + dataTgzPath + ", sigfile:" + sigFilePath);
|
|
1394
|
-
log.verbose("package#createSign()", "keyPath:" + keyPath + ", crtPath:" + crtPath);
|
|
1395
|
-
|
|
1396
|
-
try {
|
|
1397
|
-
// Create certificate to tmp/ctrl directory
|
|
1398
|
-
shelljs.cp('-f', crtPath, dstDir);
|
|
1399
|
-
|
|
1400
|
-
// Create signature and write data.tar.gz.sha256.txt
|
|
1401
|
-
const privateKey = fs.readFileSync(keyPath, 'utf-8'),
|
|
1402
|
-
dataFile = fs.readFileSync(dataTgzPath), // data.tar.gz
|
|
1403
|
-
signer = crypto.createSign('sha256');
|
|
1404
|
-
|
|
1405
|
-
signer.update(dataFile);
|
|
1406
|
-
signer.end();
|
|
1407
|
-
|
|
1408
|
-
const signature = signer.sign(privateKey),
|
|
1409
|
-
buff = Buffer.from(signature),
|
|
1410
|
-
base64data = buff.toString('base64');
|
|
1411
|
-
|
|
1412
|
-
fs.writeFile(sigFilePath, base64data, next);
|
|
1413
|
-
} catch (err) {
|
|
1414
|
-
setImmediate(next, err);
|
|
1415
|
-
}
|
|
1416
|
-
}
|
|
1417
|
-
|
|
1418
|
-
function createDebianBinary(dstDir, next) {
|
|
1419
|
-
const dstFilePath = path.join(dstDir, "debian-binary");
|
|
1420
|
-
log.verbose("package#createDebianBinary()", dstFilePath);
|
|
1421
|
-
fs.writeFile(dstFilePath, "2.0\n", next);
|
|
1422
|
-
}
|
|
1423
|
-
|
|
1424
|
-
function makeTgz(srcDir, dstDir, next) {
|
|
1425
|
-
log.verbose("package#makeTgz()", "makeTgz " + dstDir + " from " + srcDir);
|
|
1426
|
-
const pkgServiceNames = this.pkgServiceNames;
|
|
1427
|
-
// @see https://github.com/isaacs/node-tar/issues/7
|
|
1428
|
-
// it is a workaround for packaged ipk on windows can set +x into directory
|
|
1429
|
-
const fixupDirs = function(entry) {
|
|
1430
|
-
// Make sure readable directories have execute permission
|
|
1431
|
-
if (entry.props.type === "Directory") {
|
|
1432
|
-
let maskingBits = 201; // 0311
|
|
1433
|
-
// special case for service directory should have writable permission.
|
|
1434
|
-
if (pkgServiceNames.indexOf(entry.props.basename) !== -1) {
|
|
1435
|
-
maskingBits = 219; // 0333
|
|
1436
|
-
}
|
|
1437
|
-
entry.props.mode |= (entry.props.mode >>> 2) & maskingBits;
|
|
1438
|
-
} else if (entry.props.type === "File") {
|
|
1439
|
-
// Add other user's readable permission to all files
|
|
1440
|
-
entry.props.mode |= 4; // 04
|
|
1441
|
-
}
|
|
1442
|
-
return true;
|
|
1443
|
-
};
|
|
1444
|
-
|
|
1445
|
-
// TODO: when this PR (https://github.com/npm/node-tar/pull/73) is merged, need to update node-tar
|
|
1446
|
-
fstream
|
|
1447
|
-
.Reader({path: srcDir, type: 'Directory', filter: fixupDirs })
|
|
1448
|
-
.pipe(tarFilterPack({ noProprietary: true, fromBase: true, permission: this.packageProperties }))
|
|
1449
|
-
// .pipe(tarFilterPack({ noProprietary: true, pathFilter: filter, permission: this.packageProperties }))
|
|
1450
|
-
.pipe(zlib.createGzip())
|
|
1451
|
-
.pipe(fs.createWriteStream(dstDir))
|
|
1452
|
-
.on("close", next)
|
|
1453
|
-
.on('error', next);
|
|
1454
|
-
}
|
|
1455
|
-
|
|
1456
|
-
function setIpkFileName(next) {
|
|
1457
|
-
let filename = this.pkg.name;
|
|
1458
|
-
if (this.pkg.version) {
|
|
1459
|
-
// This is asked to replace 'x86' from 'i586' as a file suffix (From NDK)
|
|
1460
|
-
const archSuffix = ('i586' === this.architecture) ? 'x86' : (this.architecture || 'all');
|
|
1461
|
-
filename = filename.concat("_" + this.pkg.version + "_" + archSuffix + ".ipk");
|
|
1462
|
-
} else {
|
|
1463
|
-
filename = filename.concat(".ipk");
|
|
1464
|
-
}
|
|
1465
|
-
this.ipkFileName = filename;
|
|
1466
|
-
setImmediate(next);
|
|
1467
|
-
}
|
|
1468
|
-
|
|
1469
|
-
function removeExistingIpk(destination, next) {
|
|
1470
|
-
if (this.appCount === 0) {
|
|
1471
|
-
return setImmediate(next);
|
|
1472
|
-
}
|
|
1473
|
-
|
|
1474
|
-
const filename = path.join(destination, this.ipkFileName);
|
|
1475
|
-
fs.exists(filename, function(exists) {
|
|
1476
|
-
if (exists) {
|
|
1477
|
-
fs.unlink(filename, next);
|
|
1478
|
-
} else {
|
|
1479
|
-
setImmediate(next); // Nothing to do
|
|
1480
|
-
}
|
|
1481
|
-
});
|
|
1482
|
-
}
|
|
1483
|
-
|
|
1484
|
-
function padSpace(input, length) {
|
|
1485
|
-
// max field length in ar is 16
|
|
1486
|
-
const ret = String(input + ' ');
|
|
1487
|
-
return ret.slice(0, length);
|
|
1488
|
-
}
|
|
1489
|
-
|
|
1490
|
-
function arFileHeader(name, size) {
|
|
1491
|
-
const epoch = Math.floor(Date.now() / 1000) ;
|
|
1492
|
-
return padSpace(name, 16) +
|
|
1493
|
-
padSpace(epoch, 12) +
|
|
1494
|
-
"0 " + // UID, 6 bytes
|
|
1495
|
-
"0 " + // GID, 6 bytes
|
|
1496
|
-
"100644 " + // file mode, 8 bytes
|
|
1497
|
-
padSpace(size, 10) +
|
|
1498
|
-
"\x60\x0A"; // don't ask
|
|
1499
|
-
}
|
|
1500
|
-
|
|
1501
|
-
function makeIpk(srcDir, next) {
|
|
1502
|
-
this.IpkDir = srcDir;
|
|
1503
|
-
this.ipk = path.join(srcDir, this.ipkFileName);
|
|
1504
|
-
log.info("package#makeIpk()", "makeIpk in dir " + this.IpkDir + " file " + this.ipkFileName);
|
|
1505
|
-
|
|
1506
|
-
if (this.nativecmd) { // TODO: TBR
|
|
1507
|
-
shelljs.cd(this.IpkDir);
|
|
1508
|
-
shelljs.exec("ar -q " + this.ipk + " debian-binary control.tar.gz data.tar.gz", {silent: this.silent});
|
|
1509
|
-
|
|
1510
|
-
setImmediate(next);
|
|
1511
|
-
return;
|
|
1512
|
-
}
|
|
1513
|
-
|
|
1514
|
-
// global header, see http://en.wikipedia.org/wiki/Ar_%28Unix%29
|
|
1515
|
-
const header = "!<arch>\n",
|
|
1516
|
-
debBinary = arFileHeader("debian-binary",4) + "2.0\n",
|
|
1517
|
-
that = this,
|
|
1518
|
-
arStream = CombinedStream.create(),
|
|
1519
|
-
pkgFiles = [ 'control.tar.gz', 'data.tar.gz' ],
|
|
1520
|
-
ipkStream = fstream.Writer(this.ipk);
|
|
1521
|
-
|
|
1522
|
-
arStream.append(header + debBinary);
|
|
1523
|
-
pkgFiles.forEach(function(f) {
|
|
1524
|
-
const fpath = path.join(that.IpkDir, f),
|
|
1525
|
-
s = fstream.Reader({ path: fpath, type: 'File'}),
|
|
1526
|
-
stat = fs.statSync(fpath); // TODO: move to asynchronous processing
|
|
1527
|
-
|
|
1528
|
-
arStream.append(arFileHeader(f, stat.size));
|
|
1529
|
-
arStream.append(s);
|
|
1530
|
-
if ((stat.size % 2) !== 0) {
|
|
1531
|
-
log.verbose("package#makeIpk()", 'adding a filler for file ' + f);
|
|
1532
|
-
arStream.append('\n');
|
|
1533
|
-
}
|
|
1534
|
-
}, this);
|
|
1535
|
-
|
|
1536
|
-
arStream.pipe(ipkStream);
|
|
1537
|
-
ipkStream.on('close', function() {
|
|
1538
|
-
setImmediate(next);
|
|
1539
|
-
});
|
|
1540
|
-
ipkStream.on('error', next);
|
|
1541
|
-
}
|
|
1542
|
-
|
|
1543
|
-
function cleanupTmpDir(middleCb, next) {
|
|
1544
|
-
if (this.noclean) {
|
|
1545
|
-
middleCb("Skipping removal of " + this.tempDir);
|
|
1546
|
-
setImmediate(next);
|
|
1547
|
-
} else {
|
|
1548
|
-
rimraf(this.tempDir, function(err) {
|
|
1549
|
-
log.verbose("package#cleanupTmpDir()", "removed " + this.tempDir);
|
|
1550
|
-
setImmediate(next, err);
|
|
1551
|
-
}.bind(this));
|
|
1552
|
-
}
|
|
1553
|
-
}
|
|
1554
|
-
|
|
1555
|
-
function checkDirectory(options, directory, callback) {
|
|
1556
|
-
log.verbose("package#checkDirectory()", directory);
|
|
1557
|
-
if (fs.existsSync(directory)) { // TODO: move to asynchronous processing
|
|
1558
|
-
const stat = fs.statSync(directory);
|
|
1559
|
-
if (!stat.isDirectory()) {
|
|
1560
|
-
callback(errHndl.getErrMsg("NOT_DIRTYPE_PATH", directory));
|
|
1561
|
-
return;
|
|
1562
|
-
}
|
|
1563
|
-
directory = fs.realpathSync(directory);
|
|
1564
|
-
} else {
|
|
1565
|
-
callback(errHndl.getErrMsg("NOT_EXIST_PATH", directory));
|
|
1566
|
-
return;
|
|
1567
|
-
}
|
|
1568
|
-
|
|
1569
|
-
if (options.force) {
|
|
1570
|
-
return callback();
|
|
1571
|
-
}
|
|
1572
|
-
|
|
1573
|
-
if (fs.existsSync(path.join(directory, "packageinfo.json"))) {
|
|
1574
|
-
return callback(errHndl.getErrMsg("NOT_PACKAGE_WITH_PKGDIR"));
|
|
1575
|
-
}
|
|
1576
|
-
|
|
1577
|
-
if (fs.existsSync(path.join(directory, "appinfo.json"))) {// TODO: move to asynchronous processing
|
|
1578
|
-
this.appCount++;
|
|
1579
|
-
log.verbose("package#checkDirectory()", "FOUND appinfo.json, appCount " + this.appCount);
|
|
1580
|
-
|
|
1581
|
-
if (this.appCount > 1) {
|
|
1582
|
-
callback(errHndl.getErrMsg("OVER_APPCOUNT"));
|
|
1583
|
-
} else if (this.rscCount > 0) {
|
|
1584
|
-
callback(errHndl.getErrMsg("NOT_PACKAGE_WITH_RESOURCE"));
|
|
1585
|
-
} else {
|
|
1586
|
-
this.appDir = directory;
|
|
1587
|
-
this.originAppDir = directory;
|
|
1588
|
-
if (fs.existsSync(path.join(directory, "package.js"))) {
|
|
1589
|
-
this.pkgJSExist = true;
|
|
1590
|
-
}
|
|
1591
|
-
callback();
|
|
1592
|
-
}
|
|
1593
|
-
} else if (fs.existsSync(path.join(directory, "services.json"))) {
|
|
1594
|
-
if (this.rscCount > 0) {
|
|
1595
|
-
callback(errHndl.getErrMsg("NOT_PACKAGE_WITH_RESOURCE"));
|
|
1596
|
-
}
|
|
1597
|
-
this.svcDir = this.svcDir || [];
|
|
1598
|
-
this.svcDir = this.svcDir.concat(directory);
|
|
1599
|
-
callback();
|
|
1600
|
-
} else if (fs.existsSync(path.join(directory, "resourceinfo.json"))) {
|
|
1601
|
-
this.rscCount++;
|
|
1602
|
-
log.verbose("FOUND resourceinfo.json, rscCount " + this.rscCount);
|
|
1603
|
-
if (this.appCount > 0 || this.svcDir && this.svcDir.length > 0) {
|
|
1604
|
-
callback(errHndl.getErrMsg("NOT_PACKAGE_WITH_RESOURCE"));
|
|
1605
|
-
}
|
|
1606
|
-
this.resources = this.resources || [];
|
|
1607
|
-
const rsc = {};
|
|
1608
|
-
rsc.dir = directory;
|
|
1609
|
-
this.resources = this.resources.concat(rsc);
|
|
1610
|
-
callback();
|
|
1611
|
-
} else if (fs.existsSync(path.join(directory, "account-templates.json"))) {
|
|
1612
|
-
callback(errHndl.getErrMsg("NO_ACCOUNT"));
|
|
1613
|
-
} else {
|
|
1614
|
-
// find service directory recursively
|
|
1615
|
-
const foundSvcDirs = [];
|
|
1616
|
-
this.svcDir = this.svcDir || [];
|
|
1617
|
-
this.svcDir = this.svcDir.concat(directory);
|
|
1618
|
-
|
|
1619
|
-
findServiceDir.call(this, foundSvcDirs, function() {
|
|
1620
|
-
if (foundSvcDirs.length > 0) {
|
|
1621
|
-
if (this.rscCount > 0) {
|
|
1622
|
-
callback(errHndl.getErrMsg("NOT_PACKAGE_WITH_RESOURCE"));
|
|
1623
|
-
}
|
|
1624
|
-
callback();
|
|
1625
|
-
} else {
|
|
1626
|
-
callback(errHndl.getErrMsg("NO_METAFILE", "APP_DIR/SVC_DIR", directory));
|
|
1627
|
-
}
|
|
1628
|
-
});
|
|
1629
|
-
}
|
|
1630
|
-
}
|
|
1631
|
-
|
|
1632
|
-
// find service directories checking if directory has services.json file
|
|
1633
|
-
function findServiceDir(services, next) {
|
|
1634
|
-
const checkDirs = [].concat(this.svcDir || this.originAppDir || []),
|
|
1635
|
-
foundFilePath = [];
|
|
1636
|
-
if (checkDirs.length === 0) {
|
|
1637
|
-
return setImmediate(next);
|
|
1638
|
-
}
|
|
1639
|
-
|
|
1640
|
-
async.forEach(checkDirs, function(checkDir, next) {
|
|
1641
|
-
walkFolder(checkDir, "services.json", foundFilePath, 3, function(err) {
|
|
1642
|
-
if (err) {
|
|
1643
|
-
return setImmediate(next, err);
|
|
1644
|
-
}
|
|
1645
|
-
|
|
1646
|
-
foundFilePath.forEach(function(filePath) {
|
|
1647
|
-
const svc = new Service();
|
|
1648
|
-
svc.srcDir = path.dirname(filePath);
|
|
1649
|
-
svc.dirName = path.basename(svc.srcDir);
|
|
1650
|
-
services.push(svc);
|
|
1651
|
-
});
|
|
1652
|
-
foundFilePath.pop();
|
|
1653
|
-
setImmediate(next, err);
|
|
1654
|
-
});
|
|
1655
|
-
}, function(err) {
|
|
1656
|
-
setImmediate(next, err);
|
|
1657
|
-
});
|
|
1658
|
-
}
|
|
1659
|
-
|
|
1660
|
-
function walkFolder(dirPath, findFileName, foundFilePath, depth, next) {
|
|
1661
|
-
if (depth <= 0) {
|
|
1662
|
-
return next();
|
|
1663
|
-
}
|
|
1664
|
-
async.waterfall([
|
|
1665
|
-
fs.readdir.bind(null, dirPath),
|
|
1666
|
-
function(fileNames, next) {
|
|
1667
|
-
async.forEach(fileNames, function(fileName, next) {
|
|
1668
|
-
const filePath = path.join(dirPath, fileName);
|
|
1669
|
-
async.waterfall([
|
|
1670
|
-
fs.lstat.bind(null, filePath),
|
|
1671
|
-
function(stat, next) {
|
|
1672
|
-
if (stat.isFile()) {
|
|
1673
|
-
if (fileName === findFileName) {
|
|
1674
|
-
foundFilePath.push(filePath);
|
|
1675
|
-
}
|
|
1676
|
-
next();
|
|
1677
|
-
} else if (stat.isDirectory()) {
|
|
1678
|
-
walkFolder(filePath, findFileName, foundFilePath, (depth-1), next);
|
|
1679
|
-
} else {
|
|
1680
|
-
next();
|
|
1681
|
-
}
|
|
1682
|
-
}
|
|
1683
|
-
], next); // async.waterfall
|
|
1684
|
-
}, next); // async.forEach
|
|
1685
|
-
}
|
|
1686
|
-
], function(err) {
|
|
1687
|
-
next(err);
|
|
1688
|
-
}); // async.waterfall
|
|
1689
|
-
}
|
|
1690
|
-
|
|
1691
|
-
// read services.json recursivly
|
|
1692
|
-
function loadServiceInfo(next) {
|
|
1693
|
-
for (const idx in this.services) {
|
|
1694
|
-
const filename = path.join(this.services[idx].srcDir, "services.json");
|
|
1695
|
-
try {
|
|
1696
|
-
const data = fs.readFileSync(filename),
|
|
1697
|
-
info = JSON.parse(data);
|
|
1698
|
-
if (!(Object.prototype.hasOwnProperty.call(info, 'id') &&
|
|
1699
|
-
Object.prototype.hasOwnProperty.call(info, 'services'))) {
|
|
1700
|
-
continue;
|
|
1701
|
-
}
|
|
1702
|
-
this.services[idx].serviceInfo = info;
|
|
1703
|
-
this.services[idx].valid = true;
|
|
1704
|
-
} catch (err) {
|
|
1705
|
-
return setImmediate(next, err);
|
|
1706
|
-
}
|
|
1707
|
-
}
|
|
1708
|
-
log.info("package#loadServiceInfo()", "num of serviceInfo: " + this.services.length);
|
|
1709
|
-
setImmediate(next);
|
|
1710
|
-
}
|
|
1711
|
-
|
|
1712
|
-
// check services.json recursivly
|
|
1713
|
-
function checkServiceInfo(next) {
|
|
1714
|
-
const pkgId = this.pkgId || this.appinfo.id,
|
|
1715
|
-
svcIds = [];
|
|
1716
|
-
let errFlag = false;
|
|
1717
|
-
|
|
1718
|
-
this.services.forEach(function(service) {
|
|
1719
|
-
if (service.valid === false) {
|
|
1720
|
-
return;
|
|
1721
|
-
}
|
|
1722
|
-
|
|
1723
|
-
svcIds.push(getPkgServiceNames(service.serviceInfo)[0]);
|
|
1724
|
-
});
|
|
1725
|
-
|
|
1726
|
-
svcIds.forEach(function(svcId) {
|
|
1727
|
-
if (svcId.indexOf(pkgId + ".") !== 0) {
|
|
1728
|
-
errFlag = true;
|
|
1729
|
-
}
|
|
1730
|
-
});
|
|
1731
|
-
|
|
1732
|
-
if (errFlag) {
|
|
1733
|
-
return setImmediate(next, errHndl.getErrMsg("INVALID_SERVICEID", pkgId));
|
|
1734
|
-
}
|
|
1735
|
-
setImmediate(next);
|
|
1736
|
-
}
|
|
1737
|
-
|
|
1738
|
-
// create dir with each service's name under (tmp) + data/usr/palm/services/
|
|
1739
|
-
function createServiceDir(next) {
|
|
1740
|
-
this.services.forEach(function(service) {
|
|
1741
|
-
if (service.valid === false) {
|
|
1742
|
-
return;
|
|
1743
|
-
}
|
|
1744
|
-
|
|
1745
|
-
getPkgServiceNames(service.serviceInfo).forEach(function(serviceName) {
|
|
1746
|
-
const serviceDir = path.join(this.tempDir, "data/usr/palm/services", serviceName);
|
|
1747
|
-
service.dstDirs.push(serviceDir);
|
|
1748
|
-
try {
|
|
1749
|
-
log.info("package#createServiceDir()", "service dir:" + serviceDir);
|
|
1750
|
-
mkdirp.sync(serviceDir);
|
|
1751
|
-
} catch (err) {
|
|
1752
|
-
return setImmediate(next, err);
|
|
1753
|
-
}
|
|
1754
|
-
}.bind(this));
|
|
1755
|
-
}.bind(this));
|
|
1756
|
-
setImmediate(next);
|
|
1757
|
-
}
|
|
1758
|
-
|
|
1759
|
-
// copy service files into each serviceInfos[x].id directory.
|
|
1760
|
-
function copyService(next) {
|
|
1761
|
-
log.info("package#copyService()");
|
|
1762
|
-
const self = this,
|
|
1763
|
-
validServices = this.services.filter(function(service) {
|
|
1764
|
-
return service.valid;
|
|
1765
|
-
});
|
|
1766
|
-
try {
|
|
1767
|
-
async.forEachSeries(validServices, function(service, next) {
|
|
1768
|
-
async.forEach(service.dstDirs, function(dstDir, next) {
|
|
1769
|
-
self.dataCopyCount++;
|
|
1770
|
-
self.minifyDone = !self.minify;
|
|
1771
|
-
copySrcToDst.call(self, service.srcDir, dstDir, next);
|
|
1772
|
-
}, next);
|
|
1773
|
-
}, next);
|
|
1774
|
-
} catch (err) {
|
|
1775
|
-
setImmediate(next, err);
|
|
1776
|
-
}
|
|
1777
|
-
}
|
|
1778
|
-
|
|
1779
|
-
// add service info into packageinfo.json.
|
|
1780
|
-
function addServiceInPkgInfo(next) {
|
|
1781
|
-
if (!this.rom) {
|
|
1782
|
-
const filename = path.join(this.packageDir, "packageinfo.json");
|
|
1783
|
-
let pkginfo, validServiceCount;
|
|
1784
|
-
try {
|
|
1785
|
-
const data = fs.readFileSync(filename);
|
|
1786
|
-
validServiceCount = 0;
|
|
1787
|
-
pkginfo = JSON.parse(data);
|
|
1788
|
-
log.silly("package#addServiceInPkgInfo()", "PACKAGEINFO:", pkginfo);
|
|
1789
|
-
} catch (err) {
|
|
1790
|
-
console.error(err);
|
|
1791
|
-
setImmediate(next, err);
|
|
1792
|
-
}
|
|
1793
|
-
this.services.filter(function(s) {
|
|
1794
|
-
return s.valid;
|
|
1795
|
-
}).forEach(function(service) {
|
|
1796
|
-
getPkgServiceNames(service.serviceInfo).forEach(function(serviceName) {
|
|
1797
|
-
this.pkgServiceNames.push(serviceName);
|
|
1798
|
-
validServiceCount++;
|
|
1799
|
-
}.bind(this));
|
|
1800
|
-
}.bind(this));
|
|
1801
|
-
|
|
1802
|
-
if (validServiceCount > 0) {
|
|
1803
|
-
pkginfo.services = this.pkgServiceNames;
|
|
1804
|
-
const data = JSON.stringify(pkginfo, null, 2) + "\n";
|
|
1805
|
-
log.silly("package#addServiceInPkgInfo()", "Modified package.json:" + data);
|
|
1806
|
-
fs.writeFile(path.join(this.packageDir, "packageinfo.json"), data, next);
|
|
1807
|
-
} else {
|
|
1808
|
-
setImmediate(next);
|
|
1809
|
-
}
|
|
1810
|
-
} else {
|
|
1811
|
-
setImmediate(next);
|
|
1812
|
-
}
|
|
1813
|
-
}
|
|
1814
|
-
|
|
1815
|
-
// remove service dir from tmp source dir before packaging
|
|
1816
|
-
function removeServiceFromAppDir(middleCb, next) {
|
|
1817
|
-
if (this.appCount === 0) {
|
|
1818
|
-
return setImmediate(next);
|
|
1819
|
-
}
|
|
1820
|
-
|
|
1821
|
-
let checkDir = this.applicationDir,
|
|
1822
|
-
needRmCheckDir = false;
|
|
1823
|
-
const fileList = fs.readdirSync(checkDir);
|
|
1824
|
-
|
|
1825
|
-
function _checkDir(dir) {
|
|
1826
|
-
if (this.dirName === dir) {
|
|
1827
|
-
try {
|
|
1828
|
-
const rmDir = path.join(this.applicationDir, this.dirName);
|
|
1829
|
-
shelljs.rm('-rf', rmDir);
|
|
1830
|
-
} catch (err) {
|
|
1831
|
-
next(Error("ERROR" + err), null);
|
|
1832
|
-
}
|
|
1833
|
-
}
|
|
1834
|
-
}
|
|
1835
|
-
|
|
1836
|
-
if (fileList.indexOf('services') !== -1) {
|
|
1837
|
-
checkDir = path.join(this.applicationDir, 'services');
|
|
1838
|
-
const stats = fs.statSync(checkDir);
|
|
1839
|
-
if (stats.isDirectory()) {
|
|
1840
|
-
needRmCheckDir = true;
|
|
1841
|
-
}
|
|
1842
|
-
}
|
|
1843
|
-
if (needRmCheckDir === true) {
|
|
1844
|
-
try {
|
|
1845
|
-
shelljs.rm('-rf', checkDir);
|
|
1846
|
-
} catch (err) {
|
|
1847
|
-
middleCb("ERROR:" + err);
|
|
1848
|
-
}
|
|
1849
|
-
} else {
|
|
1850
|
-
for (const idx in this.services) {
|
|
1851
|
-
this.dirName = this.services[idx].dirName;
|
|
1852
|
-
fileList.forEach(_checkDir, this);
|
|
1853
|
-
}
|
|
1854
|
-
}
|
|
1855
|
-
setImmediate(next);
|
|
1856
|
-
}
|
|
1857
|
-
|
|
1858
|
-
function loadResourceInfo(next) {
|
|
1859
|
-
log.verbose("loadResourceInfo");
|
|
1860
|
-
if (this.rscCount === 0) {
|
|
1861
|
-
return setImmediate(next);
|
|
1862
|
-
}
|
|
1863
|
-
|
|
1864
|
-
this.resources.forEach(function(resource) {
|
|
1865
|
-
const filePath = path.join(resource.dir, "resourceinfo.json");
|
|
1866
|
-
try {
|
|
1867
|
-
const data = fs.readFileSync(filePath),
|
|
1868
|
-
info = JSON.parse(data);
|
|
1869
|
-
resource.info = info;
|
|
1870
|
-
} catch (err) {
|
|
1871
|
-
return setImmediate(next, errHndl.getErrMsg("INVALID_JSON_FORMAT", "resourceinfo.json"));
|
|
1872
|
-
}
|
|
1873
|
-
});
|
|
1874
|
-
log.verbose("num of resourceInfo: " + this.resources.length);
|
|
1875
|
-
setImmediate(next);
|
|
1876
|
-
}
|
|
1877
|
-
|
|
1878
|
-
function checkResourceInfo(next) {
|
|
1879
|
-
log.verbose("checkResourceInfo");
|
|
1880
|
-
const pkgId = this.pkgId;
|
|
1881
|
-
|
|
1882
|
-
this.resources.forEach(function(resource) {
|
|
1883
|
-
if (!resource.info.id) {
|
|
1884
|
-
return setImmediate(next, errHndl.getErrMsg("REQUIRED_FIELD", "id"));
|
|
1885
|
-
} else if (resource.info.id.indexOf(pkgId) !== 0) {
|
|
1886
|
-
return setImmediate(next, errHndl.getErrMsg("INVALID_RESOURCEID", pkgId));
|
|
1887
|
-
} else if (resource.info.id.length < 1 || !(/^[a-z0-9.+-]*$/.test(resource.info.id))) {
|
|
1888
|
-
log.error(errHndl.getErrMsg("INVALID_VALUE", "id", resource.info.id));
|
|
1889
|
-
return setImmediate(next, errHndl.getErrMsg("INVALID_ID_RULE"));
|
|
1890
|
-
}
|
|
1891
|
-
});
|
|
1892
|
-
setImmediate(next);
|
|
1893
|
-
}
|
|
1894
|
-
|
|
1895
|
-
function createResourceDir(next) {
|
|
1896
|
-
log.verbose("createResourceDir");
|
|
1897
|
-
this.resources.forEach(function(resource) {
|
|
1898
|
-
try {
|
|
1899
|
-
const resourceDir = path.join(this.tempDir, "data/usr/palm/resources", resource.info.id);
|
|
1900
|
-
log.verbose("resource dir = " + resourceDir);
|
|
1901
|
-
resource.dstDir = resourceDir;
|
|
1902
|
-
mkdirp.sync(resourceDir);
|
|
1903
|
-
} catch (err) {
|
|
1904
|
-
return setImmediate(next, err);
|
|
1905
|
-
}
|
|
1906
|
-
}.bind(this));
|
|
1907
|
-
log.silly("this.resources:", this.resources);
|
|
1908
|
-
setImmediate(next);
|
|
1909
|
-
}
|
|
1910
|
-
|
|
1911
|
-
function copyResource(next) {
|
|
1912
|
-
log.verbose("copyResource()");
|
|
1913
|
-
if (this.rscCount === 0) {
|
|
1914
|
-
return setImmediate(next);
|
|
1915
|
-
}
|
|
1916
|
-
|
|
1917
|
-
const self = this;
|
|
1918
|
-
try {
|
|
1919
|
-
async.forEachSeries(this.resources, function(resource, next) {
|
|
1920
|
-
self.dataCopyCount++;
|
|
1921
|
-
log.verbose("copyResource(), copy " + resource.dir + " ==> " + resource.dstDir);
|
|
1922
|
-
copySrcToDst.call(self, resource.dir, resource.dstDir, next);
|
|
1923
|
-
}, next);
|
|
1924
|
-
} catch (err) {
|
|
1925
|
-
setImmediate(next, err);
|
|
1926
|
-
}
|
|
1927
|
-
}
|
|
1928
|
-
|
|
1929
|
-
function addResourceInPkgInfo(next) {
|
|
1930
|
-
log.verbose("addResourceInPkgInfo");
|
|
1931
|
-
if (!this.rom && this.rscCount > 0) {
|
|
1932
|
-
const filename = path.join(this.packageDir, "packageinfo.json");
|
|
1933
|
-
let pkginfo, data;
|
|
1934
|
-
try {
|
|
1935
|
-
data = fs.readFileSync(filename);
|
|
1936
|
-
log.verbose("PACKAGEINFO: " + data);
|
|
1937
|
-
pkginfo = JSON.parse(data);
|
|
1938
|
-
} catch (err) {
|
|
1939
|
-
console.error(err);
|
|
1940
|
-
setImmediate(next, err);
|
|
1941
|
-
}
|
|
1942
|
-
|
|
1943
|
-
this.resources.forEach(function(resource) {
|
|
1944
|
-
this.pkgResourceNames.push(resource.info.id);
|
|
1945
|
-
}.bind(this));
|
|
1946
|
-
|
|
1947
|
-
pkginfo.resources = this.pkgResourceNames;
|
|
1948
|
-
data = JSON.stringify(pkginfo, null, 2) + "\n";
|
|
1949
|
-
log.verbose("Modified package.json: " + data);
|
|
1950
|
-
fs.writeFile(path.join(this.packageDir, "packageinfo.json"), data, next);
|
|
1951
|
-
} else {
|
|
1952
|
-
setImmediate(next);
|
|
1953
|
-
}
|
|
1954
|
-
}
|
|
1955
|
-
|
|
1956
|
-
function copyData(inDirs, forceCopy, next) {
|
|
1957
|
-
log.verbose("package#copyData()", "only run when force packaging");
|
|
1958
|
-
if (forceCopy && this.dataCopyCount === 0) {
|
|
1959
|
-
const dst = path.join(this.tempDir, "data");
|
|
1960
|
-
async.forEachSeries(inDirs, function(src, next) {
|
|
1961
|
-
copySrcToDst.call(this, src, dst, next);
|
|
1962
|
-
}, function(err) {
|
|
1963
|
-
setImmediate(next, err);
|
|
1964
|
-
});
|
|
1965
|
-
} else {
|
|
1966
|
-
return setImmediate(next);
|
|
1967
|
-
}
|
|
1968
|
-
}
|
|
1969
|
-
|
|
1970
|
-
function getPkgServiceNames(serviceInfo) {
|
|
1971
|
-
let serviceNames = [];
|
|
1972
|
-
if (servicePkgMethod === "id") {
|
|
1973
|
-
serviceNames = [serviceInfo.id];
|
|
1974
|
-
} else if (serviceInfo.services) {
|
|
1975
|
-
const serviceProps = (serviceInfo.services instanceof Array)
|
|
1976
|
-
? serviceInfo.services : [serviceInfo.services];
|
|
1977
|
-
serviceNames = serviceProps.map(function(serviceProp) {
|
|
1978
|
-
return serviceProp.name;
|
|
1979
|
-
});
|
|
1980
|
-
}
|
|
1981
|
-
return serviceNames;
|
|
1982
|
-
}
|
|
1983
|
-
|
|
1984
|
-
function setUmask(mask, next) {
|
|
1985
|
-
this.oldmask = process.umask(mask);
|
|
1986
|
-
setImmediate(next);
|
|
1987
|
-
}
|
|
1988
|
-
|
|
1989
|
-
function recoverUmask(next) {
|
|
1990
|
-
if (this.oldmask) {
|
|
1991
|
-
process.umask(this.oldmask);
|
|
1992
|
-
}
|
|
1993
|
-
setImmediate(next);
|
|
1994
|
-
}
|
|
1995
|
-
|
|
1996
|
-
function rewriteFileWoBOMAsUtf8(filePath, rewriteFlag, next) {
|
|
1997
|
-
let data = fs.readFileSync(filePath);
|
|
1998
|
-
const encodingFormat = chardet.detect(Buffer.from(data));
|
|
1999
|
-
|
|
2000
|
-
if (['UTF-8', 'ISO-8895-1'].indexOf(encodingFormat) === -1) {
|
|
2001
|
-
log.silly("package#rewriteFileWoBOMAsUtf8()", "current encoding type:" + encodingFormat);
|
|
2002
|
-
data = encoding.convert(data, "UTF-8", encodingFormat);
|
|
2003
|
-
}
|
|
2004
|
-
data = stripbom(data);
|
|
2005
|
-
|
|
2006
|
-
if (rewriteFlag) {
|
|
2007
|
-
fs.writeFileSync(filePath,
|
|
2008
|
-
data, { encoding: "utf8" }
|
|
2009
|
-
);
|
|
2010
|
-
}
|
|
2011
|
-
|
|
2012
|
-
if (next !== 'undefined' && typeof next === 'function') {
|
|
2013
|
-
setImmediate(next, null, data);
|
|
2014
|
-
}
|
|
2015
|
-
return data;
|
|
2016
|
-
}
|
|
2017
|
-
|
|
2018
|
-
function encryptPackage(next) {
|
|
2019
|
-
if (this.rom || !this.encrypt) { // Do not encrypt when -rom option is given
|
|
2020
|
-
setImmediate(next);
|
|
2021
|
-
return;
|
|
2022
|
-
} else {
|
|
2023
|
-
this.encryptDir = path.join(this.tempDir, "encrypt");
|
|
2024
|
-
const encryptDir = this.encryptDir,
|
|
2025
|
-
encryptCtrlDir = path.join(encryptDir, 'ctrl'),
|
|
2026
|
-
ctrlTgzFile = path.join(encryptDir, 'control.tar.gz'),
|
|
2027
|
-
encryptDataDir = path.join(encryptDir, 'data'),
|
|
2028
|
-
dataTgzFile = path.join(encryptDir, 'data.tar.gz');
|
|
2029
|
-
|
|
2030
|
-
async.series([
|
|
2031
|
-
createDir.bind(this, encryptDir),
|
|
2032
|
-
createDir.bind(this, encryptCtrlDir),
|
|
2033
|
-
createDir.bind(this, encryptDataDir),
|
|
2034
|
-
createkeyIVfile.bind(this, encryptCtrlDir),
|
|
2035
|
-
encryptIpk.bind(this, encryptDataDir),
|
|
2036
|
-
makeTgz.bind(this, encryptDataDir, dataTgzFile),
|
|
2037
|
-
createControlFile.bind(this, encryptCtrlDir, true),
|
|
2038
|
-
makeTgz.bind(this, encryptCtrlDir, ctrlTgzFile),
|
|
2039
|
-
createDebianBinary.bind(this, encryptDir),
|
|
2040
|
-
makeIpk.bind(this, encryptDir)
|
|
2041
|
-
], function(err) {
|
|
2042
|
-
if (err) {
|
|
2043
|
-
setImmediate(next, err);
|
|
2044
|
-
return;
|
|
2045
|
-
}
|
|
2046
|
-
log.verbose("package#encryptPackage()", "success to encrypt pacakge");
|
|
2047
|
-
setImmediate(next);
|
|
2048
|
-
});
|
|
2049
|
-
}
|
|
2050
|
-
}
|
|
2051
|
-
|
|
2052
|
-
function createkeyIVfile(dstPath, next) {
|
|
2053
|
-
// generate random key& IV
|
|
2054
|
-
this.key = Buffer.from(crypto.randomBytes(32), 'base64');
|
|
2055
|
-
this.iv = Buffer.from(crypto.randomBytes(16), 'base64');
|
|
2056
|
-
|
|
2057
|
-
try {
|
|
2058
|
-
// Read public key
|
|
2059
|
-
const publickeyPath = path.join(__dirname, '../', 'files', 'conf', 'pubkey.pem'),
|
|
2060
|
-
publickey = fs.readFileSync(publickeyPath, 'utf8');
|
|
2061
|
-
|
|
2062
|
-
// Encrypt key, iv by publickey
|
|
2063
|
-
const encryptedKey= crypto.publicEncrypt({
|
|
2064
|
-
key: publickey,
|
|
2065
|
-
padding: 4 /* crypto.constants.RSA_PKCS1_OAEP_PADDING */
|
|
2066
|
-
}, Buffer.from(this.key.toString('base64')));
|
|
2067
|
-
|
|
2068
|
-
const encryptedIV= crypto.publicEncrypt({
|
|
2069
|
-
key: publickey,
|
|
2070
|
-
padding: 4 /* crypto.constants.RSA_PKCS1_OAEP_PADDING */
|
|
2071
|
-
}, Buffer.from(this.iv.toString('base64')));
|
|
2072
|
-
|
|
2073
|
-
const keyFilePath = path.join(dstPath, "key");
|
|
2074
|
-
fs.writeFileSync(keyFilePath, encryptedKey, 'binary');
|
|
2075
|
-
|
|
2076
|
-
// write iv file on encrypt/control
|
|
2077
|
-
const ivFilePath = path.join(dstPath , "iv");
|
|
2078
|
-
fs.writeFileSync(ivFilePath, encryptedIV, 'binary');
|
|
2079
|
-
|
|
2080
|
-
} catch (err) {
|
|
2081
|
-
setImmediate(next, errHndl.getErrMsg(err));
|
|
2082
|
-
}
|
|
2083
|
-
setImmediate(next);
|
|
2084
|
-
}
|
|
2085
|
-
|
|
2086
|
-
function copyOutputToDst(destination, middleCb, next) {
|
|
2087
|
-
// copy data directory to destination
|
|
2088
|
-
if (this.rom) {
|
|
2089
|
-
middleCb("Create output directory to " + destination);
|
|
2090
|
-
copySrcToDst.call(this, path.join(this.tempDir, 'data'), destination, next);
|
|
2091
|
-
} else if (this.encrypt) {
|
|
2092
|
-
// copy encrypted ipk to destination
|
|
2093
|
-
middleCb("Create encrypted " + this.ipkFileName + " to " + destination);
|
|
2094
|
-
copySrcToDst.call(this, path.join(this.encryptDir, this.ipkFileName), destination, next);
|
|
2095
|
-
} else {
|
|
2096
|
-
// copy plain ipk to destination
|
|
2097
|
-
let outmsg = "Create ";
|
|
2098
|
-
if (this.sign && this.certificate) {
|
|
2099
|
-
outmsg = outmsg.concat("signed ");
|
|
2100
|
-
}
|
|
2101
|
-
middleCb(outmsg + this.ipkFileName + " to " + destination);
|
|
2102
|
-
copySrcToDst.call(this, path.join(this.tempDir, this.ipkFileName), destination, next);
|
|
2103
|
-
}
|
|
2104
|
-
}
|
|
2105
|
-
|
|
2106
|
-
function encryptIpk(dstPath, next) {
|
|
2107
|
-
log.verbose("package#encryptPackage()#encrypIpk()", "encrypt plain ipk to /encrypt/data");
|
|
2108
|
-
const plainIpkPath = path.join(this.tempDir, this.ipkFileName),
|
|
2109
|
-
encrypedIpkPath = path.join(dstPath, this.ipkFileName);
|
|
2110
|
-
|
|
2111
|
-
try {
|
|
2112
|
-
const input = fs.createReadStream(plainIpkPath),
|
|
2113
|
-
output = fs.createWriteStream(encrypedIpkPath),
|
|
2114
|
-
cipher = crypto.createCipheriv('aes-256-cbc', this.key, this.iv);
|
|
2115
|
-
|
|
2116
|
-
output.on('close', function() {
|
|
2117
|
-
log.verbose("package#encryptPackage()#encrypIpk()", "encrypted Ipk to " + encrypedIpkPath);
|
|
2118
|
-
setImmediate(next);
|
|
2119
|
-
}).
|
|
2120
|
-
on('error', function(err) {
|
|
2121
|
-
setImmediate(next, err);
|
|
2122
|
-
});
|
|
2123
|
-
|
|
2124
|
-
input.pipe(cipher).pipe(output);
|
|
2125
|
-
} catch (err) {
|
|
2126
|
-
setImmediate(next, err);
|
|
2127
|
-
}
|
|
2128
|
-
}
|
|
2129
|
-
}());
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2020-2024 LG Electronics Inc.
|
|
3
|
+
*
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const ar = require('ar-async'),
|
|
8
|
+
async = require('async'),
|
|
9
|
+
chardet = require('chardet'),
|
|
10
|
+
CombinedStream = require('combined-stream'),
|
|
11
|
+
crypto = require('crypto'),
|
|
12
|
+
decompress = require('decompress'),
|
|
13
|
+
decompressTargz = require('decompress-targz'),
|
|
14
|
+
ElfParser = require('elfy').Parser,
|
|
15
|
+
encoding = require('encoding'),
|
|
16
|
+
fs = require('fs'),
|
|
17
|
+
fstream = require('fstream'),
|
|
18
|
+
Validator = require('jsonschema').Validator,
|
|
19
|
+
mkdirp = require('mkdirp'),
|
|
20
|
+
log = require('npmlog'),
|
|
21
|
+
path = require('path'),
|
|
22
|
+
rimraf = require('rimraf'),
|
|
23
|
+
shelljs = require('shelljs'),
|
|
24
|
+
stripbom = require('strip-bom'),
|
|
25
|
+
temp = require('temp'),
|
|
26
|
+
uglify = require('terser'),
|
|
27
|
+
util = require('util'),
|
|
28
|
+
zlib = require('zlib'),
|
|
29
|
+
tarFilterPack = require('./tar-filter-pack'),
|
|
30
|
+
errHndl = require('./base/error-handler');
|
|
31
|
+
|
|
32
|
+
(function() {
|
|
33
|
+
log.heading = 'packager';
|
|
34
|
+
log.level = 'warn';
|
|
35
|
+
|
|
36
|
+
const servicePkgMethod = 'id',
|
|
37
|
+
defaultAssetsFields = {
|
|
38
|
+
"main": true,
|
|
39
|
+
"icon": true,
|
|
40
|
+
"largeIcon": true,
|
|
41
|
+
"bgImage": true,
|
|
42
|
+
"splashBackground": true,
|
|
43
|
+
"imageForRecents": true,
|
|
44
|
+
"sysAssetsBasePath": true
|
|
45
|
+
},
|
|
46
|
+
FILE_TYPE = {
|
|
47
|
+
file: 'file',
|
|
48
|
+
dir: 'dir',
|
|
49
|
+
symlink: 'symlink'
|
|
50
|
+
},
|
|
51
|
+
packager = {};
|
|
52
|
+
let objectCounter = 0;
|
|
53
|
+
|
|
54
|
+
if (typeof module !== 'undefined' && module.exports) {
|
|
55
|
+
module.exports = packager;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function Packager() {
|
|
59
|
+
this.objectId = objectCounter++;
|
|
60
|
+
this.verbose = false;
|
|
61
|
+
this.silent = true;
|
|
62
|
+
this.noclean = false;
|
|
63
|
+
this.nativecmd = false;
|
|
64
|
+
this.minify = true;
|
|
65
|
+
this.excludeFiles = [];
|
|
66
|
+
this.rom = false;
|
|
67
|
+
this.encrypt = false;
|
|
68
|
+
this.sign = "";
|
|
69
|
+
this.certificate = "";
|
|
70
|
+
this.appCount = 0;
|
|
71
|
+
this.services = [];
|
|
72
|
+
this.pkgServiceNames = [];
|
|
73
|
+
this.rscCount = 0;
|
|
74
|
+
this.resources = [];
|
|
75
|
+
this.pkgResourceNames = [];
|
|
76
|
+
this.pkgId;
|
|
77
|
+
this.pkginfofile;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
packager.Packager = Packager;
|
|
81
|
+
|
|
82
|
+
Packager.prototype = {
|
|
83
|
+
prepareOptions: function(options, next) {
|
|
84
|
+
if (options && options.level) {
|
|
85
|
+
log.level = options.level;
|
|
86
|
+
if (['warn', 'error'].indexOf(options.level) !== -1) {
|
|
87
|
+
this.silent = false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (options && options.noclean === true) {
|
|
92
|
+
this.noclean = true;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (options && options.nativecmd === true) {
|
|
96
|
+
this.nativecmd = true;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (options && Object.prototype.hasOwnProperty.call(options, 'minify')) {
|
|
100
|
+
this.minify = options.minify;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (options && Object.prototype.hasOwnProperty.call(options, 'excludefiles')) {
|
|
104
|
+
if (options.excludefiles instanceof Array) {
|
|
105
|
+
this.excludeFiles = options.excludefiles;
|
|
106
|
+
} else {
|
|
107
|
+
this.excludeFiles.push(options.excludefiles);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
this.excludeFiles.forEach(function(excl_file) {
|
|
111
|
+
if (excl_file === "appinfo.json") {
|
|
112
|
+
return next(errHndl.getErrMsg("NOT_EXCLUDE_APPINFO"));
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (options && Object.prototype.hasOwnProperty.call(options, 'rom')) {
|
|
118
|
+
this.rom = options.rom;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (options && Object.prototype.hasOwnProperty.call(options, 'encrypt')) {
|
|
122
|
+
this.encrypt = options.encrypt;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (options && Object.prototype.hasOwnProperty.call(options, 'sign')) {
|
|
126
|
+
if (!fs.existsSync(path.resolve(options.sign))) {
|
|
127
|
+
return next(errHndl.getErrMsg("NOT_EXIST_PATH", options.sign));
|
|
128
|
+
}
|
|
129
|
+
this.sign = options.sign;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (options && Object.prototype.hasOwnProperty.call(options, 'certificate')) {
|
|
133
|
+
if (!fs.existsSync(path.resolve(options.certificate))) {
|
|
134
|
+
return next(errHndl.getErrMsg("NOT_EXIST_PATH", options.certificate));
|
|
135
|
+
}
|
|
136
|
+
this.certificate = options.certificate;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// check sign option must be used with certificate option
|
|
140
|
+
if ((this.sign && !this.certificate) ||
|
|
141
|
+
(this.certificate && !this.sign)) {
|
|
142
|
+
return next(errHndl.getErrMsg("USE_WITH_OPTIONS", "sign, certificate"));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
log.verbose("package#Packager()", "Packager id:" + this.objectId);
|
|
146
|
+
|
|
147
|
+
this.pkgVersion = options.pkgversion;
|
|
148
|
+
|
|
149
|
+
if (options && Object.prototype.hasOwnProperty.call(options, 'pkgid')) {
|
|
150
|
+
this.pkgId = options.pkgid;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (options && Object.prototype.hasOwnProperty.call(options, 'pkginfofile')) {
|
|
154
|
+
this.pkginfofile = options.pkginfofile;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (this.pkgId && this.pkginfofile) {
|
|
158
|
+
return next(errHndl.getErrMsg("NOT_USE_WITH_OPTIONS", "pkginfofile, pkgid"));
|
|
159
|
+
}
|
|
160
|
+
this.appCount = 0;
|
|
161
|
+
},
|
|
162
|
+
checkInputDirectories: function(inDirs, options, next) {
|
|
163
|
+
log.verbose("package#Packager#checkInputDirectories()", "input directory:", inDirs);
|
|
164
|
+
this.prepareOptions(options, next);
|
|
165
|
+
|
|
166
|
+
async.forEachSeries(inDirs, checkDirectory.bind(this, options), function(err) {
|
|
167
|
+
if (err) {
|
|
168
|
+
setImmediate(next, err);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
setImmediate(next);
|
|
172
|
+
});
|
|
173
|
+
return {app: this.appCount, resource: this.rscCount};
|
|
174
|
+
},
|
|
175
|
+
servicePackaging: function(inDirs, destination, options, middleCb, next) {
|
|
176
|
+
log.info("package#Packager#servicePackaging()");
|
|
177
|
+
|
|
178
|
+
if (!Object.hasOwnProperty.call(options, 'pkgid') && !Object.hasOwnProperty.call(options, 'pkginfofile')) {
|
|
179
|
+
return next(errHndl.getErrMsg("USE_PKGID_PKGINFO"));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async.series([
|
|
183
|
+
this.checkInputDirectories.bind(this, inDirs, options),
|
|
184
|
+
setUmask.bind(this, 0),
|
|
185
|
+
loadPkgInfo.bind(this),
|
|
186
|
+
createTmpDir.bind(this),
|
|
187
|
+
excludeIpkFileFromApp.bind(this),
|
|
188
|
+
createPackageDir.bind(this),
|
|
189
|
+
fillPackageDir.bind(this),
|
|
190
|
+
findServiceDir.bind(this, this.services),
|
|
191
|
+
loadServiceInfo.bind(this),
|
|
192
|
+
checkServiceInfo.bind(this),
|
|
193
|
+
createServiceDir.bind(this),
|
|
194
|
+
copyService.bind(this),
|
|
195
|
+
addServiceInPkgInfo.bind(this),
|
|
196
|
+
copyData.bind(this, inDirs, options.force),
|
|
197
|
+
loadPackageProperties.bind(this, inDirs),
|
|
198
|
+
excludeFromApp.bind(this, middleCb),
|
|
199
|
+
outputPackage.bind(this, destination),
|
|
200
|
+
encryptPackage.bind(this),
|
|
201
|
+
copyOutputToDst.bind(this, destination, middleCb),
|
|
202
|
+
recoverUmask.bind(this),
|
|
203
|
+
cleanupTmpDir.bind(this, middleCb)
|
|
204
|
+
], function(err) {
|
|
205
|
+
if (err) {
|
|
206
|
+
// TODO: call cleanupTmpDir() before returning
|
|
207
|
+
setImmediate(next, err);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
// TODO: probably some more checkings are needed
|
|
211
|
+
setImmediate(next, null, {ipk: this.ipk, msg: "Success"});
|
|
212
|
+
}.bind(this));
|
|
213
|
+
},
|
|
214
|
+
resourcePackaging: function(inDirs, destination, options, middleCb, next) {
|
|
215
|
+
log.info("package#Packager#resourcePackaging()");
|
|
216
|
+
this.dataCopyCount = 0;
|
|
217
|
+
async.series([
|
|
218
|
+
this.checkInputDirectories.bind(this, inDirs, options),
|
|
219
|
+
setUmask.bind(this, 0),
|
|
220
|
+
loadPkgInfo.bind(this),
|
|
221
|
+
createTmpDir.bind(this),
|
|
222
|
+
excludeIpkFileFromApp.bind(this),
|
|
223
|
+
createPackageDir.bind(this),
|
|
224
|
+
fillPackageDir.bind(this),
|
|
225
|
+
loadResourceInfo.bind(this),
|
|
226
|
+
checkResourceInfo.bind(this),
|
|
227
|
+
createResourceDir.bind(this),
|
|
228
|
+
copyResource.bind(this),
|
|
229
|
+
addResourceInPkgInfo.bind(this),
|
|
230
|
+
copyData.bind(this, inDirs, options.force),
|
|
231
|
+
loadPackageProperties.bind(this, inDirs),
|
|
232
|
+
excludeFromApp.bind(this, middleCb),
|
|
233
|
+
outputPackage.bind(this, destination),
|
|
234
|
+
encryptPackage.bind(this),
|
|
235
|
+
copyOutputToDst.bind(this, destination, middleCb),
|
|
236
|
+
recoverUmask.bind(this),
|
|
237
|
+
cleanupTmpDir.bind(this, middleCb)
|
|
238
|
+
], function(err) {
|
|
239
|
+
if (err) {
|
|
240
|
+
setImmediate(next, err);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
setImmediate(next, null, {ipk: this.ipk, msg: "Success"});
|
|
244
|
+
}.bind(this));
|
|
245
|
+
},
|
|
246
|
+
generatePackage: function(inDirs, destination, options, middleCb, next) {
|
|
247
|
+
log.info("package#Packager#generatePackage()", "from ", inDirs);
|
|
248
|
+
// check whether app or service directories are copied or not
|
|
249
|
+
this.dataCopyCount = 0;
|
|
250
|
+
this.minifyDone = !this.minify;
|
|
251
|
+
|
|
252
|
+
async.series([
|
|
253
|
+
this.checkInputDirectories.bind(this, inDirs, options),
|
|
254
|
+
setUmask.bind(this, 0),
|
|
255
|
+
loadPkgInfo.bind(this),
|
|
256
|
+
loadAppInfo.bind(this),
|
|
257
|
+
checkAppInfo.bind(this),
|
|
258
|
+
createTmpDir.bind(this),
|
|
259
|
+
createAppDir.bind(this),
|
|
260
|
+
checkELFHeader.bind(this),
|
|
261
|
+
fillAssetsField.bind(this),
|
|
262
|
+
copyAssets.bind(this),
|
|
263
|
+
copyApp.bind(this),
|
|
264
|
+
excludeIpkFileFromApp.bind(this),
|
|
265
|
+
createPackageDir.bind(this),
|
|
266
|
+
fillPackageDir.bind(this),
|
|
267
|
+
findServiceDir.bind(this, this.services),
|
|
268
|
+
loadServiceInfo.bind(this),
|
|
269
|
+
checkServiceInfo.bind(this),
|
|
270
|
+
createServiceDir.bind(this),
|
|
271
|
+
copyService.bind(this),
|
|
272
|
+
addServiceInPkgInfo.bind(this),
|
|
273
|
+
removeServiceFromAppDir.bind(this, middleCb),
|
|
274
|
+
copyData.bind(this, inDirs, options.force),
|
|
275
|
+
loadPackageProperties.bind(this, inDirs),
|
|
276
|
+
excludeFromApp.bind(this, middleCb),
|
|
277
|
+
outputPackage.bind(this, destination),
|
|
278
|
+
encryptPackage.bind(this),
|
|
279
|
+
copyOutputToDst.bind(this, destination, middleCb),
|
|
280
|
+
recoverUmask.bind(this),
|
|
281
|
+
cleanupTmpDir.bind(this, middleCb)
|
|
282
|
+
], function(err) {
|
|
283
|
+
if (err) {
|
|
284
|
+
// TODO: call cleanupTmpDir() before returning
|
|
285
|
+
setImmediate(next, err);
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
// TODO: probably some more checkings are needed
|
|
289
|
+
setImmediate(next, null, {ipk: this.ipk, msg: "Success"});
|
|
290
|
+
}.bind(this));
|
|
291
|
+
},
|
|
292
|
+
analyzeIPK: function(options, next) {
|
|
293
|
+
log.info("package#Packager#analyzeIPK()");
|
|
294
|
+
let ipkConfigFile, ipkFile, ipkConfig, tmpDirPath;
|
|
295
|
+
this.prepareOptions(options);
|
|
296
|
+
|
|
297
|
+
async.series([
|
|
298
|
+
_setConfig,
|
|
299
|
+
_unpackIpk,
|
|
300
|
+
_unpackTar,
|
|
301
|
+
_analyzeMetaFile,
|
|
302
|
+
_removeTmpDir,
|
|
303
|
+
], function(err, results) {
|
|
304
|
+
log.silly("package#analyzeIPK()", "err:", err, ", results:", results);
|
|
305
|
+
if (err) {
|
|
306
|
+
return next(err);
|
|
307
|
+
}
|
|
308
|
+
return next(null, {msg: results[3].trim()});
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
function _setConfig(next) {
|
|
312
|
+
log.info("package#analyzeIPK()#_setConfig()");
|
|
313
|
+
if (options.info === 'true') {
|
|
314
|
+
return next(errHndl.getErrMsg("EMPTY_VALUE", 'info'));
|
|
315
|
+
} else if (options.infodetail === 'true') {
|
|
316
|
+
return next(errHndl.getErrMsg("EMPTY_VALUE", 'info-detail'));
|
|
317
|
+
}
|
|
318
|
+
ipkFile = options.info ? options.info : options.infodetail;
|
|
319
|
+
ipkConfigFile = path.resolve(__dirname, "../files/conf/ipk.json");
|
|
320
|
+
|
|
321
|
+
if (path.extname(ipkFile) !== ".ipk") {
|
|
322
|
+
return next(errHndl.getErrMsg("SUPPORT_ONLY_IPK", ipkFile));
|
|
323
|
+
}
|
|
324
|
+
if (!fs.existsSync(ipkFile)) {
|
|
325
|
+
return next(errHndl.getErrMsg("NOT_EXIST_PATH", ipkFile));
|
|
326
|
+
}
|
|
327
|
+
if (!fs.existsSync(ipkConfigFile)) {
|
|
328
|
+
return next(errHndl.getErrMsg("NOT_EXIST_PATH", ipkConfigFile));
|
|
329
|
+
}
|
|
330
|
+
ipkConfig = JSON.parse(fs.readFileSync(ipkConfigFile));
|
|
331
|
+
tmpDirPath = temp.path(ipkConfig.tmpPath);
|
|
332
|
+
|
|
333
|
+
if (!fs.existsSync(tmpDirPath)) {
|
|
334
|
+
fs.mkdirSync(tmpDirPath);
|
|
335
|
+
}
|
|
336
|
+
next();
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function _unpackIpk(next) {
|
|
340
|
+
log.info("package#analyzeIPK()#_unpackIpk()");
|
|
341
|
+
const reader = new ar.ArReader(ipkFile);
|
|
342
|
+
|
|
343
|
+
reader.on("entry", function(entry, next) {
|
|
344
|
+
const name = entry.fileName();
|
|
345
|
+
entry.fileData()
|
|
346
|
+
.pipe(fs.createWriteStream(path.resolve(tmpDirPath, name)))
|
|
347
|
+
.on("finish", next);
|
|
348
|
+
});
|
|
349
|
+
reader.on("error", function(err) {
|
|
350
|
+
return next(err);
|
|
351
|
+
});
|
|
352
|
+
reader.on("close", function() {
|
|
353
|
+
log.verbose("package#analyzeIPK()#_unpackIpk()", "unpack ipk close");
|
|
354
|
+
next();
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function _unpackTar(next) {
|
|
359
|
+
log.info("package#analyzeIPK()#_unpackTar()");
|
|
360
|
+
if (fs.existsSync(path.resolve(tmpDirPath, "control.tar.gz"))) {
|
|
361
|
+
(async function() {
|
|
362
|
+
await decompress(path.resolve(tmpDirPath, "control.tar.gz"), tmpDirPath, {
|
|
363
|
+
plugins: [
|
|
364
|
+
decompressTargz()
|
|
365
|
+
]
|
|
366
|
+
});
|
|
367
|
+
log.verbose("package#analyzeIPK()#_unpackTar()", "control.tar.gz decompressed");
|
|
368
|
+
})();
|
|
369
|
+
} else if (fs.existsSync(path.resolve(tmpDirPath, "control.tar.xz"))) {
|
|
370
|
+
return next(errHndl.getErrMsg("NOT_SUPPORT_XZ"));
|
|
371
|
+
} else {
|
|
372
|
+
return next(errHndl.getErrMsg("NO_COMPONENT_FILE", ", control tar file"));
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (fs.existsSync(path.resolve(tmpDirPath, "data.tar.gz"))) {
|
|
376
|
+
(async function() {
|
|
377
|
+
await decompress(path.resolve(tmpDirPath, "data.tar.gz"), tmpDirPath, {
|
|
378
|
+
plugins: [
|
|
379
|
+
decompressTargz()
|
|
380
|
+
]
|
|
381
|
+
});
|
|
382
|
+
log.verbose("package#analyzeIPK()#_unpackTar()", "data.tar.gz decompressed");
|
|
383
|
+
next();
|
|
384
|
+
})();
|
|
385
|
+
} else if (fs.existsSync(path.resolve(tmpDirPath, "data.tar.xz"))) {
|
|
386
|
+
return next(errHndl.getErrMsg("NOT_SUPPORT_XZ"));
|
|
387
|
+
} else {
|
|
388
|
+
return next(errHndl.getErrMsg("NO_COMPONENT_FILE", "data tar file"));
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function _analyzeMetaFile(next) {
|
|
393
|
+
log.info("package#analyzeIPK()#_analyzeMetaFile()");
|
|
394
|
+
let result = "", targetFile, tmpTxt;
|
|
395
|
+
|
|
396
|
+
ipkConfig.webOSMetaFiles.forEach(function(item) {
|
|
397
|
+
const targetPath = path.resolve(tmpDirPath, ipkConfig[item].path);
|
|
398
|
+
// Analyze control file
|
|
399
|
+
if (item === "control") {
|
|
400
|
+
targetFile = path.resolve(targetPath, ipkConfig[item].fileName);
|
|
401
|
+
if (!fs.existsSync(targetFile)) {
|
|
402
|
+
return next(errHndl.getErrMsg("NO_COMPONENT_FILE", "control file"));
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (options.infodetail) {
|
|
406
|
+
result = "\n\n< " + ipkConfig[item].fileName + " >\n";
|
|
407
|
+
result += fs.readFileSync(targetFile).toString().trim();
|
|
408
|
+
} else {
|
|
409
|
+
result = ipkConfig[item].heading + "\n";
|
|
410
|
+
tmpTxt = fs.readFileSync(targetFile).toString().trim();
|
|
411
|
+
ipkConfig[item].info.forEach(function(field) {
|
|
412
|
+
if (tmpTxt.match(new RegExp(field + ": (.*)", "gi"))) {
|
|
413
|
+
result += tmpTxt.match(new RegExp(field + ": (.*)", "gi"))[0] + "\n";
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
// Analyze appinfo.json, packageinfo.json, services.json, package.json files
|
|
418
|
+
} else if (fs.existsSync(targetPath)) {
|
|
419
|
+
const dirArr = fs.readdirSync(targetPath);
|
|
420
|
+
let beforeDir = dirArr[0];
|
|
421
|
+
|
|
422
|
+
dirArr.forEach(function(dir) {
|
|
423
|
+
if (ipkConfig[item].fileName) {
|
|
424
|
+
ipkConfig[item].fileName.forEach(function(file) {
|
|
425
|
+
targetFile = path.resolve(targetPath, dir, file);
|
|
426
|
+
if (fs.existsSync(targetFile)) {
|
|
427
|
+
if (options.infodetail) {
|
|
428
|
+
result += "\n\n< " + file + " >\n";
|
|
429
|
+
result += fs.readFileSync(targetFile).toString().trim();
|
|
430
|
+
} else {
|
|
431
|
+
if (ipkConfig[item].heading && beforeDir !== path.join(ipkConfig[item].path, dir)) {
|
|
432
|
+
result += "\n" + ipkConfig[item].heading + "\n";
|
|
433
|
+
}
|
|
434
|
+
tmpTxt = fs.readFileSync(targetFile).toString().trim();
|
|
435
|
+
const tmpJson = JSON.parse(tmpTxt);
|
|
436
|
+
ipkConfig[item].info.forEach(function(field) {
|
|
437
|
+
if (tmpJson[field]) {
|
|
438
|
+
const fieldValue = tmpJson[field];
|
|
439
|
+
if (typeof fieldValue === "object") {
|
|
440
|
+
if (ipkConfig[item][field]) { // Case of services.json, services_name field
|
|
441
|
+
const subField = ipkConfig[item][field],
|
|
442
|
+
subResult = [];
|
|
443
|
+
fieldValue.forEach(function(subItem) {
|
|
444
|
+
subResult.push(subItem[subField]);
|
|
445
|
+
});
|
|
446
|
+
result += field + ": " + JSON.stringify(subResult) + "\n";
|
|
447
|
+
} else { // Case of packageinfo.json, services field
|
|
448
|
+
result += field + ": " + JSON.stringify(fieldValue) + "\n";
|
|
449
|
+
}
|
|
450
|
+
} else {
|
|
451
|
+
result += field + ": " + fieldValue + "\n";
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
beforeDir = path.join(ipkConfig[item].path, dir);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
});
|
|
463
|
+
next(null, result);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
function _removeTmpDir(next) {
|
|
467
|
+
log.info("package#analyzeIPK()#_removeTmpDir()");
|
|
468
|
+
rimraf(tmpDirPath, function(err) {
|
|
469
|
+
log.verbose("package#analyzeIPK()#_removeTmpDir()", "removed " + tmpDirPath);
|
|
470
|
+
next(err);
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
};
|
|
475
|
+
|
|
476
|
+
function Service() {
|
|
477
|
+
this.srcDir = "";
|
|
478
|
+
this.dstDirs = [];
|
|
479
|
+
this.valid = false;
|
|
480
|
+
this.serviceInfo = "";
|
|
481
|
+
this.dirName = "";
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// Private functions
|
|
485
|
+
function loadPkgInfo(next) {
|
|
486
|
+
log.verbose("loadPkgInfo");
|
|
487
|
+
let data;
|
|
488
|
+
|
|
489
|
+
if (this.pkginfofile) {
|
|
490
|
+
log.silly("Use pkginfofile option");
|
|
491
|
+
if (fs.existsSync(this.pkginfofile)) {
|
|
492
|
+
if ("packageinfo.json" !== path.basename(this.pkginfofile)) {
|
|
493
|
+
return setImmediate(next, errHndl.getErrMsg("INVALID_FILE", "packageinfo.json"));
|
|
494
|
+
}
|
|
495
|
+
data = rewriteFileWoBOMAsUtf8(this.pkginfofile, true);
|
|
496
|
+
try {
|
|
497
|
+
log.verbose("PKGINFO >>" + data + "<<");
|
|
498
|
+
this.pkginfo = JSON.parse(data);
|
|
499
|
+
|
|
500
|
+
if (!Object.prototype.hasOwnProperty.call(this.pkginfo, 'id')) {
|
|
501
|
+
return setImmediate(next, errHndl.getErrMsg("REQUIRED_FIELD", "id"));
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
this.pkgId = this.pkginfo.id;
|
|
505
|
+
this.pkgVersion = this.pkgVersion || this.pkginfo.version;
|
|
506
|
+
setImmediate(next);
|
|
507
|
+
}
|
|
508
|
+
catch (err) {
|
|
509
|
+
return setImmediate(next, errHndl.getErrMsg("INVALID_JSON_FORMAT", "packageinfo.json"));
|
|
510
|
+
}
|
|
511
|
+
} else {
|
|
512
|
+
return setImmediate(next, errHndl.getErrMsg("NOT_EXIST_PATH", this.pkginfofile));
|
|
513
|
+
}
|
|
514
|
+
} else if (this.pkgDir) {
|
|
515
|
+
log.silly("Use packageinfo.json from PKG_DIR");
|
|
516
|
+
data = rewriteFileWoBOMAsUtf8(path.join(this.pkgDir, "packageinfo.json"));
|
|
517
|
+
try {
|
|
518
|
+
log.verbose("PKGINFO >>" + data + "<<");
|
|
519
|
+
this.pkginfo = JSON.parse(data);
|
|
520
|
+
|
|
521
|
+
if (!Object.prototype.hasOwnProperty.call(this.pkginfo, 'id')) {
|
|
522
|
+
return setImmediate(next, errHndl.getErrMsg("REQUIRED_FIELD", "id"));
|
|
523
|
+
}
|
|
524
|
+
this.pkgId = this.pkginfo.id;
|
|
525
|
+
this.pkgVersion = this.pkgVersion || this.pkginfo.version;
|
|
526
|
+
setImmediate(next);
|
|
527
|
+
}
|
|
528
|
+
catch (err) {
|
|
529
|
+
return setImmediate(next, errHndl.getErrMsg("INVALID_JSON_FORMAT", "packageinfo.json"));
|
|
530
|
+
}
|
|
531
|
+
} else {
|
|
532
|
+
return setImmediate(next);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
function loadAppInfo(next) {
|
|
537
|
+
log.verbose("loadAppInfo");
|
|
538
|
+
if (this.appCount === 0) {
|
|
539
|
+
return setImmediate(next);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
const filepath = path.join(this.appDir, "appinfo.json"),
|
|
543
|
+
data = rewriteFileWoBOMAsUtf8(filepath, true);
|
|
544
|
+
try {
|
|
545
|
+
this.appinfo = JSON.parse(data);
|
|
546
|
+
log.silly("package#loadAppInfo()", "content of appinfo.json:", this.appinfo);
|
|
547
|
+
|
|
548
|
+
if (!this.appinfo.version || this.appinfo.version === undefined) {
|
|
549
|
+
this.appinfo.version = "1.0.0";
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
this.pkgVersion = this.pkgVersion || this.appinfo.version;
|
|
553
|
+
setImmediate(next);
|
|
554
|
+
} catch(err) {
|
|
555
|
+
setImmediate(next, err);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
function checkAppInfo(next) {
|
|
560
|
+
if (this.appCount === 0) {
|
|
561
|
+
return setImmediate(next);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// check enyo app
|
|
565
|
+
if (this.pkgJSExist && this.appinfo.main && this.appinfo.main.match(/(\.html|\.htm)$/gi)) {
|
|
566
|
+
const mainFile = path.join(this.appDir, this.appinfo.main);
|
|
567
|
+
if (!fs.existsSync(mainFile)) {
|
|
568
|
+
return setImmediate(next, errHndl.getErrMsg("NOT_EXIST_PATH", this.appinfo.main));
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
const regex = new RegExp("(<script[^>]*src[ \t]*=[ \t]*['\"])[^'\"]*/enyo.js(['\"])"),
|
|
572
|
+
data = fs.readFileSync(mainFile);
|
|
573
|
+
if (data.toString().match(regex)) {
|
|
574
|
+
// If enyo app, stop packaging.
|
|
575
|
+
return setImmediate(next, errHndl.getErrMsg("NOT_SUPPORT_ENYO"));
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
if (!this.appinfo.id || this.appinfo.id === undefined) {
|
|
580
|
+
return setImmediate(next, errHndl.getErrMsg("REQUIRED_FIELD", "id"));
|
|
581
|
+
}
|
|
582
|
+
if (this.appinfo.id.length < 1 || !(/^[a-z0-9.+-]*$/.test(this.appinfo.id))) {
|
|
583
|
+
log.error(errHndl.getErrMsg("INVALID_VALUE", "id", this.appinfo.id));
|
|
584
|
+
return setImmediate(next, errHndl.getErrMsg("INVALID_ID_RULE"));
|
|
585
|
+
}
|
|
586
|
+
if (this.pkgId && this.appinfo.id.indexOf(this.pkgId) !== 0) {
|
|
587
|
+
log.error(errHndl.getErrMsg("INVALID_VALUE", "id", this.appinfo.id));
|
|
588
|
+
return setImmediate(next, errHndl.getErrMsg("INVALID_APPID", this.pkgId));
|
|
589
|
+
}
|
|
590
|
+
if (!this.appinfo.version || this.appinfo.version === undefined) {
|
|
591
|
+
return setImmediate(next, errHndl.getErrMsg("REQUIRED_FIELD", "version"));
|
|
592
|
+
}
|
|
593
|
+
if (this.appinfo.version.length < 1 || !(/^([1-9]\d{0,8}|\d)\.([1-9]\d{0,8}|\d)\.([1-9]\d{0,8}|\d)$/.test(this.appinfo.version))) {
|
|
594
|
+
log.error(errHndl.getErrMsg("INVALID_VALUE", "version", this.appinfo.version));
|
|
595
|
+
return setImmediate(next, errHndl.getErrMsg("INVALID_VERSION_RULE"));
|
|
596
|
+
}
|
|
597
|
+
if (this.appinfo.type && this.appinfo.type.match(/clock/gi)) {
|
|
598
|
+
return setImmediate(next);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
const schemaFile = path.resolve(__dirname, "../files/schema/ApplicationDescription.schema");
|
|
602
|
+
async.waterfall([
|
|
603
|
+
fs.readFile.bind(this, schemaFile, "utf-8"),
|
|
604
|
+
function getSchema(data, next) {
|
|
605
|
+
try {
|
|
606
|
+
const schema = JSON.parse(data);
|
|
607
|
+
/* "required" keyword is redefined in draft 4.
|
|
608
|
+
But current jsonschema lib support only draft 3.
|
|
609
|
+
So this line changes "required" attribute according to the draft 3.
|
|
610
|
+
*/
|
|
611
|
+
const reqKeys = schema.required;
|
|
612
|
+
if (reqKeys) {
|
|
613
|
+
for (const key in schema.properties) {
|
|
614
|
+
if (reqKeys.indexOf(key) !== -1) {
|
|
615
|
+
schema.properties[key].required = true;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
next(null, schema);
|
|
620
|
+
} catch(err) {
|
|
621
|
+
next(errHndl.getErrMsg("INVALID_JSON_FORMAT", "AppDescription schema"));
|
|
622
|
+
}
|
|
623
|
+
},
|
|
624
|
+
function checkValid(schema, next) {
|
|
625
|
+
try {
|
|
626
|
+
next(null, new Validator().validate(this.appinfo, schema));
|
|
627
|
+
} catch (err) {
|
|
628
|
+
log.error(err);
|
|
629
|
+
next(errHndl.getErrMsg("INVALID_JSON_FORMAT"));
|
|
630
|
+
}
|
|
631
|
+
}.bind(this)
|
|
632
|
+
], function(err, result) {
|
|
633
|
+
if (err) {
|
|
634
|
+
setImmediate(next, err);
|
|
635
|
+
} else {
|
|
636
|
+
if (result && result.errors.length > 0) {
|
|
637
|
+
const errFile = "appinfo.json";
|
|
638
|
+
let errMsg = "";
|
|
639
|
+
for (const idx in result.errors) {
|
|
640
|
+
let errMsgLine = result.errors[idx].property + " " + result.errors[idx].message;
|
|
641
|
+
if (errMsgLine.indexOf("instance.") > -1) {
|
|
642
|
+
errMsgLine = errMsgLine.substring("instance.".length);
|
|
643
|
+
errMsg = errMsg.concat("\n");
|
|
644
|
+
errMsg = errMsg.concat(errMsgLine);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
errMsg = errHndl.getErrMsg("INVALID_FILE", errFile, errMsg);
|
|
648
|
+
return setImmediate(next, errMsg);
|
|
649
|
+
} else {
|
|
650
|
+
log.verbose("package#checkAppInfo()", "APPINFO is valid");
|
|
651
|
+
}
|
|
652
|
+
setImmediate(next);
|
|
653
|
+
}
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
function fillAssetsField(next) {
|
|
658
|
+
if (this.appCount === 0) {
|
|
659
|
+
return setImmediate(next);
|
|
660
|
+
}
|
|
661
|
+
// make appinfo.assets to have default values so that they can be copied into the package
|
|
662
|
+
this.appinfo.assets = this.appinfo.assets || [];
|
|
663
|
+
for (const i in this.appinfo) {
|
|
664
|
+
if (Object.prototype.hasOwnProperty.call(this.appinfo, i) && defaultAssetsFields[i]) {
|
|
665
|
+
// no duplicated adding & value should not null string & file/dir should exist
|
|
666
|
+
if ((this.appinfo.assets.indexOf(this.appinfo[i]) === -1) && this.appinfo[i]) {
|
|
667
|
+
this.appinfo.assets.push(this.appinfo[i]);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
// refer to appinfo.json files in localization directory.
|
|
673
|
+
const appInfoPath = this.originAppDir,
|
|
674
|
+
checkDir = path.join(this.originAppDir, "resources"),
|
|
675
|
+
foundFilePath = [],
|
|
676
|
+
resourcesAssets = [];
|
|
677
|
+
|
|
678
|
+
try {
|
|
679
|
+
const stat = fs.lstatSync(checkDir);
|
|
680
|
+
if (!stat.isDirectory()) {
|
|
681
|
+
return setImmediate(next, null);
|
|
682
|
+
}
|
|
683
|
+
} catch(err) {
|
|
684
|
+
if (err.code === "ENOENT") {
|
|
685
|
+
return setImmediate(next, null);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
async.series([
|
|
690
|
+
walkFolder.bind(null, checkDir, "appinfo.json", foundFilePath, 1),
|
|
691
|
+
function(next) {
|
|
692
|
+
async.forEach(foundFilePath, function(filePath, next) {
|
|
693
|
+
rewriteFileWoBOMAsUtf8(filePath, true, function(err, data) {
|
|
694
|
+
try {
|
|
695
|
+
const appInfo = JSON.parse(data),
|
|
696
|
+
dirPath = path.dirname(filePath);
|
|
697
|
+
for (const i in appInfo) {
|
|
698
|
+
if (Object.prototype.hasOwnProperty.call(appInfo, i) && defaultAssetsFields[i]) {
|
|
699
|
+
if (appInfo[i]) {
|
|
700
|
+
const itemPath = path.join(dirPath, appInfo[i]),
|
|
701
|
+
relPath = path.relative(appInfoPath, itemPath);
|
|
702
|
+
// no duplicated adding & value should not null string & file/dir should exist
|
|
703
|
+
if ((resourcesAssets.indexOf(relPath) === -1)) {
|
|
704
|
+
resourcesAssets.push(relPath);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
setImmediate(next, null);
|
|
710
|
+
} catch(error) {
|
|
711
|
+
setImmediate(next, errHndl.getErrMsg("INVALID_JSON_FORMAT", filePath));
|
|
712
|
+
}
|
|
713
|
+
});
|
|
714
|
+
}, function(err) {
|
|
715
|
+
setImmediate(next, err);
|
|
716
|
+
});
|
|
717
|
+
},
|
|
718
|
+
function(next) {
|
|
719
|
+
this.appinfo.assets = this.appinfo.assets.concat(resourcesAssets);
|
|
720
|
+
setImmediate(next, null);
|
|
721
|
+
}.bind(this)
|
|
722
|
+
], function(err) {
|
|
723
|
+
setImmediate(next, err);
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
function createTmpDir(next) {
|
|
728
|
+
this.tempDir = temp.path({prefix: 'com.palm.ares.hermes.bdOpenwebOS'}) + '.d';
|
|
729
|
+
log.verbose("package#createTmpDir()", "temp dir:", this.tempDir);
|
|
730
|
+
mkdirp(this.tempDir, next);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
function createAppDir(next) {
|
|
734
|
+
if (this.appCount === 0) {
|
|
735
|
+
return setImmediate(next);
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
try {
|
|
739
|
+
this.applicationDir = path.resolve(this.tempDir, "data/usr/palm/applications", this.appinfo.id);
|
|
740
|
+
log.info("package#createAppDir()", "application dir:" + this.applicationDir);
|
|
741
|
+
mkdirp(this.applicationDir, next);
|
|
742
|
+
} catch (err) {
|
|
743
|
+
return setImmediate(next, err);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
function copySrcToDst(src, dst, next) {
|
|
748
|
+
const fileList = [],
|
|
749
|
+
self = this,
|
|
750
|
+
requireMinify = !!((!!self.minify && !self.minifyDone));
|
|
751
|
+
src = path.normalize(path.resolve(src));
|
|
752
|
+
dst = path.normalize(path.resolve(dst));
|
|
753
|
+
|
|
754
|
+
async.series([
|
|
755
|
+
function(next) {
|
|
756
|
+
const stat = fs.statSync(src);
|
|
757
|
+
if (stat.isFile()) {
|
|
758
|
+
_pushList(fileList, 'file', path.dirname(src), path.basename(src), true, null);
|
|
759
|
+
setImmediate(next);
|
|
760
|
+
} else {
|
|
761
|
+
_getFileList(src, src, fileList, next);
|
|
762
|
+
}
|
|
763
|
+
},
|
|
764
|
+
_copySrcToDst.bind(null, fileList, dst, requireMinify)
|
|
765
|
+
], function(err) {
|
|
766
|
+
next(err);
|
|
767
|
+
});
|
|
768
|
+
|
|
769
|
+
function _pushList(list, type, basePath, relPath, isSubPath, indRelPath) {
|
|
770
|
+
if (!FILE_TYPE[type]) {
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
list.push({
|
|
774
|
+
type: type,
|
|
775
|
+
basePath: basePath,
|
|
776
|
+
relPath: relPath,
|
|
777
|
+
isSubPath: isSubPath,
|
|
778
|
+
indRelPath: indRelPath
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
function _getFileList(dirPath, basePath, files, next) {
|
|
783
|
+
// TODO: the following code should be more concise.
|
|
784
|
+
// Handling symbolic links
|
|
785
|
+
// if the path sym-link indicates is a sub-path of source directory, treat a sym-link as it is.
|
|
786
|
+
// otherwise the files sym-link indicates should be copied
|
|
787
|
+
async.waterfall([
|
|
788
|
+
fs.readdir.bind(null, dirPath),
|
|
789
|
+
function(fileNames, next) {
|
|
790
|
+
if (fileNames.length === 0) {
|
|
791
|
+
_pushList(files, 'dir', basePath, path.relative(basePath, dirPath), true, null);
|
|
792
|
+
return setImmediate(next);
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
async.forEachSeries(fileNames, function(fileName, next) {
|
|
796
|
+
const filePath = path.join(dirPath, fileName),
|
|
797
|
+
relPath = path.relative(basePath, filePath);
|
|
798
|
+
|
|
799
|
+
async.waterfall([
|
|
800
|
+
fs.lstat.bind(null, filePath),
|
|
801
|
+
function(lstat, next) {
|
|
802
|
+
if (lstat.isSymbolicLink()) {
|
|
803
|
+
let indicateFullPath;
|
|
804
|
+
try {
|
|
805
|
+
indicateFullPath = fs.realpathSync(filePath);
|
|
806
|
+
} catch (err) {
|
|
807
|
+
if (err.code === 'ENOENT') {
|
|
808
|
+
log.warn("The file for symbolic link (" + filePath + ") is missing...");
|
|
809
|
+
return setImmediate(next);
|
|
810
|
+
}
|
|
811
|
+
return setImmediate(next, err);
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
const indicateRelPath = fs.readlinkSync(filePath);
|
|
815
|
+
if (indicateFullPath.indexOf(basePath) !== -1) {
|
|
816
|
+
_pushList(files, 'symlink', basePath, relPath, true, indicateRelPath);
|
|
817
|
+
} else {
|
|
818
|
+
const stat = fs.statSync(filePath);
|
|
819
|
+
if (stat.isDirectory()) {
|
|
820
|
+
return _getFileList(filePath, basePath, files, next);
|
|
821
|
+
} else if (stat.isFile()) {
|
|
822
|
+
_pushList(files, 'file', basePath, relPath, true, null);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
setImmediate(next);
|
|
826
|
+
} else if (lstat.isDirectory()) {
|
|
827
|
+
return _getFileList(filePath, basePath, files, next);
|
|
828
|
+
} else if (lstat.isFile()) {
|
|
829
|
+
_pushList(files, 'file', basePath, relPath, true, null);
|
|
830
|
+
setImmediate(next);
|
|
831
|
+
} else {
|
|
832
|
+
setImmediate(next);
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
], next); // async.waterfall
|
|
836
|
+
}, next); // async.forEach
|
|
837
|
+
}
|
|
838
|
+
], function(err) {
|
|
839
|
+
return setImmediate(next, err);
|
|
840
|
+
}); // async.waterfall
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
function _copySrcToDst(files, dstPath, minify, next) {
|
|
844
|
+
try {
|
|
845
|
+
async.forEachSeries(files, function(file, next) {
|
|
846
|
+
if (!FILE_TYPE[file.type]) {
|
|
847
|
+
log.verbose("package#copySrcToDst()#_copySrcToDst()", "ignore 'unknown file type'("+file.type+")");
|
|
848
|
+
return;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
if (!file.relPath) {
|
|
852
|
+
log.verbose("package#copySrcToDst()#_copySrcToDst()", "ignore 'unknown path'");
|
|
853
|
+
return setImmediate(next);
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
if (file.type === FILE_TYPE.dir) {
|
|
857
|
+
mkdirp.sync(path.join(dstPath, file.relPath));
|
|
858
|
+
return setImmediate(next);
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
const dstDirPath = path.dirname(path.join(dstPath, file.relPath));
|
|
862
|
+
if (!fs.existsSync(dstDirPath)) {
|
|
863
|
+
mkdirp.sync(dstDirPath);
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
if (file.type === FILE_TYPE.symlink) {
|
|
867
|
+
if (file.isSubPath && file.indRelPath) {
|
|
868
|
+
const linkFile = path.join(dstPath, file.relPath);
|
|
869
|
+
if (fs.existsSync(linkFile)) {
|
|
870
|
+
if (fs.lstatSync(linkFile).isSymbolicLink()) {
|
|
871
|
+
fs.unlinkSync(linkFile);
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
fs.symlinkSync(file.indRelPath, linkFile, null);
|
|
875
|
+
}
|
|
876
|
+
} else {
|
|
877
|
+
const sourceFile = path.join(file.basePath, file.relPath);
|
|
878
|
+
if (fs.existsSync(sourceFile)) {
|
|
879
|
+
if (minify && '.js' === path.extname(sourceFile) && file.relPath.indexOf('node_modules') === -1) {
|
|
880
|
+
log.verbose("package#copySrcToDst()#_copySrcToDst()", "require minification # sourceFile:", sourceFile);
|
|
881
|
+
try {
|
|
882
|
+
const data = uglify.minify(fs.readFileSync(sourceFile,'utf8'));
|
|
883
|
+
if (data.error) {
|
|
884
|
+
throw data.error;
|
|
885
|
+
}
|
|
886
|
+
fs.writeFileSync(path.join(dstPath, file.relPath), data.code, 'utf8');
|
|
887
|
+
} catch (e) {
|
|
888
|
+
log.verbose("package#copySrcToDst()#_copySrcToDst()", util.format('Failed to uglify code %s: %s', sourceFile, e.stack));
|
|
889
|
+
return setImmediate(next, errHndl.getErrMsg("FAILED_MINIFY", sourceFile));
|
|
890
|
+
}
|
|
891
|
+
} else {
|
|
892
|
+
shelljs.cp('-Rf', sourceFile, path.join(dstPath, file.relPath, '..'));
|
|
893
|
+
}
|
|
894
|
+
} else {
|
|
895
|
+
log.verbose("package#copySrcToDst()#_copySrcToDst()", "ignore '" + file.relPath + "'");
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
setImmediate(next);
|
|
899
|
+
}, function(err) {
|
|
900
|
+
if (!err && minify) {
|
|
901
|
+
self.minifyDone = true;
|
|
902
|
+
}
|
|
903
|
+
setImmediate(next, err);
|
|
904
|
+
});
|
|
905
|
+
} catch(err) {
|
|
906
|
+
setImmediate(next, err);
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
function checkELFHeader(next) {
|
|
912
|
+
const self = this,
|
|
913
|
+
ELF_HEADER_LEN = 64,
|
|
914
|
+
buf = Buffer.alloc(ELF_HEADER_LEN),
|
|
915
|
+
mainFile = path.resolve(path.join(this.appDir, this.appinfo.main));
|
|
916
|
+
|
|
917
|
+
if (!fs.existsSync(mainFile)) {
|
|
918
|
+
return setImmediate(next, errHndl.getErrMsg("NOT_EXIST_PATH", mainFile));
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
const fd = fs.openSync(mainFile, 'r'),
|
|
922
|
+
stats = fs.fstatSync(fd),
|
|
923
|
+
elfParser = new ElfParser(),
|
|
924
|
+
_isELF = function(_buf) {
|
|
925
|
+
if (_buf.slice(0, 4).toString() !== '\x7fELF') {
|
|
926
|
+
return false;
|
|
927
|
+
} else {
|
|
928
|
+
return true;
|
|
929
|
+
}
|
|
930
|
+
};
|
|
931
|
+
|
|
932
|
+
if (stats.size < ELF_HEADER_LEN) {
|
|
933
|
+
log.verbose("package#checkELFHeader()", "file size is smaller than ELF Header size");
|
|
934
|
+
return setImmediate(next);
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
fs.read(fd, buf, 0, ELF_HEADER_LEN, 0, function(err, bytesRead, _buf) {
|
|
938
|
+
if (bytesRead < ELF_HEADER_LEN || err) {
|
|
939
|
+
log.silly("package#checkELFHeader()", "err:", err, ", bytesRead:", bytesRead);
|
|
940
|
+
log.silly("package#checkELFHeader()", "readBuf to parse ELF header is small or error occurred during reading file.");
|
|
941
|
+
return setImmediate(next);
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
if (!_isELF(_buf)) {
|
|
945
|
+
log.silly("package#checkELFHeader()", mainFile + " is not ELF format");
|
|
946
|
+
} else {
|
|
947
|
+
log.silly("package#checkELFHeader()", mainFile + " is ELF format");
|
|
948
|
+
try {
|
|
949
|
+
const elfHeader = elfParser.parseHeader(_buf);
|
|
950
|
+
log.silly("package#checkELFHeader()", "elfHeader:", elfHeader);
|
|
951
|
+
|
|
952
|
+
if (elfHeader.machine && elfHeader.machine.match(/86$/)) {
|
|
953
|
+
// current emulator opkg is allowing only all, noarch and i586.
|
|
954
|
+
// when it is used with --offline-root.
|
|
955
|
+
self.architecture = 'i586';
|
|
956
|
+
} else if (elfHeader.machine && elfHeader.machine.match(/amd64$/)) {
|
|
957
|
+
// change amd64 to x86_64
|
|
958
|
+
self.architecture = 'x86_64';
|
|
959
|
+
} else if (elfHeader.machine && elfHeader.machine.match(/AArch64$/)) {
|
|
960
|
+
// change AArch64 to aarch64
|
|
961
|
+
self.architecture = 'aarch64';
|
|
962
|
+
} else {
|
|
963
|
+
self.architecture = elfHeader.machine;
|
|
964
|
+
}
|
|
965
|
+
} catch(e) {
|
|
966
|
+
log.verbose("package#checkELFHeader()", "exception:", e);
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
log.verbose("package#checkELFHeader()", "machine:", self.architecture);
|
|
970
|
+
fs.close(fd);
|
|
971
|
+
setImmediate(next);
|
|
972
|
+
});
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
function copyApp(next) {
|
|
976
|
+
if (this.appCount === 0) {
|
|
977
|
+
return setImmediate(next);
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
this.dataCopyCount++;
|
|
981
|
+
log.info("package#copyApp()", "copy " + this.appDir + " ==> " + this.applicationDir);
|
|
982
|
+
copySrcToDst.call(this, this.appDir, this.applicationDir, next);
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
function copyAssets(next) {
|
|
986
|
+
if (this.appCount === 0) {
|
|
987
|
+
return setImmediate(next);
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
try {
|
|
991
|
+
async.forEachSeries(this.appinfo.assets, _handleAssets.bind(this), next);
|
|
992
|
+
} catch (err) {
|
|
993
|
+
return setImmediate(next, err);
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
function _handleAssets(file, next) {
|
|
997
|
+
if (path.resolve(this.originAppDir) === path.resolve(this.appDir)) {
|
|
998
|
+
_checkAppinfo.call(this, file, next);
|
|
999
|
+
} else {
|
|
1000
|
+
async.series([
|
|
1001
|
+
_checkAppinfo.bind(this, file),
|
|
1002
|
+
_copyAssets.bind(this, file)
|
|
1003
|
+
], next);
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
function _checkAppinfo(file, next) {
|
|
1008
|
+
let source;
|
|
1009
|
+
if (path.resolve(file) === path.normalize(file)) {
|
|
1010
|
+
return next(errHndl.getErrMsg("NOT_RELATIVE_PATH_APPINFO", file));
|
|
1011
|
+
} else {
|
|
1012
|
+
source = path.join(this.originAppDir, file);
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
if (path.resolve(source).indexOf(this.originAppDir) !== 0) {
|
|
1016
|
+
return next(errHndl.getErrMsg("NOT_RELATIVE_PATH_APPINFO", file));
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
if (!fs.existsSync(source) && !this.rom) {
|
|
1020
|
+
const msg = errHndl.getErrMsg("NOT_EXIST_PATH", file);
|
|
1021
|
+
if (path.basename(source).indexOf('$') === 0) {
|
|
1022
|
+
// ignore property with starting $ prefix (dynamic property handling in the platform)
|
|
1023
|
+
return setImmediate(next);
|
|
1024
|
+
} else {
|
|
1025
|
+
return setImmediate(next, msg);
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
setImmediate(next);
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
function _copyAssets(file, next) {
|
|
1032
|
+
log.verbose("package#copyAssets()#_copyAssets", "'" + file + "' will be located in app directory");
|
|
1033
|
+
const source = path.join(this.originAppDir, file),
|
|
1034
|
+
destination = this.appDir;
|
|
1035
|
+
|
|
1036
|
+
async.series([
|
|
1037
|
+
function(next) {
|
|
1038
|
+
if (!fs.existsSync(destination)) {
|
|
1039
|
+
mkdirp(destination, next);
|
|
1040
|
+
} else {
|
|
1041
|
+
setImmediate(next);
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
], function(err) {
|
|
1045
|
+
if (err) {
|
|
1046
|
+
return setImmediate(next, err);
|
|
1047
|
+
}
|
|
1048
|
+
shelljs.cp('-Rf', source, destination);
|
|
1049
|
+
setImmediate(next);
|
|
1050
|
+
});
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
function excludeIpkFileFromApp(next) {
|
|
1055
|
+
// Exclude a pre-built .ipk file
|
|
1056
|
+
this.excludeFiles = this.excludeFiles.concat([
|
|
1057
|
+
// eslint-disable-next-line no-useless-escape
|
|
1058
|
+
"[.]*[\\.]ipk",
|
|
1059
|
+
".DS_Store",
|
|
1060
|
+
// ".vscode",
|
|
1061
|
+
// ".reloadignore",
|
|
1062
|
+
// ".launchparams.json"
|
|
1063
|
+
]);
|
|
1064
|
+
setImmediate(next);
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
function _retrieve(list, regExp, dirPath, depth, next) {
|
|
1068
|
+
async.waterfall([
|
|
1069
|
+
fs.readdir.bind(null, dirPath), function(fileNames, next) {
|
|
1070
|
+
async.forEach(fileNames, function(fileName, next) {
|
|
1071
|
+
const filePath = path.join(dirPath, fileName);
|
|
1072
|
+
async.waterfall([
|
|
1073
|
+
fs.lstat.bind(null, filePath), function(stat, next) {
|
|
1074
|
+
let result = false;
|
|
1075
|
+
if (depth > 1 && regExp.test(fileName)) {
|
|
1076
|
+
result = true;
|
|
1077
|
+
list.push(filePath);
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
if (!result && stat.isDirectory()) {
|
|
1081
|
+
_retrieve(list, regExp, filePath, depth + 1, next);
|
|
1082
|
+
} else {
|
|
1083
|
+
setImmediate(next);
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
], next);
|
|
1087
|
+
}, next);
|
|
1088
|
+
}
|
|
1089
|
+
], function(err) {
|
|
1090
|
+
setImmediate(next, err);
|
|
1091
|
+
});
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
function excludeFromApp(middleCb, next) {
|
|
1095
|
+
let excludes;
|
|
1096
|
+
if (this.appCount === 0) {
|
|
1097
|
+
excludes = this.excludeFiles;
|
|
1098
|
+
} else {
|
|
1099
|
+
excludes = this.excludeFiles.concat(this.appinfo.exclude || []);
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
const regExpQueries = excludes.map(function(exclude) {
|
|
1103
|
+
return exclude.replace(/^\./g,"^\\.").replace(/^\*/g,"").replace(/$/g,"$");
|
|
1104
|
+
}, this),
|
|
1105
|
+
strRegExp = regExpQueries.join("|"),
|
|
1106
|
+
regExp = new RegExp(strRegExp, "i"),
|
|
1107
|
+
excludeList = [],
|
|
1108
|
+
tempDirPrefix = path.resolve(this.tempDir, "data/usr/palm");
|
|
1109
|
+
|
|
1110
|
+
async.series([
|
|
1111
|
+
_retrieve.bind(this, excludeList, regExp, tempDirPrefix, 0), function(next) {
|
|
1112
|
+
const meta_files = ["appinfo.json", "services.json", "packageinfo.json", "resourceinfo.json"],
|
|
1113
|
+
unexcludable = [],
|
|
1114
|
+
excluded = [];
|
|
1115
|
+
try {
|
|
1116
|
+
excludeList.forEach(function(file) {
|
|
1117
|
+
if (meta_files.includes(path.basename(file))) {
|
|
1118
|
+
unexcludable.push(path.basename(file));
|
|
1119
|
+
} else {
|
|
1120
|
+
const sliced = file.slice(tempDirPrefix.length + 1);
|
|
1121
|
+
excluded.push(sliced.slice(sliced.indexOf(path.sep) + 1));
|
|
1122
|
+
shelljs.rm('-rf', file);
|
|
1123
|
+
}
|
|
1124
|
+
});
|
|
1125
|
+
if (unexcludable.length > 0) {
|
|
1126
|
+
middleCb("Cannot exclude: " + unexcludable); // FIXME
|
|
1127
|
+
}
|
|
1128
|
+
if (excluded.length > 0) {
|
|
1129
|
+
middleCb("Excluded: " + excluded); // FIXME
|
|
1130
|
+
}
|
|
1131
|
+
setImmediate(next);
|
|
1132
|
+
} catch(err) {
|
|
1133
|
+
setImmediate(next, err);
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
], function(err) {
|
|
1137
|
+
if (err) {
|
|
1138
|
+
return setImmediate(next, err);
|
|
1139
|
+
}
|
|
1140
|
+
setImmediate(next);
|
|
1141
|
+
});
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
function createPackageDir(next) {
|
|
1145
|
+
if (!this.rom) {
|
|
1146
|
+
const pkgDirName = this.pkgId || this.appinfo.id;
|
|
1147
|
+
this.packageDir = path.join(this.tempDir, "data/usr/palm/packages", pkgDirName);
|
|
1148
|
+
log.verbose("package#createPackageDir", "directory:", this.packageDir);
|
|
1149
|
+
mkdirp(this.packageDir, next);
|
|
1150
|
+
} else {
|
|
1151
|
+
setImmediate(next);
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
function fillPackageDir(next) {
|
|
1156
|
+
log.verbose("fillPackageDir");
|
|
1157
|
+
|
|
1158
|
+
if (!this.rom) {
|
|
1159
|
+
let data = "";
|
|
1160
|
+
if (this.pkginfo) {
|
|
1161
|
+
// Use pkginfofile option or PKG_DIR
|
|
1162
|
+
if (!this.pkginfo.version) {
|
|
1163
|
+
this.pkginfo.version = this.pkgVersion || "1.0.0";
|
|
1164
|
+
}
|
|
1165
|
+
if (this.appinfo) {
|
|
1166
|
+
this.pkginfo.app = this.appinfo.id;
|
|
1167
|
+
}
|
|
1168
|
+
_checkPkgInfo(this.pkginfo.id, this.pkginfo.version, next);
|
|
1169
|
+
data = JSON.stringify(this.pkginfo, null, 2) + "\n";
|
|
1170
|
+
} else {
|
|
1171
|
+
// Use pkgid option or no option
|
|
1172
|
+
const pkginfo = {
|
|
1173
|
+
"id": this.pkgId || this.appinfo.id,
|
|
1174
|
+
"version": this.pkgVersion || "1.0.0"
|
|
1175
|
+
};
|
|
1176
|
+
if (this.appinfo) {
|
|
1177
|
+
pkginfo.app = this.appinfo.id;
|
|
1178
|
+
}
|
|
1179
|
+
_checkPkgInfo(pkginfo.id, pkginfo.version, next);
|
|
1180
|
+
data = JSON.stringify(pkginfo, null, 2) + "\n";
|
|
1181
|
+
}
|
|
1182
|
+
log.verbose("Generating packageinfo.json: " + data);
|
|
1183
|
+
fs.writeFile(path.join(this.packageDir, "packageinfo.json"), data, next);
|
|
1184
|
+
} else {
|
|
1185
|
+
setImmediate(next);
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
function _checkPkgInfo(id, version, next) {
|
|
1189
|
+
if (!(/^[a-z0-9.+-]*$/.test(id))) {
|
|
1190
|
+
log.error(errHndl.getErrMsg("INVALID_VALUE", "pkg id", id));
|
|
1191
|
+
return setImmediate(next, errHndl.getErrMsg("INVALID_ID_RULE"));
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
if (!(/^([1-9]\d{0,8}|\d)\.([1-9]\d{0,8}|\d)\.([1-9]\d{0,8}|\d)$/.test(version))) {
|
|
1195
|
+
log.error(errHndl.getErrMsg("INVALID_VALUE", "pkg version", version));
|
|
1196
|
+
return setImmediate(next, errHndl.getErrMsg("INVALID_VERSION_RULE"));
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
|
|
1202
|
+
function loadPackageProperties(inDirs, next) {
|
|
1203
|
+
const self = this;
|
|
1204
|
+
self.packageProperties = {};
|
|
1205
|
+
|
|
1206
|
+
async.forEach(inDirs, function(inDir, next) {
|
|
1207
|
+
const filename = path.join(inDir, "package.properties");
|
|
1208
|
+
|
|
1209
|
+
function _checkFileMode(file) {
|
|
1210
|
+
file = file.replace(/\\/g, "/").trim();
|
|
1211
|
+
const idx = file.lastIndexOf("/");
|
|
1212
|
+
file = (idx !== -1) ? file.slice(idx + 1) : file;
|
|
1213
|
+
self.packageProperties[file] = this.fileMode;
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
if (fs.existsSync(filename)) {
|
|
1217
|
+
const data = fs.readFileSync(filename);
|
|
1218
|
+
try {
|
|
1219
|
+
const lines = data.toString().split("\n");
|
|
1220
|
+
let seperatorIndex;
|
|
1221
|
+
|
|
1222
|
+
for (const i in lines) {
|
|
1223
|
+
if (lines[i].indexOf("filemode.") === 0) {
|
|
1224
|
+
seperatorIndex = lines[i].indexOf("=");
|
|
1225
|
+
const fileList = lines[i].slice(seperatorIndex + 1).trim(),
|
|
1226
|
+
fileArray = fileList.split(",");
|
|
1227
|
+
this.fileMode = lines[i].slice(9, seperatorIndex).trim();
|
|
1228
|
+
|
|
1229
|
+
fileArray.forEach(_checkFileMode);
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
setImmediate(next);
|
|
1233
|
+
} catch (error) {
|
|
1234
|
+
setImmediate(next, error);
|
|
1235
|
+
}
|
|
1236
|
+
} else {
|
|
1237
|
+
setImmediate(next);
|
|
1238
|
+
}
|
|
1239
|
+
}, function(err) {
|
|
1240
|
+
// Exclude package.propeties from ipk file
|
|
1241
|
+
self.excludeFiles = self.excludeFiles.concat([
|
|
1242
|
+
"package.properties"
|
|
1243
|
+
]);
|
|
1244
|
+
setImmediate(next, err);
|
|
1245
|
+
});
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
function outputPackage(destination, next) {
|
|
1249
|
+
log.info("package#outputPackage()");
|
|
1250
|
+
|
|
1251
|
+
// Check that the directories exist
|
|
1252
|
+
if (fs.existsSync(destination)) {
|
|
1253
|
+
const stats = fs.statSync(destination);
|
|
1254
|
+
if (!stats.isDirectory()) {
|
|
1255
|
+
return next(errHndl.getErrMsg("NOT_DIRTYPE_PATH", destination));
|
|
1256
|
+
}
|
|
1257
|
+
} else {
|
|
1258
|
+
log.verbose("setOutputDir()", "creating directory '" + destination + "' ...");
|
|
1259
|
+
mkdirp.sync(destination);
|
|
1260
|
+
}
|
|
1261
|
+
destination = fs.realpathSync(destination);
|
|
1262
|
+
|
|
1263
|
+
if (this.rom) {
|
|
1264
|
+
copySrcToDst.call(this, path.join(this.tempDir, 'data'), destination, next);
|
|
1265
|
+
} else {
|
|
1266
|
+
const tempDir = this.tempDir,
|
|
1267
|
+
tempCtrlDir = path.join(tempDir, 'ctrl'),
|
|
1268
|
+
ctrlTgzFile = path.join(tempDir, 'control.tar.gz'),
|
|
1269
|
+
tempDataDir = path.join(tempDir, 'data'),
|
|
1270
|
+
dataTgzFile = path.join(tempDir, 'data.tar.gz');
|
|
1271
|
+
|
|
1272
|
+
async.series([
|
|
1273
|
+
decidePkgName.bind(this, this.pkgId, this.pkgVersion),
|
|
1274
|
+
getFileSize.bind(this, tempDataDir),
|
|
1275
|
+
makeTgz.bind(this, tempDataDir, dataTgzFile),
|
|
1276
|
+
createDir.bind(this, tempCtrlDir),
|
|
1277
|
+
createControlFile.bind(this, tempCtrlDir, false),
|
|
1278
|
+
createSign.bind(this, tempCtrlDir, dataTgzFile),
|
|
1279
|
+
makeTgz.bind(this, tempCtrlDir, ctrlTgzFile),
|
|
1280
|
+
createDebianBinary.bind(this, tempDir),
|
|
1281
|
+
setIpkFileName.bind(this),
|
|
1282
|
+
removeExistingIpk.bind(this, destination),
|
|
1283
|
+
makeIpk.bind(this, tempDir)
|
|
1284
|
+
], function(err) {
|
|
1285
|
+
if (err) {
|
|
1286
|
+
setImmediate(next, err);
|
|
1287
|
+
return;
|
|
1288
|
+
}
|
|
1289
|
+
setImmediate(next);
|
|
1290
|
+
});
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
function decidePkgName(pkgName, pkgVersion, next) {
|
|
1295
|
+
if (this.appCount !== 0) {
|
|
1296
|
+
this.pkg = {
|
|
1297
|
+
name: pkgName || this.appinfo.id,
|
|
1298
|
+
version: pkgVersion || this.appinfo.version
|
|
1299
|
+
};
|
|
1300
|
+
} else if (this.services.length > 0) {
|
|
1301
|
+
this.pkg = {
|
|
1302
|
+
name: pkgName || this.services[0].serviceInfo.id || this.services[0].serviceInfo.services[0].name,
|
|
1303
|
+
version: pkgVersion || "1.0.0"
|
|
1304
|
+
};
|
|
1305
|
+
} else {
|
|
1306
|
+
this.pkg = {
|
|
1307
|
+
name: pkgName || "unknown",
|
|
1308
|
+
version: pkgVersion || "1.0.0"
|
|
1309
|
+
};
|
|
1310
|
+
}
|
|
1311
|
+
setImmediate(next);
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
function getFileSize(srcDir, next) {
|
|
1315
|
+
const self = this;
|
|
1316
|
+
|
|
1317
|
+
async.waterfall([
|
|
1318
|
+
_readSizeRecursive.bind(this, srcDir)
|
|
1319
|
+
], function(err, size) {
|
|
1320
|
+
if (!err && size) {
|
|
1321
|
+
log.verbose("package#getFileSize()", "Installed-Size:", size);
|
|
1322
|
+
self.size = size;
|
|
1323
|
+
}
|
|
1324
|
+
setImmediate(next, err);
|
|
1325
|
+
});
|
|
1326
|
+
|
|
1327
|
+
function _readSizeRecursive(item, next) {
|
|
1328
|
+
fs.lstat(item, function(err, stats) {
|
|
1329
|
+
let total = stats.size;
|
|
1330
|
+
|
|
1331
|
+
if (!err && stats.isDirectory()) {
|
|
1332
|
+
fs.readdir(item, function(error, list) {
|
|
1333
|
+
if (error) {
|
|
1334
|
+
return next(error);
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
async.forEach(list, function(diritem, callback) {
|
|
1338
|
+
_readSizeRecursive(path.join(item, diritem), function(_err, size) {
|
|
1339
|
+
total += size;
|
|
1340
|
+
callback(_err);
|
|
1341
|
+
});
|
|
1342
|
+
}, function(e) {
|
|
1343
|
+
next(e, total);
|
|
1344
|
+
}
|
|
1345
|
+
);
|
|
1346
|
+
});
|
|
1347
|
+
} else {
|
|
1348
|
+
next(err, total);
|
|
1349
|
+
}
|
|
1350
|
+
});
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
function createDir(dstPath, next) {
|
|
1355
|
+
log.verbose("package#createDir()", "createDir:" + dstPath);
|
|
1356
|
+
mkdirp(dstPath, next);
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
function createControlFile(dstDir, encInfo, next) {
|
|
1360
|
+
const dstFilePath = path.join(dstDir, 'control');
|
|
1361
|
+
log.verbose("package#createControlFile()", "createControlFile:" + dstFilePath);
|
|
1362
|
+
|
|
1363
|
+
const lines = [
|
|
1364
|
+
"Package: " + this.pkg.name,
|
|
1365
|
+
"Version: " + this.pkg.version,
|
|
1366
|
+
"Section: misc",
|
|
1367
|
+
"Priority: optional",
|
|
1368
|
+
"Architecture: " + (this.architecture || "all"),
|
|
1369
|
+
"Installed-Size: " + (this.size || 1234), // TODO: TBC
|
|
1370
|
+
"Maintainer: N/A <nobody@example.com>", // TODO: TBC
|
|
1371
|
+
"Description: This is a webOS application.",
|
|
1372
|
+
"webOS-Package-Format-Version: 2", // TODO: TBC
|
|
1373
|
+
"webOS-Packager-Version: x.y.x" // TODO: TBC
|
|
1374
|
+
];
|
|
1375
|
+
|
|
1376
|
+
if (encInfo) {
|
|
1377
|
+
lines.push("Encrypt-Algorithm: AES-256-CBC");
|
|
1378
|
+
}
|
|
1379
|
+
lines.push(''); // for the trailing \n
|
|
1380
|
+
fs.writeFile(dstFilePath, lines.join("\n"), next);
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
function createSign(dstDir, dataTgzPath, next) {
|
|
1384
|
+
if ((!this.sign) || (!this.certificate)) {
|
|
1385
|
+
log.verbose("package#createSign()", "App signing is skipped");
|
|
1386
|
+
return setImmediate(next);
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
const sigFilePath = path.join(dstDir, 'data.tar.gz.sha256.txt'),
|
|
1390
|
+
keyPath = path.resolve(this.sign),
|
|
1391
|
+
crtPath = path.resolve(this.certificate);
|
|
1392
|
+
|
|
1393
|
+
log.verbose("package#createSign()", "dataTgzPath:" + dataTgzPath + ", sigfile:" + sigFilePath);
|
|
1394
|
+
log.verbose("package#createSign()", "keyPath:" + keyPath + ", crtPath:" + crtPath);
|
|
1395
|
+
|
|
1396
|
+
try {
|
|
1397
|
+
// Create certificate to tmp/ctrl directory
|
|
1398
|
+
shelljs.cp('-f', crtPath, dstDir);
|
|
1399
|
+
|
|
1400
|
+
// Create signature and write data.tar.gz.sha256.txt
|
|
1401
|
+
const privateKey = fs.readFileSync(keyPath, 'utf-8'),
|
|
1402
|
+
dataFile = fs.readFileSync(dataTgzPath), // data.tar.gz
|
|
1403
|
+
signer = crypto.createSign('sha256');
|
|
1404
|
+
|
|
1405
|
+
signer.update(dataFile);
|
|
1406
|
+
signer.end();
|
|
1407
|
+
|
|
1408
|
+
const signature = signer.sign(privateKey),
|
|
1409
|
+
buff = Buffer.from(signature),
|
|
1410
|
+
base64data = buff.toString('base64');
|
|
1411
|
+
|
|
1412
|
+
fs.writeFile(sigFilePath, base64data, next);
|
|
1413
|
+
} catch (err) {
|
|
1414
|
+
setImmediate(next, err);
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
function createDebianBinary(dstDir, next) {
|
|
1419
|
+
const dstFilePath = path.join(dstDir, "debian-binary");
|
|
1420
|
+
log.verbose("package#createDebianBinary()", dstFilePath);
|
|
1421
|
+
fs.writeFile(dstFilePath, "2.0\n", next);
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
function makeTgz(srcDir, dstDir, next) {
|
|
1425
|
+
log.verbose("package#makeTgz()", "makeTgz " + dstDir + " from " + srcDir);
|
|
1426
|
+
const pkgServiceNames = this.pkgServiceNames;
|
|
1427
|
+
// @see https://github.com/isaacs/node-tar/issues/7
|
|
1428
|
+
// it is a workaround for packaged ipk on windows can set +x into directory
|
|
1429
|
+
const fixupDirs = function(entry) {
|
|
1430
|
+
// Make sure readable directories have execute permission
|
|
1431
|
+
if (entry.props.type === "Directory") {
|
|
1432
|
+
let maskingBits = 201; // 0311
|
|
1433
|
+
// special case for service directory should have writable permission.
|
|
1434
|
+
if (pkgServiceNames.indexOf(entry.props.basename) !== -1) {
|
|
1435
|
+
maskingBits = 219; // 0333
|
|
1436
|
+
}
|
|
1437
|
+
entry.props.mode |= (entry.props.mode >>> 2) & maskingBits;
|
|
1438
|
+
} else if (entry.props.type === "File") {
|
|
1439
|
+
// Add other user's readable permission to all files
|
|
1440
|
+
entry.props.mode |= 4; // 04
|
|
1441
|
+
}
|
|
1442
|
+
return true;
|
|
1443
|
+
};
|
|
1444
|
+
|
|
1445
|
+
// TODO: when this PR (https://github.com/npm/node-tar/pull/73) is merged, need to update node-tar
|
|
1446
|
+
fstream
|
|
1447
|
+
.Reader({path: srcDir, type: 'Directory', filter: fixupDirs })
|
|
1448
|
+
.pipe(tarFilterPack({ noProprietary: true, fromBase: true, permission: this.packageProperties }))
|
|
1449
|
+
// .pipe(tarFilterPack({ noProprietary: true, pathFilter: filter, permission: this.packageProperties }))
|
|
1450
|
+
.pipe(zlib.createGzip())
|
|
1451
|
+
.pipe(fs.createWriteStream(dstDir))
|
|
1452
|
+
.on("close", next)
|
|
1453
|
+
.on('error', next);
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
function setIpkFileName(next) {
|
|
1457
|
+
let filename = this.pkg.name;
|
|
1458
|
+
if (this.pkg.version) {
|
|
1459
|
+
// This is asked to replace 'x86' from 'i586' as a file suffix (From NDK)
|
|
1460
|
+
const archSuffix = ('i586' === this.architecture) ? 'x86' : (this.architecture || 'all');
|
|
1461
|
+
filename = filename.concat("_" + this.pkg.version + "_" + archSuffix + ".ipk");
|
|
1462
|
+
} else {
|
|
1463
|
+
filename = filename.concat(".ipk");
|
|
1464
|
+
}
|
|
1465
|
+
this.ipkFileName = filename;
|
|
1466
|
+
setImmediate(next);
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
function removeExistingIpk(destination, next) {
|
|
1470
|
+
if (this.appCount === 0) {
|
|
1471
|
+
return setImmediate(next);
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
const filename = path.join(destination, this.ipkFileName);
|
|
1475
|
+
fs.exists(filename, function(exists) {
|
|
1476
|
+
if (exists) {
|
|
1477
|
+
fs.unlink(filename, next);
|
|
1478
|
+
} else {
|
|
1479
|
+
setImmediate(next); // Nothing to do
|
|
1480
|
+
}
|
|
1481
|
+
});
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
function padSpace(input, length) {
|
|
1485
|
+
// max field length in ar is 16
|
|
1486
|
+
const ret = String(input + ' ');
|
|
1487
|
+
return ret.slice(0, length);
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
function arFileHeader(name, size) {
|
|
1491
|
+
const epoch = Math.floor(Date.now() / 1000) ;
|
|
1492
|
+
return padSpace(name, 16) +
|
|
1493
|
+
padSpace(epoch, 12) +
|
|
1494
|
+
"0 " + // UID, 6 bytes
|
|
1495
|
+
"0 " + // GID, 6 bytes
|
|
1496
|
+
"100644 " + // file mode, 8 bytes
|
|
1497
|
+
padSpace(size, 10) +
|
|
1498
|
+
"\x60\x0A"; // don't ask
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
function makeIpk(srcDir, next) {
|
|
1502
|
+
this.IpkDir = srcDir;
|
|
1503
|
+
this.ipk = path.join(srcDir, this.ipkFileName);
|
|
1504
|
+
log.info("package#makeIpk()", "makeIpk in dir " + this.IpkDir + " file " + this.ipkFileName);
|
|
1505
|
+
|
|
1506
|
+
if (this.nativecmd) { // TODO: TBR
|
|
1507
|
+
shelljs.cd(this.IpkDir);
|
|
1508
|
+
shelljs.exec("ar -q " + this.ipk + " debian-binary control.tar.gz data.tar.gz", {silent: this.silent});
|
|
1509
|
+
|
|
1510
|
+
setImmediate(next);
|
|
1511
|
+
return;
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
// global header, see http://en.wikipedia.org/wiki/Ar_%28Unix%29
|
|
1515
|
+
const header = "!<arch>\n",
|
|
1516
|
+
debBinary = arFileHeader("debian-binary",4) + "2.0\n",
|
|
1517
|
+
that = this,
|
|
1518
|
+
arStream = CombinedStream.create(),
|
|
1519
|
+
pkgFiles = [ 'control.tar.gz', 'data.tar.gz' ],
|
|
1520
|
+
ipkStream = fstream.Writer(this.ipk);
|
|
1521
|
+
|
|
1522
|
+
arStream.append(header + debBinary);
|
|
1523
|
+
pkgFiles.forEach(function(f) {
|
|
1524
|
+
const fpath = path.join(that.IpkDir, f),
|
|
1525
|
+
s = fstream.Reader({ path: fpath, type: 'File'}),
|
|
1526
|
+
stat = fs.statSync(fpath); // TODO: move to asynchronous processing
|
|
1527
|
+
|
|
1528
|
+
arStream.append(arFileHeader(f, stat.size));
|
|
1529
|
+
arStream.append(s);
|
|
1530
|
+
if ((stat.size % 2) !== 0) {
|
|
1531
|
+
log.verbose("package#makeIpk()", 'adding a filler for file ' + f);
|
|
1532
|
+
arStream.append('\n');
|
|
1533
|
+
}
|
|
1534
|
+
}, this);
|
|
1535
|
+
|
|
1536
|
+
arStream.pipe(ipkStream);
|
|
1537
|
+
ipkStream.on('close', function() {
|
|
1538
|
+
setImmediate(next);
|
|
1539
|
+
});
|
|
1540
|
+
ipkStream.on('error', next);
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
function cleanupTmpDir(middleCb, next) {
|
|
1544
|
+
if (this.noclean) {
|
|
1545
|
+
middleCb("Skipping removal of " + this.tempDir);
|
|
1546
|
+
setImmediate(next);
|
|
1547
|
+
} else {
|
|
1548
|
+
rimraf(this.tempDir, function(err) {
|
|
1549
|
+
log.verbose("package#cleanupTmpDir()", "removed " + this.tempDir);
|
|
1550
|
+
setImmediate(next, err);
|
|
1551
|
+
}.bind(this));
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
function checkDirectory(options, directory, callback) {
|
|
1556
|
+
log.verbose("package#checkDirectory()", directory);
|
|
1557
|
+
if (fs.existsSync(directory)) { // TODO: move to asynchronous processing
|
|
1558
|
+
const stat = fs.statSync(directory);
|
|
1559
|
+
if (!stat.isDirectory()) {
|
|
1560
|
+
callback(errHndl.getErrMsg("NOT_DIRTYPE_PATH", directory));
|
|
1561
|
+
return;
|
|
1562
|
+
}
|
|
1563
|
+
directory = fs.realpathSync(directory);
|
|
1564
|
+
} else {
|
|
1565
|
+
callback(errHndl.getErrMsg("NOT_EXIST_PATH", directory));
|
|
1566
|
+
return;
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
if (options.force) {
|
|
1570
|
+
return callback();
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
if (fs.existsSync(path.join(directory, "packageinfo.json"))) {
|
|
1574
|
+
return callback(errHndl.getErrMsg("NOT_PACKAGE_WITH_PKGDIR"));
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
if (fs.existsSync(path.join(directory, "appinfo.json"))) {// TODO: move to asynchronous processing
|
|
1578
|
+
this.appCount++;
|
|
1579
|
+
log.verbose("package#checkDirectory()", "FOUND appinfo.json, appCount " + this.appCount);
|
|
1580
|
+
|
|
1581
|
+
if (this.appCount > 1) {
|
|
1582
|
+
callback(errHndl.getErrMsg("OVER_APPCOUNT"));
|
|
1583
|
+
} else if (this.rscCount > 0) {
|
|
1584
|
+
callback(errHndl.getErrMsg("NOT_PACKAGE_WITH_RESOURCE"));
|
|
1585
|
+
} else {
|
|
1586
|
+
this.appDir = directory;
|
|
1587
|
+
this.originAppDir = directory;
|
|
1588
|
+
if (fs.existsSync(path.join(directory, "package.js"))) {
|
|
1589
|
+
this.pkgJSExist = true;
|
|
1590
|
+
}
|
|
1591
|
+
callback();
|
|
1592
|
+
}
|
|
1593
|
+
} else if (fs.existsSync(path.join(directory, "services.json"))) {
|
|
1594
|
+
if (this.rscCount > 0) {
|
|
1595
|
+
callback(errHndl.getErrMsg("NOT_PACKAGE_WITH_RESOURCE"));
|
|
1596
|
+
}
|
|
1597
|
+
this.svcDir = this.svcDir || [];
|
|
1598
|
+
this.svcDir = this.svcDir.concat(directory);
|
|
1599
|
+
callback();
|
|
1600
|
+
} else if (fs.existsSync(path.join(directory, "resourceinfo.json"))) {
|
|
1601
|
+
this.rscCount++;
|
|
1602
|
+
log.verbose("FOUND resourceinfo.json, rscCount " + this.rscCount);
|
|
1603
|
+
if (this.appCount > 0 || this.svcDir && this.svcDir.length > 0) {
|
|
1604
|
+
callback(errHndl.getErrMsg("NOT_PACKAGE_WITH_RESOURCE"));
|
|
1605
|
+
}
|
|
1606
|
+
this.resources = this.resources || [];
|
|
1607
|
+
const rsc = {};
|
|
1608
|
+
rsc.dir = directory;
|
|
1609
|
+
this.resources = this.resources.concat(rsc);
|
|
1610
|
+
callback();
|
|
1611
|
+
} else if (fs.existsSync(path.join(directory, "account-templates.json"))) {
|
|
1612
|
+
callback(errHndl.getErrMsg("NO_ACCOUNT"));
|
|
1613
|
+
} else {
|
|
1614
|
+
// find service directory recursively
|
|
1615
|
+
const foundSvcDirs = [];
|
|
1616
|
+
this.svcDir = this.svcDir || [];
|
|
1617
|
+
this.svcDir = this.svcDir.concat(directory);
|
|
1618
|
+
|
|
1619
|
+
findServiceDir.call(this, foundSvcDirs, function() {
|
|
1620
|
+
if (foundSvcDirs.length > 0) {
|
|
1621
|
+
if (this.rscCount > 0) {
|
|
1622
|
+
callback(errHndl.getErrMsg("NOT_PACKAGE_WITH_RESOURCE"));
|
|
1623
|
+
}
|
|
1624
|
+
callback();
|
|
1625
|
+
} else {
|
|
1626
|
+
callback(errHndl.getErrMsg("NO_METAFILE", "APP_DIR/SVC_DIR", directory));
|
|
1627
|
+
}
|
|
1628
|
+
});
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
// find service directories checking if directory has services.json file
|
|
1633
|
+
function findServiceDir(services, next) {
|
|
1634
|
+
const checkDirs = [].concat(this.svcDir || this.originAppDir || []),
|
|
1635
|
+
foundFilePath = [];
|
|
1636
|
+
if (checkDirs.length === 0) {
|
|
1637
|
+
return setImmediate(next);
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
async.forEach(checkDirs, function(checkDir, next) {
|
|
1641
|
+
walkFolder(checkDir, "services.json", foundFilePath, 3, function(err) {
|
|
1642
|
+
if (err) {
|
|
1643
|
+
return setImmediate(next, err);
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1646
|
+
foundFilePath.forEach(function(filePath) {
|
|
1647
|
+
const svc = new Service();
|
|
1648
|
+
svc.srcDir = path.dirname(filePath);
|
|
1649
|
+
svc.dirName = path.basename(svc.srcDir);
|
|
1650
|
+
services.push(svc);
|
|
1651
|
+
});
|
|
1652
|
+
foundFilePath.pop();
|
|
1653
|
+
setImmediate(next, err);
|
|
1654
|
+
});
|
|
1655
|
+
}, function(err) {
|
|
1656
|
+
setImmediate(next, err);
|
|
1657
|
+
});
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
function walkFolder(dirPath, findFileName, foundFilePath, depth, next) {
|
|
1661
|
+
if (depth <= 0) {
|
|
1662
|
+
return next();
|
|
1663
|
+
}
|
|
1664
|
+
async.waterfall([
|
|
1665
|
+
fs.readdir.bind(null, dirPath),
|
|
1666
|
+
function(fileNames, next) {
|
|
1667
|
+
async.forEach(fileNames, function(fileName, next) {
|
|
1668
|
+
const filePath = path.join(dirPath, fileName);
|
|
1669
|
+
async.waterfall([
|
|
1670
|
+
fs.lstat.bind(null, filePath),
|
|
1671
|
+
function(stat, next) {
|
|
1672
|
+
if (stat.isFile()) {
|
|
1673
|
+
if (fileName === findFileName) {
|
|
1674
|
+
foundFilePath.push(filePath);
|
|
1675
|
+
}
|
|
1676
|
+
next();
|
|
1677
|
+
} else if (stat.isDirectory()) {
|
|
1678
|
+
walkFolder(filePath, findFileName, foundFilePath, (depth-1), next);
|
|
1679
|
+
} else {
|
|
1680
|
+
next();
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
], next); // async.waterfall
|
|
1684
|
+
}, next); // async.forEach
|
|
1685
|
+
}
|
|
1686
|
+
], function(err) {
|
|
1687
|
+
next(err);
|
|
1688
|
+
}); // async.waterfall
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1691
|
+
// read services.json recursivly
|
|
1692
|
+
function loadServiceInfo(next) {
|
|
1693
|
+
for (const idx in this.services) {
|
|
1694
|
+
const filename = path.join(this.services[idx].srcDir, "services.json");
|
|
1695
|
+
try {
|
|
1696
|
+
const data = fs.readFileSync(filename),
|
|
1697
|
+
info = JSON.parse(data);
|
|
1698
|
+
if (!(Object.prototype.hasOwnProperty.call(info, 'id') &&
|
|
1699
|
+
Object.prototype.hasOwnProperty.call(info, 'services'))) {
|
|
1700
|
+
continue;
|
|
1701
|
+
}
|
|
1702
|
+
this.services[idx].serviceInfo = info;
|
|
1703
|
+
this.services[idx].valid = true;
|
|
1704
|
+
} catch (err) {
|
|
1705
|
+
return setImmediate(next, err);
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
log.info("package#loadServiceInfo()", "num of serviceInfo: " + this.services.length);
|
|
1709
|
+
setImmediate(next);
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
// check services.json recursivly
|
|
1713
|
+
function checkServiceInfo(next) {
|
|
1714
|
+
const pkgId = this.pkgId || this.appinfo.id,
|
|
1715
|
+
svcIds = [];
|
|
1716
|
+
let errFlag = false;
|
|
1717
|
+
|
|
1718
|
+
this.services.forEach(function(service) {
|
|
1719
|
+
if (service.valid === false) {
|
|
1720
|
+
return;
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
svcIds.push(getPkgServiceNames(service.serviceInfo)[0]);
|
|
1724
|
+
});
|
|
1725
|
+
|
|
1726
|
+
svcIds.forEach(function(svcId) {
|
|
1727
|
+
if (svcId.indexOf(pkgId + ".") !== 0) {
|
|
1728
|
+
errFlag = true;
|
|
1729
|
+
}
|
|
1730
|
+
});
|
|
1731
|
+
|
|
1732
|
+
if (errFlag) {
|
|
1733
|
+
return setImmediate(next, errHndl.getErrMsg("INVALID_SERVICEID", pkgId));
|
|
1734
|
+
}
|
|
1735
|
+
setImmediate(next);
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
// create dir with each service's name under (tmp) + data/usr/palm/services/
|
|
1739
|
+
function createServiceDir(next) {
|
|
1740
|
+
this.services.forEach(function(service) {
|
|
1741
|
+
if (service.valid === false) {
|
|
1742
|
+
return;
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
getPkgServiceNames(service.serviceInfo).forEach(function(serviceName) {
|
|
1746
|
+
const serviceDir = path.join(this.tempDir, "data/usr/palm/services", serviceName);
|
|
1747
|
+
service.dstDirs.push(serviceDir);
|
|
1748
|
+
try {
|
|
1749
|
+
log.info("package#createServiceDir()", "service dir:" + serviceDir);
|
|
1750
|
+
mkdirp.sync(serviceDir);
|
|
1751
|
+
} catch (err) {
|
|
1752
|
+
return setImmediate(next, err);
|
|
1753
|
+
}
|
|
1754
|
+
}.bind(this));
|
|
1755
|
+
}.bind(this));
|
|
1756
|
+
setImmediate(next);
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
// copy service files into each serviceInfos[x].id directory.
|
|
1760
|
+
function copyService(next) {
|
|
1761
|
+
log.info("package#copyService()");
|
|
1762
|
+
const self = this,
|
|
1763
|
+
validServices = this.services.filter(function(service) {
|
|
1764
|
+
return service.valid;
|
|
1765
|
+
});
|
|
1766
|
+
try {
|
|
1767
|
+
async.forEachSeries(validServices, function(service, next) {
|
|
1768
|
+
async.forEach(service.dstDirs, function(dstDir, next) {
|
|
1769
|
+
self.dataCopyCount++;
|
|
1770
|
+
self.minifyDone = !self.minify;
|
|
1771
|
+
copySrcToDst.call(self, service.srcDir, dstDir, next);
|
|
1772
|
+
}, next);
|
|
1773
|
+
}, next);
|
|
1774
|
+
} catch (err) {
|
|
1775
|
+
setImmediate(next, err);
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
// add service info into packageinfo.json.
|
|
1780
|
+
function addServiceInPkgInfo(next) {
|
|
1781
|
+
if (!this.rom) {
|
|
1782
|
+
const filename = path.join(this.packageDir, "packageinfo.json");
|
|
1783
|
+
let pkginfo, validServiceCount;
|
|
1784
|
+
try {
|
|
1785
|
+
const data = fs.readFileSync(filename);
|
|
1786
|
+
validServiceCount = 0;
|
|
1787
|
+
pkginfo = JSON.parse(data);
|
|
1788
|
+
log.silly("package#addServiceInPkgInfo()", "PACKAGEINFO:", pkginfo);
|
|
1789
|
+
} catch (err) {
|
|
1790
|
+
console.error(err);
|
|
1791
|
+
setImmediate(next, err);
|
|
1792
|
+
}
|
|
1793
|
+
this.services.filter(function(s) {
|
|
1794
|
+
return s.valid;
|
|
1795
|
+
}).forEach(function(service) {
|
|
1796
|
+
getPkgServiceNames(service.serviceInfo).forEach(function(serviceName) {
|
|
1797
|
+
this.pkgServiceNames.push(serviceName);
|
|
1798
|
+
validServiceCount++;
|
|
1799
|
+
}.bind(this));
|
|
1800
|
+
}.bind(this));
|
|
1801
|
+
|
|
1802
|
+
if (validServiceCount > 0) {
|
|
1803
|
+
pkginfo.services = this.pkgServiceNames;
|
|
1804
|
+
const data = JSON.stringify(pkginfo, null, 2) + "\n";
|
|
1805
|
+
log.silly("package#addServiceInPkgInfo()", "Modified package.json:" + data);
|
|
1806
|
+
fs.writeFile(path.join(this.packageDir, "packageinfo.json"), data, next);
|
|
1807
|
+
} else {
|
|
1808
|
+
setImmediate(next);
|
|
1809
|
+
}
|
|
1810
|
+
} else {
|
|
1811
|
+
setImmediate(next);
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
// remove service dir from tmp source dir before packaging
|
|
1816
|
+
function removeServiceFromAppDir(middleCb, next) {
|
|
1817
|
+
if (this.appCount === 0) {
|
|
1818
|
+
return setImmediate(next);
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1821
|
+
let checkDir = this.applicationDir,
|
|
1822
|
+
needRmCheckDir = false;
|
|
1823
|
+
const fileList = fs.readdirSync(checkDir);
|
|
1824
|
+
|
|
1825
|
+
function _checkDir(dir) {
|
|
1826
|
+
if (this.dirName === dir) {
|
|
1827
|
+
try {
|
|
1828
|
+
const rmDir = path.join(this.applicationDir, this.dirName);
|
|
1829
|
+
shelljs.rm('-rf', rmDir);
|
|
1830
|
+
} catch (err) {
|
|
1831
|
+
next(Error("ERROR" + err), null);
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
if (fileList.indexOf('services') !== -1) {
|
|
1837
|
+
checkDir = path.join(this.applicationDir, 'services');
|
|
1838
|
+
const stats = fs.statSync(checkDir);
|
|
1839
|
+
if (stats.isDirectory()) {
|
|
1840
|
+
needRmCheckDir = true;
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
if (needRmCheckDir === true) {
|
|
1844
|
+
try {
|
|
1845
|
+
shelljs.rm('-rf', checkDir);
|
|
1846
|
+
} catch (err) {
|
|
1847
|
+
middleCb("ERROR:" + err);
|
|
1848
|
+
}
|
|
1849
|
+
} else {
|
|
1850
|
+
for (const idx in this.services) {
|
|
1851
|
+
this.dirName = this.services[idx].dirName;
|
|
1852
|
+
fileList.forEach(_checkDir, this);
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
setImmediate(next);
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
function loadResourceInfo(next) {
|
|
1859
|
+
log.verbose("loadResourceInfo");
|
|
1860
|
+
if (this.rscCount === 0) {
|
|
1861
|
+
return setImmediate(next);
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
this.resources.forEach(function(resource) {
|
|
1865
|
+
const filePath = path.join(resource.dir, "resourceinfo.json");
|
|
1866
|
+
try {
|
|
1867
|
+
const data = fs.readFileSync(filePath),
|
|
1868
|
+
info = JSON.parse(data);
|
|
1869
|
+
resource.info = info;
|
|
1870
|
+
} catch (err) {
|
|
1871
|
+
return setImmediate(next, errHndl.getErrMsg("INVALID_JSON_FORMAT", "resourceinfo.json"));
|
|
1872
|
+
}
|
|
1873
|
+
});
|
|
1874
|
+
log.verbose("num of resourceInfo: " + this.resources.length);
|
|
1875
|
+
setImmediate(next);
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
function checkResourceInfo(next) {
|
|
1879
|
+
log.verbose("checkResourceInfo");
|
|
1880
|
+
const pkgId = this.pkgId;
|
|
1881
|
+
|
|
1882
|
+
this.resources.forEach(function(resource) {
|
|
1883
|
+
if (!resource.info.id) {
|
|
1884
|
+
return setImmediate(next, errHndl.getErrMsg("REQUIRED_FIELD", "id"));
|
|
1885
|
+
} else if (resource.info.id.indexOf(pkgId) !== 0) {
|
|
1886
|
+
return setImmediate(next, errHndl.getErrMsg("INVALID_RESOURCEID", pkgId));
|
|
1887
|
+
} else if (resource.info.id.length < 1 || !(/^[a-z0-9.+-]*$/.test(resource.info.id))) {
|
|
1888
|
+
log.error(errHndl.getErrMsg("INVALID_VALUE", "id", resource.info.id));
|
|
1889
|
+
return setImmediate(next, errHndl.getErrMsg("INVALID_ID_RULE"));
|
|
1890
|
+
}
|
|
1891
|
+
});
|
|
1892
|
+
setImmediate(next);
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1895
|
+
function createResourceDir(next) {
|
|
1896
|
+
log.verbose("createResourceDir");
|
|
1897
|
+
this.resources.forEach(function(resource) {
|
|
1898
|
+
try {
|
|
1899
|
+
const resourceDir = path.join(this.tempDir, "data/usr/palm/resources", resource.info.id);
|
|
1900
|
+
log.verbose("resource dir = " + resourceDir);
|
|
1901
|
+
resource.dstDir = resourceDir;
|
|
1902
|
+
mkdirp.sync(resourceDir);
|
|
1903
|
+
} catch (err) {
|
|
1904
|
+
return setImmediate(next, err);
|
|
1905
|
+
}
|
|
1906
|
+
}.bind(this));
|
|
1907
|
+
log.silly("this.resources:", this.resources);
|
|
1908
|
+
setImmediate(next);
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
function copyResource(next) {
|
|
1912
|
+
log.verbose("copyResource()");
|
|
1913
|
+
if (this.rscCount === 0) {
|
|
1914
|
+
return setImmediate(next);
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1917
|
+
const self = this;
|
|
1918
|
+
try {
|
|
1919
|
+
async.forEachSeries(this.resources, function(resource, next) {
|
|
1920
|
+
self.dataCopyCount++;
|
|
1921
|
+
log.verbose("copyResource(), copy " + resource.dir + " ==> " + resource.dstDir);
|
|
1922
|
+
copySrcToDst.call(self, resource.dir, resource.dstDir, next);
|
|
1923
|
+
}, next);
|
|
1924
|
+
} catch (err) {
|
|
1925
|
+
setImmediate(next, err);
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
function addResourceInPkgInfo(next) {
|
|
1930
|
+
log.verbose("addResourceInPkgInfo");
|
|
1931
|
+
if (!this.rom && this.rscCount > 0) {
|
|
1932
|
+
const filename = path.join(this.packageDir, "packageinfo.json");
|
|
1933
|
+
let pkginfo, data;
|
|
1934
|
+
try {
|
|
1935
|
+
data = fs.readFileSync(filename);
|
|
1936
|
+
log.verbose("PACKAGEINFO: " + data);
|
|
1937
|
+
pkginfo = JSON.parse(data);
|
|
1938
|
+
} catch (err) {
|
|
1939
|
+
console.error(err);
|
|
1940
|
+
setImmediate(next, err);
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1943
|
+
this.resources.forEach(function(resource) {
|
|
1944
|
+
this.pkgResourceNames.push(resource.info.id);
|
|
1945
|
+
}.bind(this));
|
|
1946
|
+
|
|
1947
|
+
pkginfo.resources = this.pkgResourceNames;
|
|
1948
|
+
data = JSON.stringify(pkginfo, null, 2) + "\n";
|
|
1949
|
+
log.verbose("Modified package.json: " + data);
|
|
1950
|
+
fs.writeFile(path.join(this.packageDir, "packageinfo.json"), data, next);
|
|
1951
|
+
} else {
|
|
1952
|
+
setImmediate(next);
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1956
|
+
function copyData(inDirs, forceCopy, next) {
|
|
1957
|
+
log.verbose("package#copyData()", "only run when force packaging");
|
|
1958
|
+
if (forceCopy && this.dataCopyCount === 0) {
|
|
1959
|
+
const dst = path.join(this.tempDir, "data");
|
|
1960
|
+
async.forEachSeries(inDirs, function(src, next) {
|
|
1961
|
+
copySrcToDst.call(this, src, dst, next);
|
|
1962
|
+
}, function(err) {
|
|
1963
|
+
setImmediate(next, err);
|
|
1964
|
+
});
|
|
1965
|
+
} else {
|
|
1966
|
+
return setImmediate(next);
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
|
|
1970
|
+
function getPkgServiceNames(serviceInfo) {
|
|
1971
|
+
let serviceNames = [];
|
|
1972
|
+
if (servicePkgMethod === "id") {
|
|
1973
|
+
serviceNames = [serviceInfo.id];
|
|
1974
|
+
} else if (serviceInfo.services) {
|
|
1975
|
+
const serviceProps = (serviceInfo.services instanceof Array)
|
|
1976
|
+
? serviceInfo.services : [serviceInfo.services];
|
|
1977
|
+
serviceNames = serviceProps.map(function(serviceProp) {
|
|
1978
|
+
return serviceProp.name;
|
|
1979
|
+
});
|
|
1980
|
+
}
|
|
1981
|
+
return serviceNames;
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1984
|
+
function setUmask(mask, next) {
|
|
1985
|
+
this.oldmask = process.umask(mask);
|
|
1986
|
+
setImmediate(next);
|
|
1987
|
+
}
|
|
1988
|
+
|
|
1989
|
+
function recoverUmask(next) {
|
|
1990
|
+
if (this.oldmask) {
|
|
1991
|
+
process.umask(this.oldmask);
|
|
1992
|
+
}
|
|
1993
|
+
setImmediate(next);
|
|
1994
|
+
}
|
|
1995
|
+
|
|
1996
|
+
function rewriteFileWoBOMAsUtf8(filePath, rewriteFlag, next) {
|
|
1997
|
+
let data = fs.readFileSync(filePath);
|
|
1998
|
+
const encodingFormat = chardet.detect(Buffer.from(data));
|
|
1999
|
+
|
|
2000
|
+
if (['UTF-8', 'ISO-8895-1'].indexOf(encodingFormat) === -1) {
|
|
2001
|
+
log.silly("package#rewriteFileWoBOMAsUtf8()", "current encoding type:" + encodingFormat);
|
|
2002
|
+
data = encoding.convert(data, "UTF-8", encodingFormat);
|
|
2003
|
+
}
|
|
2004
|
+
data = stripbom(data);
|
|
2005
|
+
|
|
2006
|
+
if (rewriteFlag) {
|
|
2007
|
+
fs.writeFileSync(filePath,
|
|
2008
|
+
data, { encoding: "utf8" }
|
|
2009
|
+
);
|
|
2010
|
+
}
|
|
2011
|
+
|
|
2012
|
+
if (next !== 'undefined' && typeof next === 'function') {
|
|
2013
|
+
setImmediate(next, null, data);
|
|
2014
|
+
}
|
|
2015
|
+
return data;
|
|
2016
|
+
}
|
|
2017
|
+
|
|
2018
|
+
function encryptPackage(next) {
|
|
2019
|
+
if (this.rom || !this.encrypt) { // Do not encrypt when -rom option is given
|
|
2020
|
+
setImmediate(next);
|
|
2021
|
+
return;
|
|
2022
|
+
} else {
|
|
2023
|
+
this.encryptDir = path.join(this.tempDir, "encrypt");
|
|
2024
|
+
const encryptDir = this.encryptDir,
|
|
2025
|
+
encryptCtrlDir = path.join(encryptDir, 'ctrl'),
|
|
2026
|
+
ctrlTgzFile = path.join(encryptDir, 'control.tar.gz'),
|
|
2027
|
+
encryptDataDir = path.join(encryptDir, 'data'),
|
|
2028
|
+
dataTgzFile = path.join(encryptDir, 'data.tar.gz');
|
|
2029
|
+
|
|
2030
|
+
async.series([
|
|
2031
|
+
createDir.bind(this, encryptDir),
|
|
2032
|
+
createDir.bind(this, encryptCtrlDir),
|
|
2033
|
+
createDir.bind(this, encryptDataDir),
|
|
2034
|
+
createkeyIVfile.bind(this, encryptCtrlDir),
|
|
2035
|
+
encryptIpk.bind(this, encryptDataDir),
|
|
2036
|
+
makeTgz.bind(this, encryptDataDir, dataTgzFile),
|
|
2037
|
+
createControlFile.bind(this, encryptCtrlDir, true),
|
|
2038
|
+
makeTgz.bind(this, encryptCtrlDir, ctrlTgzFile),
|
|
2039
|
+
createDebianBinary.bind(this, encryptDir),
|
|
2040
|
+
makeIpk.bind(this, encryptDir)
|
|
2041
|
+
], function(err) {
|
|
2042
|
+
if (err) {
|
|
2043
|
+
setImmediate(next, err);
|
|
2044
|
+
return;
|
|
2045
|
+
}
|
|
2046
|
+
log.verbose("package#encryptPackage()", "success to encrypt pacakge");
|
|
2047
|
+
setImmediate(next);
|
|
2048
|
+
});
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2051
|
+
|
|
2052
|
+
function createkeyIVfile(dstPath, next) {
|
|
2053
|
+
// generate random key& IV
|
|
2054
|
+
this.key = Buffer.from(crypto.randomBytes(32), 'base64');
|
|
2055
|
+
this.iv = Buffer.from(crypto.randomBytes(16), 'base64');
|
|
2056
|
+
|
|
2057
|
+
try {
|
|
2058
|
+
// Read public key
|
|
2059
|
+
const publickeyPath = path.join(__dirname, '../', 'files', 'conf', 'pubkey.pem'),
|
|
2060
|
+
publickey = fs.readFileSync(publickeyPath, 'utf8');
|
|
2061
|
+
|
|
2062
|
+
// Encrypt key, iv by publickey
|
|
2063
|
+
const encryptedKey= crypto.publicEncrypt({
|
|
2064
|
+
key: publickey,
|
|
2065
|
+
padding: 4 /* crypto.constants.RSA_PKCS1_OAEP_PADDING */
|
|
2066
|
+
}, Buffer.from(this.key.toString('base64')));
|
|
2067
|
+
|
|
2068
|
+
const encryptedIV= crypto.publicEncrypt({
|
|
2069
|
+
key: publickey,
|
|
2070
|
+
padding: 4 /* crypto.constants.RSA_PKCS1_OAEP_PADDING */
|
|
2071
|
+
}, Buffer.from(this.iv.toString('base64')));
|
|
2072
|
+
|
|
2073
|
+
const keyFilePath = path.join(dstPath, "key");
|
|
2074
|
+
fs.writeFileSync(keyFilePath, encryptedKey, 'binary');
|
|
2075
|
+
|
|
2076
|
+
// write iv file on encrypt/control
|
|
2077
|
+
const ivFilePath = path.join(dstPath , "iv");
|
|
2078
|
+
fs.writeFileSync(ivFilePath, encryptedIV, 'binary');
|
|
2079
|
+
|
|
2080
|
+
} catch (err) {
|
|
2081
|
+
setImmediate(next, errHndl.getErrMsg(err));
|
|
2082
|
+
}
|
|
2083
|
+
setImmediate(next);
|
|
2084
|
+
}
|
|
2085
|
+
|
|
2086
|
+
function copyOutputToDst(destination, middleCb, next) {
|
|
2087
|
+
// copy data directory to destination
|
|
2088
|
+
if (this.rom) {
|
|
2089
|
+
middleCb("Create output directory to " + destination);
|
|
2090
|
+
copySrcToDst.call(this, path.join(this.tempDir, 'data'), destination, next);
|
|
2091
|
+
} else if (this.encrypt) {
|
|
2092
|
+
// copy encrypted ipk to destination
|
|
2093
|
+
middleCb("Create encrypted " + this.ipkFileName + " to " + destination);
|
|
2094
|
+
copySrcToDst.call(this, path.join(this.encryptDir, this.ipkFileName), destination, next);
|
|
2095
|
+
} else {
|
|
2096
|
+
// copy plain ipk to destination
|
|
2097
|
+
let outmsg = "Create ";
|
|
2098
|
+
if (this.sign && this.certificate) {
|
|
2099
|
+
outmsg = outmsg.concat("signed ");
|
|
2100
|
+
}
|
|
2101
|
+
middleCb(outmsg + this.ipkFileName + " to " + destination);
|
|
2102
|
+
copySrcToDst.call(this, path.join(this.tempDir, this.ipkFileName), destination, next);
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
|
|
2106
|
+
function encryptIpk(dstPath, next) {
|
|
2107
|
+
log.verbose("package#encryptPackage()#encrypIpk()", "encrypt plain ipk to /encrypt/data");
|
|
2108
|
+
const plainIpkPath = path.join(this.tempDir, this.ipkFileName),
|
|
2109
|
+
encrypedIpkPath = path.join(dstPath, this.ipkFileName);
|
|
2110
|
+
|
|
2111
|
+
try {
|
|
2112
|
+
const input = fs.createReadStream(plainIpkPath),
|
|
2113
|
+
output = fs.createWriteStream(encrypedIpkPath),
|
|
2114
|
+
cipher = crypto.createCipheriv('aes-256-cbc', this.key, this.iv);
|
|
2115
|
+
|
|
2116
|
+
output.on('close', function() {
|
|
2117
|
+
log.verbose("package#encryptPackage()#encrypIpk()", "encrypted Ipk to " + encrypedIpkPath);
|
|
2118
|
+
setImmediate(next);
|
|
2119
|
+
}).
|
|
2120
|
+
on('error', function(err) {
|
|
2121
|
+
setImmediate(next, err);
|
|
2122
|
+
});
|
|
2123
|
+
|
|
2124
|
+
input.pipe(cipher).pipe(output);
|
|
2125
|
+
} catch (err) {
|
|
2126
|
+
setImmediate(next, err);
|
|
2127
|
+
}
|
|
2128
|
+
}
|
|
2129
|
+
}());
|